feat(authn http api): add test case and improve http api spec
This commit is contained in:
parent
327ff8636f
commit
7febcb852a
|
@ -1,11 +1,11 @@
|
||||||
emqx_authn: {
|
emqx_authn: {
|
||||||
enable: false
|
enable: false
|
||||||
authenticators: [
|
authenticators: [
|
||||||
{
|
# {
|
||||||
name: "authenticator1"
|
# name: "authenticator1"
|
||||||
mechanism: password-based
|
# mechanism: password-based
|
||||||
server_type: built-in-database
|
# server_type: built-in-database
|
||||||
user_id_type: clientid
|
# user_id_type: clientid
|
||||||
}
|
# }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
, provider :: module()
|
, provider :: module()
|
||||||
, config :: map()
|
, config :: map()
|
||||||
, state :: map()
|
, state :: map()
|
||||||
, version :: binary()
|
|
||||||
}).
|
}).
|
||||||
|
|
||||||
-record(chain,
|
-record(chain,
|
||||||
|
|
|
@ -194,20 +194,25 @@ do_update_authenticator(ChainID, AuthenticatorID, #{name := NewName} = Config, C
|
||||||
false ->
|
false ->
|
||||||
case CreateWhenNotFound of
|
case CreateWhenNotFound of
|
||||||
true ->
|
true ->
|
||||||
case do_create_authenticator(ChainID, AuthenticatorID, Config) of
|
case lists:keymember(NewName, 2, Authenticators) of
|
||||||
{ok, Authenticator} ->
|
true ->
|
||||||
NAuthenticators = Authenticators ++ [{AuthenticatorID, NewName, Authenticator}],
|
{error, name_has_be_used};
|
||||||
ok = mnesia:write(?CHAIN_TAB, Chain#chain{authenticators = NAuthenticators}, write),
|
false ->
|
||||||
{ok, serialize_authenticator(Authenticator)};
|
case do_create_authenticator(ChainID, AuthenticatorID, Config) of
|
||||||
{error, Reason} ->
|
{ok, Authenticator} ->
|
||||||
{error, Reason}
|
NAuthenticators = Authenticators ++ [{AuthenticatorID, NewName, Authenticator}],
|
||||||
end;
|
ok = mnesia:write(?CHAIN_TAB, Chain#chain{authenticators = NAuthenticators}, write),
|
||||||
|
{ok, serialize_authenticator(Authenticator)};
|
||||||
|
{error, Reason} ->
|
||||||
|
{error, Reason}
|
||||||
|
end
|
||||||
|
end;
|
||||||
false ->
|
false ->
|
||||||
{error, {not_found, {authenticator, AuthenticatorID}}}
|
{error, {not_found, {authenticator, AuthenticatorID}}}
|
||||||
end;
|
end;
|
||||||
{value,
|
{value,
|
||||||
{_, _, #authenticator{provider = Provider,
|
{_, _, #authenticator{provider = Provider,
|
||||||
state = #{version := Version} = State}},
|
state = #{version := Version} = State} = Authenticator},
|
||||||
Others} ->
|
Others} ->
|
||||||
case lists:keymember(NewName, 2, Others) of
|
case lists:keymember(NewName, 2, Others) of
|
||||||
true ->
|
true ->
|
||||||
|
@ -215,12 +220,12 @@ do_update_authenticator(ChainID, AuthenticatorID, #{name := NewName} = Config, C
|
||||||
false ->
|
false ->
|
||||||
case (NewProvider = authenticator_provider(Config)) =:= Provider of
|
case (NewProvider = authenticator_provider(Config)) =:= Provider of
|
||||||
true ->
|
true ->
|
||||||
Unique = {ChainID, AuthenticatorID, Version},
|
Unique = <<ChainID/binary, "/", AuthenticatorID/binary, ":", Version/binary>>,
|
||||||
case Provider:update(Config#{'_unique' => Unique}, State) of
|
case Provider:update(Config#{'_unique' => Unique}, State) of
|
||||||
{ok, NewState} ->
|
{ok, NewState} ->
|
||||||
NewAuthenticator = #authenticator{name = NewName,
|
NewAuthenticator = Authenticator#authenticator{name = NewName,
|
||||||
config = Config,
|
config = Config,
|
||||||
state = switch_version(NewState)},
|
state = switch_version(NewState)},
|
||||||
NewAuthenticators = replace_authenticator(AuthenticatorID, NewAuthenticator, Authenticators),
|
NewAuthenticators = replace_authenticator(AuthenticatorID, NewAuthenticator, Authenticators),
|
||||||
ok = mnesia:write(?CHAIN_TAB, Chain#chain{authenticators = NewAuthenticators}, write),
|
ok = mnesia:write(?CHAIN_TAB, Chain#chain{authenticators = NewAuthenticators}, write),
|
||||||
{ok, serialize_authenticator(NewAuthenticator)};
|
{ok, serialize_authenticator(NewAuthenticator)};
|
||||||
|
@ -228,12 +233,13 @@ do_update_authenticator(ChainID, AuthenticatorID, #{name := NewName} = Config, C
|
||||||
{error, Reason}
|
{error, Reason}
|
||||||
end;
|
end;
|
||||||
false ->
|
false ->
|
||||||
case NewProvider:create(Config#{'_unique' => {ChainID, AuthenticatorID, Version}}) of
|
Unique = <<ChainID/binary, "/", AuthenticatorID/binary, ":", Version/binary>>,
|
||||||
|
case NewProvider:create(Config#{'_unique' => Unique}) of
|
||||||
{ok, NewState} ->
|
{ok, NewState} ->
|
||||||
NewAuthenticator = #authenticator{name = NewName,
|
NewAuthenticator = Authenticator#authenticator{name = NewName,
|
||||||
provider = NewProvider,
|
provider = NewProvider,
|
||||||
config = Config,
|
config = Config,
|
||||||
state = switch_version(NewState)},
|
state = switch_version(NewState)},
|
||||||
NewAuthenticators = replace_authenticator(AuthenticatorID, NewAuthenticator, Authenticators),
|
NewAuthenticators = replace_authenticator(AuthenticatorID, NewAuthenticator, Authenticators),
|
||||||
ok = mnesia:write(?CHAIN_TAB, Chain#chain{authenticators = NewAuthenticators}, write),
|
ok = mnesia:write(?CHAIN_TAB, Chain#chain{authenticators = NewAuthenticators}, write),
|
||||||
_ = Provider:destroy(State),
|
_ = Provider:destroy(State),
|
||||||
|
|
|
@ -29,6 +29,56 @@
|
||||||
, users2/2
|
, users2/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
-define(EXAMPLE_1, #{name => <<"example 1">>,
|
||||||
|
mechanism => <<"password-based">>,
|
||||||
|
server_type => <<"built-in-example">>,
|
||||||
|
user_id_type => <<"username">>,
|
||||||
|
password_hash_algorithm => #{
|
||||||
|
name => <<"sha256">>
|
||||||
|
}}).
|
||||||
|
|
||||||
|
-define(EXAMPLE_2, #{name => <<"example 2">>,
|
||||||
|
mechanism => <<"password-based">>,
|
||||||
|
server_type => <<"http-server">>,
|
||||||
|
method => <<"post">>,
|
||||||
|
url => <<"http://localhost:80/login">>,
|
||||||
|
headers => #{
|
||||||
|
<<"content-type">> => <<"application/json">>
|
||||||
|
},
|
||||||
|
form_data => #{
|
||||||
|
<<"username">> => <<"${mqtt-username}">>,
|
||||||
|
<<"password">> => <<"${mqtt-password}">>
|
||||||
|
}}).
|
||||||
|
|
||||||
|
-define(EXAMPLE_3, #{name => <<"example 3">>,
|
||||||
|
mechanism => <<"jwt">>,
|
||||||
|
use_jwks => false,
|
||||||
|
algorithm => <<"hmac-based">>,
|
||||||
|
secret => <<"mysecret">>,
|
||||||
|
secret_base64_encoded => false,
|
||||||
|
verify_claims => #{
|
||||||
|
<<"username">> => <<"${mqtt-username}">>
|
||||||
|
}}).
|
||||||
|
|
||||||
|
-define(ERR_RESPONSE(Desc), #{description => Desc,
|
||||||
|
content => #{
|
||||||
|
'application/json' => #{
|
||||||
|
schema => minirest:ref(<<"error">>),
|
||||||
|
examples => #{
|
||||||
|
example1 => #{
|
||||||
|
summary => <<"Not Found">>,
|
||||||
|
value => #{code => <<"NOT_FOUND">>, message => <<"Authenticator '67e4c9d3' does not exist">>}
|
||||||
|
},
|
||||||
|
example2 => #{
|
||||||
|
summary => <<"Conflict">>,
|
||||||
|
value => #{code => <<"ALREADY_EXISTS">>, message => <<"Name has be used">>}
|
||||||
|
},
|
||||||
|
example3 => #{
|
||||||
|
summary => <<"Bad Request 1">>,
|
||||||
|
value => #{code => <<"OUT_OF_RANGE">>, message => <<"Out of range">>}
|
||||||
|
}
|
||||||
|
}}}}).
|
||||||
|
|
||||||
api_spec() ->
|
api_spec() ->
|
||||||
{[ authenticators_api()
|
{[ authenticators_api()
|
||||||
, authenticators_api2()
|
, authenticators_api2()
|
||||||
|
@ -39,40 +89,6 @@ api_spec() ->
|
||||||
], definitions()}.
|
], definitions()}.
|
||||||
|
|
||||||
authenticators_api() ->
|
authenticators_api() ->
|
||||||
Example1 = #{name => <<"example">>,
|
|
||||||
mechanism => <<"password-based">>,
|
|
||||||
config => #{
|
|
||||||
server_type => <<"built-in-example">>,
|
|
||||||
user_id_type => <<"username">>,
|
|
||||||
password_hash_algorithm => #{
|
|
||||||
name => <<"sha256">>
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
Example2 = #{name => <<"example">>,
|
|
||||||
mechanism => <<"password-based">>,
|
|
||||||
config => #{
|
|
||||||
server_type => <<"http-server">>,
|
|
||||||
method => <<"post">>,
|
|
||||||
url => <<"http://localhost:80/login">>,
|
|
||||||
headers => #{
|
|
||||||
<<"content-type">> => <<"application/json">>
|
|
||||||
},
|
|
||||||
form_data => #{
|
|
||||||
<<"username">> => <<"${mqtt-username}">>,
|
|
||||||
<<"password">> => <<"${mqtt-password}">>
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
Example3 = #{name => <<"example">>,
|
|
||||||
mechanism => <<"jwt">>,
|
|
||||||
config => #{
|
|
||||||
use_jwks => false,
|
|
||||||
algorithm => <<"hmac-based">>,
|
|
||||||
secret => <<"mysecret">>,
|
|
||||||
secret_base64_encoded => false,
|
|
||||||
verify_claims => #{
|
|
||||||
<<"username">> => <<"${mqtt-username}">>
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
Metadata = #{
|
Metadata = #{
|
||||||
post => #{
|
post => #{
|
||||||
description => "Create authenticator",
|
description => "Create authenticator",
|
||||||
|
@ -83,15 +99,15 @@ authenticators_api() ->
|
||||||
examples => #{
|
examples => #{
|
||||||
default => #{
|
default => #{
|
||||||
summary => <<"Default">>,
|
summary => <<"Default">>,
|
||||||
value => emqx_json:encode(Example1)
|
value => emqx_json:encode(?EXAMPLE_1)
|
||||||
},
|
},
|
||||||
http => #{
|
http => #{
|
||||||
summary => <<"Authentication provided by HTTP Server">>,
|
summary => <<"Authentication provided by HTTP Server">>,
|
||||||
value => emqx_json:encode(Example2)
|
value => emqx_json:encode(?EXAMPLE_2)
|
||||||
},
|
},
|
||||||
jwt => #{
|
jwt => #{
|
||||||
summary => <<"JWT Authentication">>,
|
summary => <<"JWT Authentication">>,
|
||||||
value => emqx_json:encode(Example3)
|
value => emqx_json:encode(?EXAMPLE_3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,10 +118,26 @@ authenticators_api() ->
|
||||||
description => <<"Created">>,
|
description => <<"Created">>,
|
||||||
content => #{
|
content => #{
|
||||||
'application/json' => #{
|
'application/json' => #{
|
||||||
schema => minirest:ref(<<"returned_authenticator">>)
|
schema => minirest:ref(<<"returned_authenticator">>),
|
||||||
|
examples => #{
|
||||||
|
example1 => #{
|
||||||
|
summary => <<"Example 1">>,
|
||||||
|
value => emqx_json:encode(maps:put(id, <<"example 1">>, ?EXAMPLE_1))
|
||||||
|
},
|
||||||
|
example2 => #{
|
||||||
|
summary => <<"Example 2">>,
|
||||||
|
value => emqx_json:encode(maps:put(id, <<"example 2">>, ?EXAMPLE_2))
|
||||||
|
},
|
||||||
|
example3 => #{
|
||||||
|
summary => <<"Example 3">>,
|
||||||
|
value => emqx_json:encode(maps:put(id, <<"example 3">>, ?EXAMPLE_3))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
<<"400">> => ?ERR_RESPONSE(<<"Bad Request">>),
|
||||||
|
<<"409">> => ?ERR_RESPONSE(<<"Conflict">>)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
get => #{
|
get => #{
|
||||||
|
@ -118,6 +150,15 @@ authenticators_api() ->
|
||||||
schema => #{
|
schema => #{
|
||||||
type => array,
|
type => array,
|
||||||
items => minirest:ref(<<"returned_authenticator">>)
|
items => minirest:ref(<<"returned_authenticator">>)
|
||||||
|
},
|
||||||
|
examples => #{
|
||||||
|
example1 => #{
|
||||||
|
summary => <<"Example 1">>,
|
||||||
|
value => emqx_json:encode([ maps:put(id, <<"example 1">>, ?EXAMPLE_1)
|
||||||
|
, maps:put(id, <<"example 2">>, ?EXAMPLE_2)
|
||||||
|
, maps:put(id, <<"example 3">>, ?EXAMPLE_3)
|
||||||
|
])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,18 +187,25 @@ authenticators_api2() ->
|
||||||
description => <<"OK">>,
|
description => <<"OK">>,
|
||||||
content => #{
|
content => #{
|
||||||
'application/json' => #{
|
'application/json' => #{
|
||||||
schema => minirest:ref(<<"returned_authenticator">>)
|
schema => minirest:ref(<<"returned_authenticator">>),
|
||||||
|
examples => #{
|
||||||
|
example1 => #{
|
||||||
|
summary => <<"Example 1">>,
|
||||||
|
value => emqx_json:encode(maps:put(id, <<"example 1">>, ?EXAMPLE_1))
|
||||||
|
},
|
||||||
|
example2 => #{
|
||||||
|
summary => <<"Example 2">>,
|
||||||
|
value => emqx_json:encode(maps:put(id, <<"example 2">>, ?EXAMPLE_2))
|
||||||
|
},
|
||||||
|
example3 => #{
|
||||||
|
summary => <<"Example 3">>,
|
||||||
|
value => emqx_json:encode(maps:put(id, <<"example 3">>, ?EXAMPLE_3))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
<<"404">> => #{
|
<<"404">> => ?ERR_RESPONSE(<<"Not Found">>)
|
||||||
description => <<"Not Found">>,
|
|
||||||
content => #{
|
|
||||||
'application/json' => #{
|
|
||||||
schema => minirest:ref(<<"error">>)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
put => #{
|
put => #{
|
||||||
|
@ -180,6 +228,16 @@ authenticators_api2() ->
|
||||||
, minirest:ref(<<"jwt">>)
|
, minirest:ref(<<"jwt">>)
|
||||||
, minirest:ref(<<"scram">>)
|
, minirest:ref(<<"scram">>)
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
examples => #{
|
||||||
|
example1 => #{
|
||||||
|
summary => <<"Example 1">>,
|
||||||
|
value => emqx_json:encode(?EXAMPLE_1)
|
||||||
|
},
|
||||||
|
example2 => #{
|
||||||
|
summary => <<"Example 2">>,
|
||||||
|
value => emqx_json:encode(?EXAMPLE_2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,18 +247,27 @@ authenticators_api2() ->
|
||||||
description => <<"OK">>,
|
description => <<"OK">>,
|
||||||
content => #{
|
content => #{
|
||||||
'application/json' => #{
|
'application/json' => #{
|
||||||
schema => minirest:ref(<<"returned_authenticator">>)
|
schema => minirest:ref(<<"returned_authenticator">>),
|
||||||
|
examples => #{
|
||||||
|
example1 => #{
|
||||||
|
summary => <<"Example 1">>,
|
||||||
|
value => emqx_json:encode(maps:put(id, <<"example 1">>, ?EXAMPLE_1))
|
||||||
|
},
|
||||||
|
example2 => #{
|
||||||
|
summary => <<"Example 2">>,
|
||||||
|
value => emqx_json:encode(maps:put(id, <<"example 2">>, ?EXAMPLE_2))
|
||||||
|
},
|
||||||
|
example3 => #{
|
||||||
|
summary => <<"Example 3">>,
|
||||||
|
value => emqx_json:encode(maps:put(id, <<"example 3">>, ?EXAMPLE_3))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
<<"404">> => #{
|
<<"400">> => ?ERR_RESPONSE(<<"Bad Request">>),
|
||||||
description => <<"Not Found">>,
|
<<"404">> => ?ERR_RESPONSE(<<"Not Found">>),
|
||||||
content => #{
|
<<"409">> => ?ERR_RESPONSE(<<"Conflict">>)
|
||||||
'application/json' => #{
|
|
||||||
schema => minirest:ref(<<"error">>)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
delete => #{
|
delete => #{
|
||||||
|
@ -219,14 +286,7 @@ authenticators_api2() ->
|
||||||
<<"204">> => #{
|
<<"204">> => #{
|
||||||
description => <<"No Content">>
|
description => <<"No Content">>
|
||||||
},
|
},
|
||||||
<<"404">> => #{
|
<<"404">> => ?ERR_RESPONSE(<<"Not Found">>)
|
||||||
description => <<"Not Found">>,
|
|
||||||
content => #{
|
|
||||||
'application/json' => #{
|
|
||||||
schema => minirest:ref(<<"error">>)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -266,14 +326,8 @@ position_api() ->
|
||||||
<<"204">> => #{
|
<<"204">> => #{
|
||||||
description => <<"No Content">>
|
description => <<"No Content">>
|
||||||
},
|
},
|
||||||
<<"404">> => #{
|
<<"400">> => ?ERR_RESPONSE(<<"Bad Request">>),
|
||||||
description => <<"Not Found">>,
|
<<"404">> => ?ERR_RESPONSE(<<"Not Found">>)
|
||||||
content => #{
|
|
||||||
'application/json' => #{
|
|
||||||
schema => minirest:ref(<<"error">>)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -312,22 +366,8 @@ import_users_api() ->
|
||||||
<<"204">> => #{
|
<<"204">> => #{
|
||||||
description => <<"No Content">>
|
description => <<"No Content">>
|
||||||
},
|
},
|
||||||
<<"400">> => #{
|
<<"400">> => ?ERR_RESPONSE(<<"Bad Request">>),
|
||||||
description => <<"Bad Request">>,
|
<<"404">> => ?ERR_RESPONSE(<<"Not Found">>)
|
||||||
content => #{
|
|
||||||
'application/json' => #{
|
|
||||||
schema => minirest:ref(<<"error">>)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
<<"404">> => #{
|
|
||||||
description => <<"Not Found">>,
|
|
||||||
content => #{
|
|
||||||
'application/json' => #{
|
|
||||||
schema => minirest:ref(<<"error">>)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -382,14 +422,8 @@ users_api() ->
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
<<"400">> => #{
|
<<"400">> => ?ERR_RESPONSE(<<"Bad Request">>),
|
||||||
description => <<"Bad Request">>,
|
<<"404">> => ?ERR_RESPONSE(<<"Not Found">>)
|
||||||
content => #{
|
|
||||||
'application/json' => #{
|
|
||||||
schema => minirest:ref(<<"error">>)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
get => #{
|
get => #{
|
||||||
|
@ -423,7 +457,8 @@ users_api() ->
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
<<"404">> => ?ERR_RESPONSE(<<"Not Found">>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -486,14 +521,8 @@ users2_api() ->
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
<<"404">> => #{
|
<<"400">> => ?ERR_RESPONSE(<<"Bad Request">>),
|
||||||
description => <<"Not Found">>,
|
<<"404">> => ?ERR_RESPONSE(<<"Not Found">>)
|
||||||
content => #{
|
|
||||||
'application/json' => #{
|
|
||||||
schema => minirest:ref(<<"error">>)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
get => #{
|
get => #{
|
||||||
|
@ -536,14 +565,7 @@ users2_api() ->
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
<<"404">> => #{
|
<<"404">> => ?ERR_RESPONSE(<<"Not Found">>)
|
||||||
description => <<"Not Found">>,
|
|
||||||
content => #{
|
|
||||||
'application/json' => #{
|
|
||||||
schema => minirest:ref(<<"error">>)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
delete => #{
|
delete => #{
|
||||||
|
@ -570,14 +592,7 @@ users2_api() ->
|
||||||
<<"204">> => #{
|
<<"204">> => #{
|
||||||
description => <<"No Content">>
|
description => <<"No Content">>
|
||||||
},
|
},
|
||||||
<<"404">> => #{
|
<<"404">> => ?ERR_RESPONSE(<<"Not Found">>)
|
||||||
description => <<"Not Found">>,
|
|
||||||
content => #{
|
|
||||||
'application/json' => #{
|
|
||||||
schema => minirest:ref(<<"error">>)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1007,47 +1022,43 @@ authenticators2(delete, Request) ->
|
||||||
position(post, Request) ->
|
position(post, Request) ->
|
||||||
AuthenticatorID = cowboy_req:binding(id, Request),
|
AuthenticatorID = cowboy_req:binding(id, Request),
|
||||||
{ok, Body, _} = cowboy_req:read_body(Request),
|
{ok, Body, _} = cowboy_req:read_body(Request),
|
||||||
case emqx_json:decode(Body, [return_maps]) of
|
NBody = emqx_json:decode(Body, [return_maps]),
|
||||||
#{<<"position">> := Position} when is_integer(Position) ->
|
Config = hocon_schema:check_plain(emqx_authn_other_schema, #{<<"position">> => NBody},
|
||||||
case emqx_authn:move_authenticator_to_the_nth(?CHAIN, AuthenticatorID, Position) of
|
#{nullable => true}, ["position"]),
|
||||||
ok ->
|
#{position := #{position := Position}} = emqx_map_lib:unsafe_atom_key_map(Config),
|
||||||
{204};
|
case emqx_authn:move_authenticator_to_the_nth(?CHAIN, AuthenticatorID, Position) of
|
||||||
{error, Reason} ->
|
ok ->
|
||||||
serialize_error(Reason)
|
{204};
|
||||||
end;
|
{error, Reason} ->
|
||||||
_ ->
|
serialize_error(Reason)
|
||||||
serialize_error({missing_parameter, position})
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
import_users(post, Request) ->
|
import_users(post, Request) ->
|
||||||
AuthenticatorID = cowboy_req:binding(id, Request),
|
AuthenticatorID = cowboy_req:binding(id, Request),
|
||||||
{ok, Body, _} = cowboy_req:read_body(Request),
|
{ok, Body, _} = cowboy_req:read_body(Request),
|
||||||
case emqx_json:decode(Body, [return_maps]) of
|
NBody = emqx_json:decode(Body, [return_maps]),
|
||||||
#{<<"filename">> := Filename} when is_binary(Filename) ->
|
Config = hocon_schema:check_plain(emqx_authn_other_schema, #{<<"filename">> => NBody},
|
||||||
case emqx_authn:import_users(?CHAIN, AuthenticatorID, Filename) of
|
#{nullable => true}, ["filename"]),
|
||||||
ok ->
|
#{filename := #{filename := Filename}} = emqx_map_lib:unsafe_atom_key_map(Config),
|
||||||
{204};
|
case emqx_authn:import_users(?CHAIN, AuthenticatorID, Filename) of
|
||||||
{error, Reason} ->
|
ok ->
|
||||||
serialize_error(Reason)
|
{204};
|
||||||
end;
|
{error, Reason} ->
|
||||||
_ ->
|
serialize_error(Reason)
|
||||||
serialize_error({missing_parameter, filename})
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
users(post, Request) ->
|
users(post, Request) ->
|
||||||
AuthenticatorID = cowboy_req:binding(id, Request),
|
AuthenticatorID = cowboy_req:binding(id, Request),
|
||||||
{ok, Body, _} = cowboy_req:read_body(Request),
|
{ok, Body, _} = cowboy_req:read_body(Request),
|
||||||
case emqx_json:decode(Body, [return_maps]) of
|
NBody = emqx_json:decode(Body, [return_maps]),
|
||||||
#{<<"user_id">> := _,
|
Config = hocon_schema:check_plain(emqx_authn_other_schema, #{<<"user_info">> => NBody},
|
||||||
<<"password">> := _} = UserInfo ->
|
#{nullable => true}, ["user_info"]),
|
||||||
case emqx_authn:add_user(?CHAIN, AuthenticatorID, UserInfo) of
|
#{user_info := UserInfo} = emqx_map_lib:unsafe_atom_key_map(Config),
|
||||||
{ok, User} ->
|
case emqx_authn:add_user(?CHAIN, AuthenticatorID, UserInfo) of
|
||||||
{201, User};
|
{ok, User} ->
|
||||||
{error, Reason} ->
|
{201, User};
|
||||||
serialize_error(Reason)
|
{error, Reason} ->
|
||||||
end;
|
serialize_error(Reason)
|
||||||
_ ->
|
|
||||||
serialize_error({missing_parameter, user_id})
|
|
||||||
end;
|
end;
|
||||||
users(get, Request) ->
|
users(get, Request) ->
|
||||||
AuthenticatorID = cowboy_req:binding(id, Request),
|
AuthenticatorID = cowboy_req:binding(id, Request),
|
||||||
|
@ -1062,16 +1073,15 @@ users2(patch, Request) ->
|
||||||
AuthenticatorID = cowboy_req:binding(id, Request),
|
AuthenticatorID = cowboy_req:binding(id, Request),
|
||||||
UserID = cowboy_req:binding(user_id, Request),
|
UserID = cowboy_req:binding(user_id, Request),
|
||||||
{ok, Body, _} = cowboy_req:read_body(Request),
|
{ok, Body, _} = cowboy_req:read_body(Request),
|
||||||
case emqx_json:decode(Body, [return_maps]) of
|
NBody = emqx_json:decode(Body, [return_maps]),
|
||||||
#{<<"password">> := _} = UserInfo ->
|
Config = hocon_schema:check_plain(emqx_authn_other_schema, #{<<"new_user_info">> => NBody},
|
||||||
case emqx_authn:add_user(?CHAIN, AuthenticatorID, UserID, UserInfo) of
|
#{nullable => true}, ["new_user_info"]),
|
||||||
{ok, User} ->
|
#{new_user_info := NewUserInfo} = emqx_map_lib:unsafe_atom_key_map(Config),
|
||||||
{200, User};
|
case emqx_authn:update_user(?CHAIN, AuthenticatorID, UserID, NewUserInfo) of
|
||||||
{error, Reason} ->
|
{ok, User} ->
|
||||||
serialize_error(Reason)
|
{200, User};
|
||||||
end;
|
{error, Reason} ->
|
||||||
_ ->
|
serialize_error(Reason)
|
||||||
serialize_error({missing_parameter, password})
|
|
||||||
end;
|
end;
|
||||||
users2(get, Request) ->
|
users2(get, Request) ->
|
||||||
AuthenticatorID = cowboy_req:binding(id, Request),
|
AuthenticatorID = cowboy_req:binding(id, Request),
|
||||||
|
@ -1106,6 +1116,6 @@ serialize_error({missing_parameter, Name}) ->
|
||||||
message => list_to_binary(
|
message => list_to_binary(
|
||||||
io_lib:format("The input parameter '~p' that is mandatory for processing this request is not supplied", [Name])
|
io_lib:format("The input parameter '~p' that is mandatory for processing this request is not supplied", [Name])
|
||||||
)}};
|
)}};
|
||||||
serialize_error(_) ->
|
serialize_error(Reason) ->
|
||||||
{400, #{code => <<"BAD_REQUEST">>,
|
{400, #{code => <<"BAD_REQUEST">>,
|
||||||
message => <<"Todo">>}}.
|
message => list_to_binary(io_lib:format("Todo: ~p", [Reason]))}}.
|
|
@ -133,8 +133,8 @@ destroy(#{user_group := UserGroup}) ->
|
||||||
end).
|
end).
|
||||||
|
|
||||||
%% TODO: binary to atom
|
%% TODO: binary to atom
|
||||||
add_user(#{<<"user_id">> := UserID,
|
add_user(#{user_id := UserID,
|
||||||
<<"password">> := Password}, #{user_group := UserGroup} = State) ->
|
password := Password}, #{user_group := UserGroup} = State) ->
|
||||||
trans(
|
trans(
|
||||||
fun() ->
|
fun() ->
|
||||||
case mnesia:read(?TAB, {UserGroup, UserID}, write) of
|
case mnesia:read(?TAB, {UserGroup, UserID}, write) of
|
||||||
|
@ -157,7 +157,7 @@ delete_user(UserID, #{user_group := UserGroup}) ->
|
||||||
end
|
end
|
||||||
end).
|
end).
|
||||||
|
|
||||||
update_user(UserID, #{<<"password">> := Password},
|
update_user(UserID, #{password := Password},
|
||||||
#{user_group := UserGroup} = State) ->
|
#{user_group := UserGroup} = State) ->
|
||||||
trans(
|
trans(
|
||||||
fun() ->
|
fun() ->
|
||||||
|
|
|
@ -178,8 +178,8 @@ import_users(Filename0, State) ->
|
||||||
{error, {unsupported_file_format, Extension}}
|
{error, {unsupported_file_format, Extension}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
add_user(#{<<"user_id">> := UserID,
|
add_user(#{user_id := UserID,
|
||||||
<<"password">> := Password},
|
password := Password},
|
||||||
#{user_group := UserGroup} = State) ->
|
#{user_group := UserGroup} = State) ->
|
||||||
trans(
|
trans(
|
||||||
fun() ->
|
fun() ->
|
||||||
|
@ -203,7 +203,7 @@ delete_user(UserID, #{user_group := UserGroup}) ->
|
||||||
end
|
end
|
||||||
end).
|
end).
|
||||||
|
|
||||||
update_user(UserID, #{<<"password">> := Password},
|
update_user(UserID, #{password := Password},
|
||||||
#{user_group := UserGroup} = State) ->
|
#{user_group := UserGroup} = State) ->
|
||||||
trans(
|
trans(
|
||||||
fun() ->
|
fun() ->
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%
|
||||||
|
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
%% you may not use this file except in compliance with the License.
|
||||||
|
%% You may obtain a copy of the License at
|
||||||
|
%%
|
||||||
|
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
%%
|
||||||
|
%% Unless required by applicable law or agreed to in writing, software
|
||||||
|
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
%% See the License for the specific language governing permissions and
|
||||||
|
%% limitations under the License.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(emqx_authn_other_schema).
|
||||||
|
|
||||||
|
-include("emqx_authn.hrl").
|
||||||
|
-include_lib("typerefl/include/types.hrl").
|
||||||
|
|
||||||
|
-behaviour(hocon_schema).
|
||||||
|
|
||||||
|
-export([ structs/0
|
||||||
|
, fields/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
structs() -> [ "filename", "position", "user_info", "new_user_info"].
|
||||||
|
|
||||||
|
fields("filename") ->
|
||||||
|
[ {filename, fun filename/1} ];
|
||||||
|
fields("position") ->
|
||||||
|
[ {position, fun position/1} ];
|
||||||
|
fields("user_info") ->
|
||||||
|
[ {user_id, fun user_id/1}
|
||||||
|
, {password, fun password/1}
|
||||||
|
];
|
||||||
|
fields("new_user_info") ->
|
||||||
|
[ {password, fun password/1}
|
||||||
|
].
|
||||||
|
|
||||||
|
filename(type) -> string();
|
||||||
|
filename(nullable) -> false;
|
||||||
|
filename(_) -> undefined.
|
||||||
|
|
||||||
|
position(type) -> integer();
|
||||||
|
position(validate) -> [fun (Position) -> Position > 0 end];
|
||||||
|
position(nullable) -> false;
|
||||||
|
position(_) -> undefined.
|
||||||
|
|
||||||
|
user_id(type) -> binary();
|
||||||
|
user_id(nullable) -> false;
|
||||||
|
user_id(_) -> undefined.
|
||||||
|
|
||||||
|
password(type) -> binary();
|
||||||
|
password(nullable) -> false;
|
||||||
|
password(_) -> undefined.
|
||||||
|
|
|
@ -54,34 +54,44 @@ t_authenticator(_) ->
|
||||||
AuthenticatorName1 = <<"myauthenticator1">>,
|
AuthenticatorName1 = <<"myauthenticator1">>,
|
||||||
AuthenticatorConfig1 = #{name => AuthenticatorName1,
|
AuthenticatorConfig1 = #{name => AuthenticatorName1,
|
||||||
mechanism => 'password-based',
|
mechanism => 'password-based',
|
||||||
config => #{
|
server_type => 'built-in-database',
|
||||||
server_type => 'built-in-database',
|
user_id_type => username,
|
||||||
user_id_type => username,
|
password_hash_algorithm => #{
|
||||||
password_hash_algorithm => #{
|
name => sha256
|
||||||
name => sha256
|
}},
|
||||||
}}},
|
{ok, #{name := AuthenticatorName1, id := ID1}} = ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig1),
|
||||||
?assertEqual({ok, AuthenticatorConfig1}, ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig1)),
|
?assertMatch({ok, #{name := AuthenticatorName1}}, ?AUTH:lookup_authenticator(?CHAIN, ID1)),
|
||||||
?assertEqual({ok, AuthenticatorConfig1}, ?AUTH:lookup_authenticator(?CHAIN, AuthenticatorName1)),
|
?assertMatch({ok, [#{name := AuthenticatorName1}]}, ?AUTH:list_authenticators(?CHAIN)),
|
||||||
?assertEqual({ok, [AuthenticatorConfig1]}, ?AUTH:list_authenticators(?CHAIN)),
|
?assertEqual({error, name_has_be_used}, ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig1)),
|
||||||
?assertEqual({error, {already_exists, {authenticator, AuthenticatorName1}}}, ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig1)),
|
|
||||||
|
AuthenticatorConfig2 = #{name => AuthenticatorName1,
|
||||||
|
mechanism => jwt,
|
||||||
|
use_jwks => false,
|
||||||
|
algorithm => 'hmac-based',
|
||||||
|
secret => <<"abcdef">>,
|
||||||
|
secret_base64_encoded => false,
|
||||||
|
verify_claims => []},
|
||||||
|
{ok, #{name := AuthenticatorName1, id := ID1, mechanism := jwt}} = ?AUTH:update_authenticator(?CHAIN, ID1, AuthenticatorConfig2),
|
||||||
|
|
||||||
|
ID2 = <<"random">>,
|
||||||
|
?assertEqual({error, {not_found, {authenticator, ID2}}}, ?AUTH:update_authenticator(?CHAIN, ID2, AuthenticatorConfig2)),
|
||||||
|
?assertEqual({error, name_has_be_used}, ?AUTH:update_or_create_authenticator(?CHAIN, ID2, AuthenticatorConfig2)),
|
||||||
|
|
||||||
AuthenticatorName2 = <<"myauthenticator2">>,
|
AuthenticatorName2 = <<"myauthenticator2">>,
|
||||||
AuthenticatorConfig2 = AuthenticatorConfig1#{name => AuthenticatorName2},
|
AuthenticatorConfig3 = AuthenticatorConfig2#{name => AuthenticatorName2},
|
||||||
?assertEqual({ok, AuthenticatorConfig2}, ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig2)),
|
{ok, #{name := AuthenticatorName2, id := ID2, secret := <<"abcdef">>}} = ?AUTH:update_or_create_authenticator(?CHAIN, ID2, AuthenticatorConfig3),
|
||||||
?assertMatch({ok, #{id := ?CHAIN, authenticators := [AuthenticatorConfig1, AuthenticatorConfig2]}}, ?AUTH:lookup_chain(?CHAIN)),
|
?assertMatch({ok, #{name := AuthenticatorName2}}, ?AUTH:lookup_authenticator(?CHAIN, ID2)),
|
||||||
?assertEqual({ok, AuthenticatorConfig2}, ?AUTH:lookup_authenticator(?CHAIN, AuthenticatorName2)),
|
{ok, #{name := AuthenticatorName2, id := ID2, secret := <<"fedcba">>}} = ?AUTH:update_or_create_authenticator(?CHAIN, ID2, AuthenticatorConfig3#{secret := <<"fedcba">>}),
|
||||||
?assertEqual({ok, [AuthenticatorConfig1, AuthenticatorConfig2]}, ?AUTH:list_authenticators(?CHAIN)),
|
|
||||||
|
|
||||||
?assertEqual(ok, ?AUTH:move_authenticator_to_the_front(?CHAIN, AuthenticatorName2)),
|
?assertMatch({ok, #{id := ?CHAIN, authenticators := [#{name := AuthenticatorName1}, #{name := AuthenticatorName2}]}}, ?AUTH:lookup_chain(?CHAIN)),
|
||||||
?assertEqual({ok, [AuthenticatorConfig2, AuthenticatorConfig1]}, ?AUTH:list_authenticators(?CHAIN)),
|
?assertMatch({ok, [#{name := AuthenticatorName1}, #{name := AuthenticatorName2}]}, ?AUTH:list_authenticators(?CHAIN)),
|
||||||
?assertEqual(ok, ?AUTH:move_authenticator_to_the_end(?CHAIN, AuthenticatorName2)),
|
|
||||||
?assertEqual({ok, [AuthenticatorConfig1, AuthenticatorConfig2]}, ?AUTH:list_authenticators(?CHAIN)),
|
?assertEqual(ok, ?AUTH:move_authenticator_to_the_nth(?CHAIN, ID2, 1)),
|
||||||
?assertEqual(ok, ?AUTH:move_authenticator_to_the_nth(?CHAIN, AuthenticatorName2, 1)),
|
?assertMatch({ok, [#{name := AuthenticatorName2}, #{name := AuthenticatorName1}]}, ?AUTH:list_authenticators(?CHAIN)),
|
||||||
?assertEqual({ok, [AuthenticatorConfig2, AuthenticatorConfig1]}, ?AUTH:list_authenticators(?CHAIN)),
|
?assertEqual({error, out_of_range}, ?AUTH:move_authenticator_to_the_nth(?CHAIN, ID2, 3)),
|
||||||
?assertEqual({error, out_of_range}, ?AUTH:move_authenticator_to_the_nth(?CHAIN, AuthenticatorName2, 3)),
|
?assertEqual({error, out_of_range}, ?AUTH:move_authenticator_to_the_nth(?CHAIN, ID2, 0)),
|
||||||
?assertEqual({error, out_of_range}, ?AUTH:move_authenticator_to_the_nth(?CHAIN, AuthenticatorName2, 0)),
|
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID1)),
|
||||||
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, AuthenticatorName1)),
|
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID2)),
|
||||||
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, AuthenticatorName2)),
|
|
||||||
?assertEqual({ok, []}, ?AUTH:list_authenticators(?CHAIN)),
|
?assertEqual({ok, []}, ?AUTH:list_authenticators(?CHAIN)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
|
|
@ -39,15 +39,14 @@ end_per_suite(_) ->
|
||||||
|
|
||||||
t_jwt_authenticator(_) ->
|
t_jwt_authenticator(_) ->
|
||||||
AuthenticatorName = <<"myauthenticator">>,
|
AuthenticatorName = <<"myauthenticator">>,
|
||||||
Config = #{use_jwks => false,
|
Config = #{name => AuthenticatorName,
|
||||||
|
mechanism => jwt,
|
||||||
|
use_jwks => false,
|
||||||
algorithm => 'hmac-based',
|
algorithm => 'hmac-based',
|
||||||
secret => <<"abcdef">>,
|
secret => <<"abcdef">>,
|
||||||
secret_base64_encoded => false,
|
secret_base64_encoded => false,
|
||||||
verify_claims => []},
|
verify_claims => []},
|
||||||
AuthenticatorConfig = #{name => AuthenticatorName,
|
{ok, #{name := AuthenticatorName, id := ID}} = ?AUTH:create_authenticator(?CHAIN, Config),
|
||||||
mechanism => jwt,
|
|
||||||
config => Config},
|
|
||||||
?assertEqual({ok, AuthenticatorConfig}, ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig)),
|
|
||||||
|
|
||||||
Payload = #{<<"username">> => <<"myuser">>},
|
Payload = #{<<"username">> => <<"myuser">>},
|
||||||
JWS = generate_jws('hmac-based', Payload, <<"abcdef">>),
|
JWS = generate_jws('hmac-based', Payload, <<"abcdef">>),
|
||||||
|
@ -62,11 +61,11 @@ t_jwt_authenticator(_) ->
|
||||||
%% secret_base64_encoded
|
%% secret_base64_encoded
|
||||||
Config2 = Config#{secret => base64:encode(<<"abcdef">>),
|
Config2 = Config#{secret => base64:encode(<<"abcdef">>),
|
||||||
secret_base64_encoded => true},
|
secret_base64_encoded => true},
|
||||||
?assertMatch({ok, _}, ?AUTH:update_authenticator(?CHAIN, AuthenticatorName, Config2)),
|
?assertMatch({ok, _}, ?AUTH:update_authenticator(?CHAIN, ID, Config2)),
|
||||||
?assertEqual({stop, ok}, ?AUTH:authenticate(ClientInfo, ok)),
|
?assertEqual({stop, ok}, ?AUTH:authenticate(ClientInfo, ok)),
|
||||||
|
|
||||||
Config3 = Config#{verify_claims => [{<<"username">>, <<"${mqtt-username}">>}]},
|
Config3 = Config#{verify_claims => [{<<"username">>, <<"${mqtt-username}">>}]},
|
||||||
?assertMatch({ok, _}, ?AUTH:update_authenticator(?CHAIN, AuthenticatorName, Config3)),
|
?assertMatch({ok, _}, ?AUTH:update_authenticator(?CHAIN, ID, Config3)),
|
||||||
?assertEqual({stop, ok}, ?AUTH:authenticate(ClientInfo, ok)),
|
?assertEqual({stop, ok}, ?AUTH:authenticate(ClientInfo, ok)),
|
||||||
?assertEqual({stop, {error, bad_username_or_password}}, ?AUTH:authenticate(ClientInfo#{username => <<"otheruser">>}, ok)),
|
?assertEqual({stop, {error, bad_username_or_password}}, ?AUTH:authenticate(ClientInfo#{username => <<"otheruser">>}, ok)),
|
||||||
|
|
||||||
|
@ -109,7 +108,7 @@ t_jwt_authenticator(_) ->
|
||||||
ClientInfo8 = ClientInfo#{password => JWS8},
|
ClientInfo8 = ClientInfo#{password => JWS8},
|
||||||
?assertEqual({stop, {error, bad_username_or_password}}, ?AUTH:authenticate(ClientInfo8, ok)),
|
?assertEqual({stop, {error, bad_username_or_password}}, ?AUTH:authenticate(ClientInfo8, ok)),
|
||||||
|
|
||||||
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, AuthenticatorName)),
|
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
t_jwt_authenticator2(_) ->
|
t_jwt_authenticator2(_) ->
|
||||||
|
@ -117,14 +116,13 @@ t_jwt_authenticator2(_) ->
|
||||||
PublicKey = list_to_binary(filename:join([Dir, "data/public_key.pem"])),
|
PublicKey = list_to_binary(filename:join([Dir, "data/public_key.pem"])),
|
||||||
PrivateKey = list_to_binary(filename:join([Dir, "data/private_key.pem"])),
|
PrivateKey = list_to_binary(filename:join([Dir, "data/private_key.pem"])),
|
||||||
AuthenticatorName = <<"myauthenticator">>,
|
AuthenticatorName = <<"myauthenticator">>,
|
||||||
Config = #{use_jwks => false,
|
Config = #{name => AuthenticatorName,
|
||||||
|
mechanism => jwt,
|
||||||
|
use_jwks => false,
|
||||||
algorithm => 'public-key',
|
algorithm => 'public-key',
|
||||||
certificate => PublicKey,
|
certificate => PublicKey,
|
||||||
verify_claims => []},
|
verify_claims => []},
|
||||||
AuthenticatorConfig = #{name => AuthenticatorName,
|
{ok, #{name := AuthenticatorName, id := ID}} = ?AUTH:create_authenticator(?CHAIN, Config),
|
||||||
mechanism => jwt,
|
|
||||||
config => Config},
|
|
||||||
?assertEqual({ok, AuthenticatorConfig}, ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig)),
|
|
||||||
|
|
||||||
Payload = #{<<"username">> => <<"myuser">>},
|
Payload = #{<<"username">> => <<"myuser">>},
|
||||||
JWS = generate_jws('public-key', Payload, PrivateKey),
|
JWS = generate_jws('public-key', Payload, PrivateKey),
|
||||||
|
@ -133,7 +131,7 @@ t_jwt_authenticator2(_) ->
|
||||||
?assertEqual({stop, ok}, ?AUTH:authenticate(ClientInfo, ok)),
|
?assertEqual({stop, ok}, ?AUTH:authenticate(ClientInfo, ok)),
|
||||||
?assertEqual({stop, {error, not_authorized}}, ?AUTH:authenticate(ClientInfo#{password => <<"badpassword">>}, ok)),
|
?assertEqual({stop, {error, not_authorized}}, ?AUTH:authenticate(ClientInfo#{password => <<"badpassword">>}, ok)),
|
||||||
|
|
||||||
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, AuthenticatorName)),
|
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
generate_jws('hmac-based', Payload, Secret) ->
|
generate_jws('hmac-based', Payload, Secret) ->
|
||||||
|
|
|
@ -41,18 +41,17 @@ t_mnesia_authenticator(_) ->
|
||||||
AuthenticatorName = <<"myauthenticator">>,
|
AuthenticatorName = <<"myauthenticator">>,
|
||||||
AuthenticatorConfig = #{name => AuthenticatorName,
|
AuthenticatorConfig = #{name => AuthenticatorName,
|
||||||
mechanism => 'password-based',
|
mechanism => 'password-based',
|
||||||
config => #{
|
server_type => 'built-in-database',
|
||||||
server_type => 'built-in-database',
|
user_id_type => username,
|
||||||
user_id_type => username,
|
password_hash_algorithm => #{
|
||||||
password_hash_algorithm => #{
|
name => sha256
|
||||||
name => sha256
|
}},
|
||||||
}}},
|
{ok, #{name := AuthenticatorName, id := ID}} = ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig),
|
||||||
?assertEqual({ok, AuthenticatorConfig}, ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig)),
|
|
||||||
|
|
||||||
UserInfo = #{<<"user_id">> => <<"myuser">>,
|
UserInfo = #{<<"user_id">> => <<"myuser">>,
|
||||||
<<"password">> => <<"mypass">>},
|
<<"password">> => <<"mypass">>},
|
||||||
?assertEqual({ok, #{user_id => <<"myuser">>}}, ?AUTH:add_user(?CHAIN, AuthenticatorName, UserInfo)),
|
?assertEqual({ok, #{user_id => <<"myuser">>}}, ?AUTH:add_user(?CHAIN, ID, UserInfo)),
|
||||||
?assertEqual({ok, #{user_id => <<"myuser">>}}, ?AUTH:lookup_user(?CHAIN, AuthenticatorName, <<"myuser">>)),
|
?assertEqual({ok, #{user_id => <<"myuser">>}}, ?AUTH:lookup_user(?CHAIN, ID, <<"myuser">>)),
|
||||||
|
|
||||||
ClientInfo = #{zone => external,
|
ClientInfo = #{zone => external,
|
||||||
username => <<"myuser">>,
|
username => <<"myuser">>,
|
||||||
|
@ -70,39 +69,38 @@ t_mnesia_authenticator(_) ->
|
||||||
?assertEqual({error, bad_username_or_password}, emqx_access_control:authenticate(ClientInfo3)),
|
?assertEqual({error, bad_username_or_password}, emqx_access_control:authenticate(ClientInfo3)),
|
||||||
|
|
||||||
UserInfo2 = UserInfo#{<<"password">> => <<"mypass2">>},
|
UserInfo2 = UserInfo#{<<"password">> => <<"mypass2">>},
|
||||||
?assertEqual({ok, #{user_id => <<"myuser">>}}, ?AUTH:update_user(?CHAIN, AuthenticatorName, <<"myuser">>, UserInfo2)),
|
?assertEqual({ok, #{user_id => <<"myuser">>}}, ?AUTH:update_user(?CHAIN, ID, <<"myuser">>, UserInfo2)),
|
||||||
ClientInfo4 = ClientInfo#{password => <<"mypass2">>},
|
ClientInfo4 = ClientInfo#{password => <<"mypass2">>},
|
||||||
?assertEqual({stop, ok}, ?AUTH:authenticate(ClientInfo4, ok)),
|
?assertEqual({stop, ok}, ?AUTH:authenticate(ClientInfo4, ok)),
|
||||||
|
|
||||||
?assertEqual(ok, ?AUTH:delete_user(?CHAIN, AuthenticatorName, <<"myuser">>)),
|
?assertEqual(ok, ?AUTH:delete_user(?CHAIN, ID, <<"myuser">>)),
|
||||||
?assertEqual({error, not_found}, ?AUTH:lookup_user(?CHAIN, AuthenticatorName, <<"myuser">>)),
|
?assertEqual({error, not_found}, ?AUTH:lookup_user(?CHAIN, ID, <<"myuser">>)),
|
||||||
|
|
||||||
?assertEqual({ok, #{user_id => <<"myuser">>}}, ?AUTH:add_user(?CHAIN, AuthenticatorName, UserInfo)),
|
?assertEqual({ok, #{user_id => <<"myuser">>}}, ?AUTH:add_user(?CHAIN, ID, UserInfo)),
|
||||||
?assertMatch({ok, #{user_id := <<"myuser">>}}, ?AUTH:lookup_user(?CHAIN, AuthenticatorName, <<"myuser">>)),
|
?assertMatch({ok, #{user_id := <<"myuser">>}}, ?AUTH:lookup_user(?CHAIN, ID, <<"myuser">>)),
|
||||||
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, AuthenticatorName)),
|
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID)),
|
||||||
|
|
||||||
?assertEqual({ok, AuthenticatorConfig}, ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig)),
|
{ok, #{name := AuthenticatorName, id := ID1}} = ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig),
|
||||||
?assertMatch({error, not_found}, ?AUTH:lookup_user(?CHAIN, AuthenticatorName, <<"myuser">>)),
|
?assertMatch({error, not_found}, ?AUTH:lookup_user(?CHAIN, ID1, <<"myuser">>)),
|
||||||
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, AuthenticatorName)),
|
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID1)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
t_import(_) ->
|
t_import(_) ->
|
||||||
AuthenticatorName = <<"myauthenticator">>,
|
AuthenticatorName = <<"myauthenticator">>,
|
||||||
AuthenticatorConfig = #{name => AuthenticatorName,
|
AuthenticatorConfig = #{name => AuthenticatorName,
|
||||||
mechanism => 'password-based',
|
mechanism => 'password-based',
|
||||||
config => #{
|
server_type => 'built-in-database',
|
||||||
server_type => 'built-in-database',
|
user_id_type => username,
|
||||||
user_id_type => username,
|
password_hash_algorithm => #{
|
||||||
password_hash_algorithm => #{
|
name => sha256
|
||||||
name => sha256
|
}},
|
||||||
}}},
|
{ok, #{name := AuthenticatorName, id := ID}} = ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig),
|
||||||
?assertEqual({ok, AuthenticatorConfig}, ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig)),
|
|
||||||
|
|
||||||
Dir = code:lib_dir(emqx_authn, test),
|
Dir = code:lib_dir(emqx_authn, test),
|
||||||
?assertEqual(ok, ?AUTH:import_users(?CHAIN, AuthenticatorName, filename:join([Dir, "data/user-credentials.json"]))),
|
?assertEqual(ok, ?AUTH:import_users(?CHAIN, ID, filename:join([Dir, "data/user-credentials.json"]))),
|
||||||
?assertEqual(ok, ?AUTH:import_users(?CHAIN, AuthenticatorName, filename:join([Dir, "data/user-credentials.csv"]))),
|
?assertEqual(ok, ?AUTH:import_users(?CHAIN, ID, filename:join([Dir, "data/user-credentials.csv"]))),
|
||||||
?assertMatch({ok, #{user_id := <<"myuser1">>}}, ?AUTH:lookup_user(?CHAIN, AuthenticatorName, <<"myuser1">>)),
|
?assertMatch({ok, #{user_id := <<"myuser1">>}}, ?AUTH:lookup_user(?CHAIN, ID, <<"myuser1">>)),
|
||||||
?assertMatch({ok, #{user_id := <<"myuser3">>}}, ?AUTH:lookup_user(?CHAIN, AuthenticatorName, <<"myuser3">>)),
|
?assertMatch({ok, #{user_id := <<"myuser3">>}}, ?AUTH:lookup_user(?CHAIN, ID, <<"myuser3">>)),
|
||||||
|
|
||||||
ClientInfo1 = #{username => <<"myuser1">>,
|
ClientInfo1 = #{username => <<"myuser1">>,
|
||||||
password => <<"mypassword1">>},
|
password => <<"mypassword1">>},
|
||||||
|
@ -110,37 +108,35 @@ t_import(_) ->
|
||||||
ClientInfo2 = ClientInfo1#{username => <<"myuser3">>,
|
ClientInfo2 = ClientInfo1#{username => <<"myuser3">>,
|
||||||
password => <<"mypassword3">>},
|
password => <<"mypassword3">>},
|
||||||
?assertEqual({stop, ok}, ?AUTH:authenticate(ClientInfo2, ok)),
|
?assertEqual({stop, ok}, ?AUTH:authenticate(ClientInfo2, ok)),
|
||||||
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, AuthenticatorName)),
|
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
t_multi_mnesia_authenticator(_) ->
|
t_multi_mnesia_authenticator(_) ->
|
||||||
AuthenticatorName1 = <<"myauthenticator1">>,
|
AuthenticatorName1 = <<"myauthenticator1">>,
|
||||||
AuthenticatorConfig1 = #{name => AuthenticatorName1,
|
AuthenticatorConfig1 = #{name => AuthenticatorName1,
|
||||||
mechanism => 'password-based',
|
mechanism => 'password-based',
|
||||||
config => #{
|
server_type => 'built-in-database',
|
||||||
server_type => 'built-in-database',
|
user_id_type => username,
|
||||||
user_id_type => username,
|
password_hash_algorithm => #{
|
||||||
password_hash_algorithm => #{
|
name => sha256
|
||||||
name => sha256
|
}},
|
||||||
}}},
|
|
||||||
AuthenticatorName2 = <<"myauthenticator2">>,
|
AuthenticatorName2 = <<"myauthenticator2">>,
|
||||||
AuthenticatorConfig2 = #{name => AuthenticatorName2,
|
AuthenticatorConfig2 = #{name => AuthenticatorName2,
|
||||||
mechanism => 'password-based',
|
mechanism => 'password-based',
|
||||||
config => #{
|
server_type => 'built-in-database',
|
||||||
server_type => 'built-in-database',
|
user_id_type => clientid,
|
||||||
user_id_type => clientid,
|
password_hash_algorithm => #{
|
||||||
password_hash_algorithm => #{
|
name => sha256
|
||||||
name => sha256
|
}},
|
||||||
}}},
|
{ok, #{name := AuthenticatorName1, id := ID1}} = ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig1),
|
||||||
?assertEqual({ok, AuthenticatorConfig1}, ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig1)),
|
{ok, #{name := AuthenticatorName2, id := ID2}} = ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig2),
|
||||||
?assertEqual({ok, AuthenticatorConfig2}, ?AUTH:create_authenticator(?CHAIN, AuthenticatorConfig2)),
|
|
||||||
|
|
||||||
?assertEqual({ok, #{user_id => <<"myuser">>}},
|
?assertEqual({ok, #{user_id => <<"myuser">>}},
|
||||||
?AUTH:add_user(?CHAIN, AuthenticatorName1,
|
?AUTH:add_user(?CHAIN, ID1,
|
||||||
#{<<"user_id">> => <<"myuser">>,
|
#{<<"user_id">> => <<"myuser">>,
|
||||||
<<"password">> => <<"mypass1">>})),
|
<<"password">> => <<"mypass1">>})),
|
||||||
?assertEqual({ok, #{user_id => <<"myclient">>}},
|
?assertEqual({ok, #{user_id => <<"myclient">>}},
|
||||||
?AUTH:add_user(?CHAIN, AuthenticatorName2,
|
?AUTH:add_user(?CHAIN, ID2,
|
||||||
#{<<"user_id">> => <<"myclient">>,
|
#{<<"user_id">> => <<"myclient">>,
|
||||||
<<"password">> => <<"mypass2">>})),
|
<<"password">> => <<"mypass2">>})),
|
||||||
|
|
||||||
|
@ -148,12 +144,12 @@ t_multi_mnesia_authenticator(_) ->
|
||||||
clientid => <<"myclient">>,
|
clientid => <<"myclient">>,
|
||||||
password => <<"mypass1">>},
|
password => <<"mypass1">>},
|
||||||
?assertEqual({stop, ok}, ?AUTH:authenticate(ClientInfo1, ok)),
|
?assertEqual({stop, ok}, ?AUTH:authenticate(ClientInfo1, ok)),
|
||||||
?assertEqual(ok, ?AUTH:move_authenticator_to_the_front(?CHAIN, AuthenticatorName2)),
|
?assertEqual(ok, ?AUTH:move_authenticator_to_the_nth(?CHAIN, ID2, 1)),
|
||||||
|
|
||||||
?assertEqual({stop, {error, bad_username_or_password}}, ?AUTH:authenticate(ClientInfo1, ok)),
|
?assertEqual({stop, {error, bad_username_or_password}}, ?AUTH:authenticate(ClientInfo1, ok)),
|
||||||
ClientInfo2 = ClientInfo1#{password => <<"mypass2">>},
|
ClientInfo2 = ClientInfo1#{password => <<"mypass2">>},
|
||||||
?assertEqual({stop, ok}, ?AUTH:authenticate(ClientInfo2, ok)),
|
?assertEqual({stop, ok}, ?AUTH:authenticate(ClientInfo2, ok)),
|
||||||
|
|
||||||
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, AuthenticatorName1)),
|
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID1)),
|
||||||
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, AuthenticatorName2)),
|
?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID2)),
|
||||||
ok.
|
ok.
|
||||||
|
|
Loading…
Reference in New Issue