Merge pull request #5679 from tigercl/chore/authn

chore(authn): update apis for user
This commit is contained in:
tigercl 2021-09-08 11:20:23 +08:00 committed by GitHub
commit f75778b037
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 744 additions and 452 deletions

View File

@ -147,6 +147,6 @@
}). }).
-record(chain, -record(chain,
{ name :: binary() { name :: atom()
, authenticators :: [#authenticator{}] , authenticators :: [#authenticator{}]
}). }).

View File

@ -29,9 +29,9 @@
-spec(authenticate(emqx_types:clientinfo()) -> -spec(authenticate(emqx_types:clientinfo()) ->
{ok, map()} | {ok, map(), binary()} | {continue, map()} | {continue, binary(), map()} | {error, term()}). {ok, map()} | {ok, map(), binary()} | {continue, map()} | {continue, binary(), map()} | {error, term()}).
authenticate(Credential) -> authenticate(Credential) ->
case run_hooks('client.authenticate', [Credential], {ok, #{superuser => false}}) of case run_hooks('client.authenticate', [Credential], {ok, #{is_superuser => false}}) of
ok -> ok ->
{ok, #{superuser => false}}; {ok, #{is_superuser => false}};
Other -> Other ->
Other Other
end. end.

View File

@ -80,7 +80,7 @@
-type config() :: #{atom() => term()}. -type config() :: #{atom() => term()}.
-type state() :: #{atom() => term()}. -type state() :: #{atom() => term()}.
-type extra() :: #{superuser := boolean(), -type extra() :: #{is_superuser := boolean(),
atom() => term()}. atom() => term()}.
-type user_info() :: #{user_id := binary(), -type user_info() :: #{user_id := binary(),
atom() => term()}. atom() => term()}.
@ -473,7 +473,7 @@ handle_call({update_authenticator, ChainName, AuthenticatorID, Config}, _From, S
state = #{version := Version} = ST} = Authenticator -> state = #{version := Version} = ST} = Authenticator ->
case AuthenticatorID =:= generate_id(Config) of case AuthenticatorID =:= generate_id(Config) of
true -> true ->
Unique = <<ChainName/binary, "/", AuthenticatorID/binary, ":", Version/binary>>, Unique = unique(ChainName, AuthenticatorID, Version),
case Provider:update(Config#{'_unique' => Unique}, ST) of case Provider:update(Config#{'_unique' => Unique}, ST) of
{ok, NewST} -> {ok, NewST} ->
NewAuthenticator = Authenticator#authenticator{state = switch_version(NewST)}, NewAuthenticator = Authenticator#authenticator{state = switch_version(NewST)},
@ -575,17 +575,17 @@ split_by_id(ID, AuthenticatorsConfig) ->
end. end.
global_chain(mqtt) -> global_chain(mqtt) ->
<<"mqtt:global">>; 'mqtt:global';
global_chain('mqtt-sn') -> global_chain('mqtt-sn') ->
<<"mqtt-sn:global">>; 'mqtt-sn:global';
global_chain(coap) -> global_chain(coap) ->
<<"coap:global">>; 'coap:global';
global_chain(lwm2m) -> global_chain(lwm2m) ->
<<"lwm2m:global">>; 'lwm2m:global';
global_chain(stomp) -> global_chain(stomp) ->
<<"stomp:global">>; 'stomp:global';
global_chain(_) -> global_chain(_) ->
<<"unknown:global">>. 'unknown:global'.
may_hook(#{hooked := false} = State) -> may_hook(#{hooked := false} = State) ->
case lists:any(fun(#chain{authenticators = []}) -> false; case lists:any(fun(#chain{authenticators = []}) -> false;
@ -618,7 +618,7 @@ do_create_authenticator(ChainName, AuthenticatorID, #{enable := Enable} = Config
undefined -> undefined ->
{error, no_available_provider}; {error, no_available_provider};
Provider -> Provider ->
Unique = <<ChainName/binary, "/", AuthenticatorID/binary, ":", ?VER_1/binary>>, Unique = unique(ChainName, AuthenticatorID, ?VER_1),
case Provider:create(Config#{'_unique' => Unique}) of case Provider:create(Config#{'_unique' => Unique}) of
{ok, State} -> {ok, State} ->
Authenticator = #authenticator{id = AuthenticatorID, Authenticator = #authenticator{id = AuthenticatorID,
@ -704,6 +704,10 @@ serialize_authenticator(#authenticator{id = ID,
, state => State , state => State
}. }.
unique(ChainName, AuthenticatorID, Version) ->
NChainName = atom_to_binary(ChainName),
<<NChainName/binary, "/", AuthenticatorID/binary, ":", Version/binary>>.
switch_version(State = #{version := ?VER_1}) -> switch_version(State = #{version := ?VER_1}) ->
State#{version := ?VER_2}; State#{version := ?VER_2};
switch_version(State = #{version := ?VER_2}) -> switch_version(State = #{version := ?VER_2}) ->

View File

@ -214,7 +214,7 @@ init(ConnInfo = #{peername := {PeerHost, _Port},
ClientInfo = set_peercert_infos( ClientInfo = set_peercert_infos(
Peercert, Peercert,
#{zone => Zone, #{zone => Zone,
listener => Listener, listener => emqx_listeners:listener_id(Type, Listener),
protocol => Protocol, protocol => Protocol,
peerhost => PeerHost, peerhost => PeerHost,
sockport => SockPort, sockport => SockPort,
@ -223,7 +223,7 @@ init(ConnInfo = #{peername := {PeerHost, _Port},
mountpoint => MountPoint, mountpoint => MountPoint,
is_bridge => false, is_bridge => false,
is_superuser => false is_superuser => false
}, Zone, Listener), }, Zone),
{NClientInfo, NConnInfo} = take_ws_cookie(ClientInfo, ConnInfo), {NClientInfo, NConnInfo} = take_ws_cookie(ClientInfo, ConnInfo),
#channel{conninfo = NConnInfo, #channel{conninfo = NConnInfo,
clientinfo = NClientInfo, clientinfo = NClientInfo,
@ -244,12 +244,12 @@ quota_policy(RawPolicy) ->
erlang:trunc(hocon_postprocess:duration(StrWind) / 1000)}} erlang:trunc(hocon_postprocess:duration(StrWind) / 1000)}}
|| {Name, [StrCount, StrWind]} <- maps:to_list(RawPolicy)]. || {Name, [StrCount, StrWind]} <- maps:to_list(RawPolicy)].
set_peercert_infos(NoSSL, ClientInfo, _, _) set_peercert_infos(NoSSL, ClientInfo, _)
when NoSSL =:= nossl; when NoSSL =:= nossl;
NoSSL =:= undefined -> NoSSL =:= undefined ->
ClientInfo#{username => undefined}; ClientInfo#{username => undefined};
set_peercert_infos(Peercert, ClientInfo, Zone, _Listener) -> set_peercert_infos(Peercert, ClientInfo, Zone) ->
{DN, CN} = {esockd_peercert:subject(Peercert), {DN, CN} = {esockd_peercert:subject(Peercert),
esockd_peercert:common_name(Peercert)}, esockd_peercert:common_name(Peercert)},
PeercetAs = fun(Key) -> PeercetAs = fun(Key) ->
@ -1303,11 +1303,11 @@ do_authenticate(#{auth_method := AuthMethod} = Credential, #channel{clientinfo =
case emqx_access_control:authenticate(Credential) of case emqx_access_control:authenticate(Credential) of
{ok, Result} -> {ok, Result} ->
{ok, Properties, {ok, Properties,
Channel#channel{clientinfo = ClientInfo#{is_superuser => maps:get(superuser, Result, false)}, Channel#channel{clientinfo = ClientInfo#{is_superuser => maps:get(is_superuser, Result, false)},
auth_cache = #{}}}; auth_cache = #{}}};
{ok, Result, AuthData} -> {ok, Result, AuthData} ->
{ok, Properties#{'Authentication-Data' => AuthData}, {ok, Properties#{'Authentication-Data' => AuthData},
Channel#channel{clientinfo = ClientInfo#{is_superuser => maps:get(superuser, Result, false)}, Channel#channel{clientinfo = ClientInfo#{is_superuser => maps:get(is_superuser, Result, false)},
auth_cache = #{}}}; auth_cache = #{}}};
{continue, AuthCache} -> {continue, AuthCache} ->
{continue, Properties, Channel#channel{auth_cache = AuthCache}}; {continue, Properties, Channel#channel{auth_cache = AuthCache}};
@ -1320,8 +1320,8 @@ do_authenticate(#{auth_method := AuthMethod} = Credential, #channel{clientinfo =
do_authenticate(Credential, #channel{clientinfo = ClientInfo} = Channel) -> do_authenticate(Credential, #channel{clientinfo = ClientInfo} = Channel) ->
case emqx_access_control:authenticate(Credential) of case emqx_access_control:authenticate(Credential) of
{ok, #{superuser := Superuser}} -> {ok, #{is_superuser := IsSuperuser}} ->
{ok, #{}, Channel#channel{clientinfo = ClientInfo#{is_superuser => Superuser}}}; {ok, #{}, Channel#channel{clientinfo = ClientInfo#{is_superuser => IsSuperuser}}};
{error, Reason} -> {error, Reason} ->
{error, emqx_reason_codes:connack_error(Reason)} {error, emqx_reason_codes:connack_error(Reason)}
end. end.

View File

@ -73,7 +73,7 @@ update(_Config, _State) ->
{ok, #{mark => 2}}. {ok, #{mark => 2}}.
authenticate(#{username := <<"good">>}, _State) -> authenticate(#{username := <<"good">>}, _State) ->
{ok, #{superuser => true}}; {ok, #{is_superuser => true}};
authenticate(#{username := _}, _State) -> authenticate(#{username := _}, _State) ->
{error, bad_username_or_password}. {error, bad_username_or_password}.
@ -94,7 +94,7 @@ end_per_suite(_) ->
t_chain(_) -> t_chain(_) ->
% CRUD of authentication chain % CRUD of authentication chain
ChainName = <<"test">>, ChainName = 'test',
?assertMatch({ok, []}, ?AUTHN:list_chains()), ?assertMatch({ok, []}, ?AUTHN:list_chains()),
?assertMatch({ok, #{name := ChainName, authenticators := []}}, ?AUTHN:create_chain(ChainName)), ?assertMatch({ok, #{name := ChainName, authenticators := []}}, ?AUTHN:create_chain(ChainName)),
?assertEqual({error, {already_exists, {chain, ChainName}}}, ?AUTHN:create_chain(ChainName)), ?assertEqual({error, {already_exists, {chain, ChainName}}}, ?AUTHN:create_chain(ChainName)),
@ -105,7 +105,7 @@ t_chain(_) ->
ok. ok.
t_authenticator(_) -> t_authenticator(_) ->
ChainName = <<"test">>, ChainName = 'test',
AuthenticatorConfig1 = #{mechanism => 'password-based', AuthenticatorConfig1 = #{mechanism => 'password-based',
backend => 'built-in-database', backend => 'built-in-database',
enable => true}, enable => true},
@ -155,13 +155,13 @@ t_authenticator(_) ->
ok. ok.
t_authenticate(_) -> t_authenticate(_) ->
ListenerID = <<"tcp:default">>, ListenerID = 'tcp:default',
ClientInfo = #{zone => default, ClientInfo = #{zone => default,
listener => ListenerID, listener => ListenerID,
protocol => mqtt, protocol => mqtt,
username => <<"good">>, username => <<"good">>,
password => <<"any">>}, password => <<"any">>},
?assertEqual({ok, #{superuser => false}}, emqx_access_control:authenticate(ClientInfo)), ?assertEqual({ok, #{is_superuser => false}}, emqx_access_control:authenticate(ClientInfo)),
AuthNType = {'password-based', 'built-in-database'}, AuthNType = {'password-based', 'built-in-database'},
?AUTHN:add_provider(AuthNType, ?MODULE), ?AUTHN:add_provider(AuthNType, ?MODULE),
@ -171,7 +171,7 @@ t_authenticate(_) ->
enable => true}, enable => true},
?AUTHN:create_chain(ListenerID), ?AUTHN:create_chain(ListenerID),
?assertMatch({ok, _}, ?AUTHN:create_authenticator(ListenerID, AuthenticatorConfig)), ?assertMatch({ok, _}, ?AUTHN:create_authenticator(ListenerID, AuthenticatorConfig)),
?assertEqual({ok, #{superuser => true}}, emqx_access_control:authenticate(ClientInfo)), ?assertEqual({ok, #{is_superuser => true}}, emqx_access_control:authenticate(ClientInfo)),
?assertEqual({error, bad_username_or_password}, emqx_access_control:authenticate(ClientInfo#{username => <<"bad">>})), ?assertEqual({error, bad_username_or_password}, emqx_access_control:authenticate(ClientInfo#{username => <<"bad">>})),
?AUTHN:delete_chain(ListenerID), ?AUTHN:delete_chain(ListenerID),
@ -186,7 +186,7 @@ t_update_config(_) ->
?AUTHN:add_provider(AuthNType1, ?MODULE), ?AUTHN:add_provider(AuthNType1, ?MODULE),
?AUTHN:add_provider(AuthNType2, ?MODULE), ?AUTHN:add_provider(AuthNType2, ?MODULE),
Global = <<"mqtt:global">>, Global = 'mqtt:global',
AuthenticatorConfig1 = #{mechanism => 'password-based', AuthenticatorConfig1 = #{mechanism => 'password-based',
backend => 'built-in-database', backend => 'built-in-database',
enable => true}, enable => true},
@ -212,7 +212,7 @@ t_update_config(_) ->
?assertMatch({ok, _}, update_config([authentication], {delete_authenticator, Global, ID1})), ?assertMatch({ok, _}, update_config([authentication], {delete_authenticator, Global, ID1})),
?assertEqual({error, {not_found, {authenticator, ID1}}}, ?AUTHN:lookup_authenticator(Global, ID1)), ?assertEqual({error, {not_found, {authenticator, ID1}}}, ?AUTHN:lookup_authenticator(Global, ID1)),
ListenerID = <<"tcp:default">>, ListenerID = 'tcp:default',
ConfKeyPath = [listeners, tcp, default, authentication], ConfKeyPath = [listeners, tcp, default, authentication],
?assertMatch({ok, _}, update_config(ConfKeyPath, {create_authenticator, ListenerID, AuthenticatorConfig1})), ?assertMatch({ok, _}, update_config(ConfKeyPath, {create_authenticator, ListenerID, AuthenticatorConfig1})),
?assertMatch({ok, #{id := ID1, state := #{mark := 1}}}, ?AUTHN:lookup_authenticator(ListenerID, ID1)), ?assertMatch({ok, #{id := ID1, state := #{mark := 1}}}, ?AUTHN:lookup_authenticator(ListenerID, ID1)),

View File

@ -144,7 +144,7 @@ init_per_suite(Config) ->
%% Access Control Meck %% Access Control Meck
ok = meck:new(emqx_access_control, [passthrough, no_history, no_link]), ok = meck:new(emqx_access_control, [passthrough, no_history, no_link]),
ok = meck:expect(emqx_access_control, authenticate, ok = meck:expect(emqx_access_control, authenticate,
fun(_) -> {ok, #{superuser => false}} end), fun(_) -> {ok, #{is_superuser => false}} end),
ok = meck:expect(emqx_access_control, authorize, fun(_, _, _) -> allow end), ok = meck:expect(emqx_access_control, authorize, fun(_, _, _) -> allow end),
%% Broker Meck %% Broker Meck
ok = meck:new(emqx_broker, [passthrough, no_history, no_link]), ok = meck:new(emqx_broker, [passthrough, no_history, no_link]),

View File

@ -1,3 +1,3 @@
user_id,password_hash,salt,superuser user_id,password_hash,salt,is_superuser
myuser3,b6c743545a7817ae8c8f624371d5f5f0373234bb0ff36b8ffbf19bce0e06ab75,de1024f462fb83910fd13151bd4bd235,true myuser3,b6c743545a7817ae8c8f624371d5f5f0373234bb0ff36b8ffbf19bce0e06ab75,de1024f462fb83910fd13151bd4bd235,true
myuser4,ee68c985a69208b6eda8c6c9b4c7c2d2b15ee2352cdd64a903171710a99182e8,ad773b5be9dd0613fe6c2f4d8c403139,false myuser4,ee68c985a69208b6eda8c6c9b4c7c2d2b15ee2352cdd64a903171710a99182e8,ad773b5be9dd0613fe6c2f4d8c403139,false

1 user_id password_hash salt superuser is_superuser
2 myuser3 b6c743545a7817ae8c8f624371d5f5f0373234bb0ff36b8ffbf19bce0e06ab75 de1024f462fb83910fd13151bd4bd235 true true
3 myuser4 ee68c985a69208b6eda8c6c9b4c7c2d2b15ee2352cdd64a903171710a99182e8 ad773b5be9dd0613fe6c2f4d8c403139 false false

View File

@ -3,12 +3,12 @@
"user_id":"myuser1", "user_id":"myuser1",
"password_hash":"c5e46903df45e5dc096dc74657610dbee8deaacae656df88a1788f1847390242", "password_hash":"c5e46903df45e5dc096dc74657610dbee8deaacae656df88a1788f1847390242",
"salt": "e378187547bf2d6f0545a3f441aa4d8a", "salt": "e378187547bf2d6f0545a3f441aa4d8a",
"superuser": true "is_superuser": true
}, },
{ {
"user_id":"myuser2", "user_id":"myuser2",
"password_hash":"f4d17f300b11e522fd33f497c11b126ef1ea5149c74d2220f9a16dc876d4567b", "password_hash":"f4d17f300b11e522fd33f497c11b126ef1ea5149c74d2220f9a16dc876d4567b",
"salt": "6d3f9bd5b54d94b98adbcfe10b6d181f", "salt": "6d3f9bd5b54d94b98adbcfe10b6d181f",
"superuser": false "is_superuser": false
} }
] ]

View File

@ -18,7 +18,7 @@
-define(AUTHN, emqx_authentication). -define(AUTHN, emqx_authentication).
-define(GLOBAL, <<"mqtt:global">>). -define(GLOBAL, 'mqtt:global').
-define(RE_PLACEHOLDER, "\\$\\{[a-z0-9\\-]+\\}"). -define(RE_PLACEHOLDER, "\\$\\{[a-z0-9\\-]+\\}").

File diff suppressed because it is too large Load Diff

View File

@ -53,7 +53,7 @@ remove_providers() ->
initialize() -> initialize() ->
?AUTHN:initialize_authentication(?GLOBAL, emqx:get_raw_config([authentication], [])), ?AUTHN:initialize_authentication(?GLOBAL, emqx:get_raw_config([authentication], [])),
lists:foreach(fun({ListenerID, ListenerConfig}) -> lists:foreach(fun({ListenerID, ListenerConfig}) ->
?AUTHN:initialize_authentication(atom_to_binary(ListenerID), maps:get(authentication, ListenerConfig, [])) ?AUTHN:initialize_authentication(ListenerID, maps:get(authentication, ListenerConfig, []))
end, emqx_listeners:list()), end, emqx_listeners:list()),
ok. ok.

View File

@ -53,7 +53,7 @@
, stored_key , stored_key
, server_key , server_key
, salt , salt
, superuser , is_superuser
}). }).
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
@ -147,9 +147,9 @@ add_user(#{user_id := UserID,
fun() -> fun() ->
case mnesia:read(?TAB, {UserGroup, UserID}, write) of case mnesia:read(?TAB, {UserGroup, UserID}, write) of
[] -> [] ->
Superuser = maps:get(superuser, UserInfo, false), IsSuperuser = maps:get(is_superuser, UserInfo, false),
add_user(UserID, Password, Superuser, State), add_user(UserID, Password, IsSuperuser, State),
{ok, #{user_id => UserID, superuser => Superuser}}; {ok, #{user_id => UserID, is_superuser => IsSuperuser}};
[_] -> [_] ->
{error, already_exist} {error, already_exist}
end end
@ -173,8 +173,8 @@ update_user(UserID, User,
case mnesia:read(?TAB, {UserGroup, UserID}, write) of case mnesia:read(?TAB, {UserGroup, UserID}, write) of
[] -> [] ->
{error, not_found}; {error, not_found};
[#user_info{superuser = Superuser} = UserInfo] -> [#user_info{is_superuser = IsSuperuser} = UserInfo] ->
UserInfo1 = UserInfo#user_info{superuser = maps:get(superuser, User, Superuser)}, UserInfo1 = UserInfo#user_info{is_superuser = maps:get(is_superuser, User, IsSuperuser)},
UserInfo2 = case maps:get(password, User, undefined) of UserInfo2 = case maps:get(password, User, undefined) of
undefined -> undefined ->
UserInfo1; UserInfo1;
@ -229,36 +229,36 @@ check_client_first_message(Bin, _Cache, #{iteration_count := IterationCount} = S
{error, not_authorized} {error, not_authorized}
end. end.
check_client_final_message(Bin, #{superuser := Superuser} = Cache, #{algorithm := Alg}) -> check_client_final_message(Bin, #{is_superuser := IsSuperuser} = Cache, #{algorithm := Alg}) ->
case esasl_scram:check_client_final_message( case esasl_scram:check_client_final_message(
Bin, Bin,
Cache#{algorithm => Alg} Cache#{algorithm => Alg}
) of ) of
{ok, ServerFinalMessage} -> {ok, ServerFinalMessage} ->
{ok, #{superuser => Superuser}, ServerFinalMessage}; {ok, #{is_superuser => IsSuperuser}, ServerFinalMessage};
{error, _Reason} -> {error, _Reason} ->
{error, not_authorized} {error, not_authorized}
end. end.
add_user(UserID, Password, Superuser, State) -> add_user(UserID, Password, IsSuperuser, State) ->
{StoredKey, ServerKey, Salt} = esasl_scram:generate_authentication_info(Password, State), {StoredKey, ServerKey, Salt} = esasl_scram:generate_authentication_info(Password, State),
UserInfo = #user_info{user_id = UserID, UserInfo = #user_info{user_id = UserID,
stored_key = StoredKey, stored_key = StoredKey,
server_key = ServerKey, server_key = ServerKey,
salt = Salt, salt = Salt,
superuser = Superuser}, is_superuser = IsSuperuser},
mnesia:write(?TAB, UserInfo, write). mnesia:write(?TAB, UserInfo, write).
retrieve(UserID, #{user_group := UserGroup}) -> retrieve(UserID, #{user_group := UserGroup}) ->
case mnesia:dirty_read(?TAB, {UserGroup, UserID}) of case mnesia:dirty_read(?TAB, {UserGroup, UserID}) of
[#user_info{stored_key = StoredKey, [#user_info{stored_key = StoredKey,
server_key = ServerKey, server_key = ServerKey,
salt = Salt, salt = Salt,
superuser = Superuser}] -> is_superuser = IsSuperuser}] ->
{ok, #{stored_key => StoredKey, {ok, #{stored_key => StoredKey,
server_key => ServerKey, server_key => ServerKey,
salt => Salt, salt => Salt,
superuser => Superuser}}; is_superuser => IsSuperuser}};
[] -> [] ->
{error, not_found} {error, not_found}
end. end.
@ -273,5 +273,5 @@ trans(Fun, Args) ->
{aborted, Reason} -> {error, Reason} {aborted, Reason} -> {error, Reason}
end. end.
serialize_user_info(#user_info{user_id = {_, UserID}, superuser = Superuser}) -> serialize_user_info(#user_info{user_id = {_, UserID}, is_superuser = IsSuperuser}) ->
#{user_id => UserID, superuser => Superuser}. #{user_id => UserID, is_superuser => IsSuperuser}.

View File

@ -161,16 +161,16 @@ authenticate(Credential, #{'_unique' := Unique,
try try
Request = generate_request(Credential, State), Request = generate_request(Credential, State),
case emqx_resource:query(Unique, {Method, Request, RequestTimeout}) of case emqx_resource:query(Unique, {Method, Request, RequestTimeout}) of
{ok, 204, _Headers} -> {ok, #{superuser => false}}; {ok, 204, _Headers} -> {ok, #{is_superuser => false}};
{ok, 200, Headers, Body} -> {ok, 200, Headers, Body} ->
ContentType = proplists:get_value(<<"content-type">>, Headers, <<"application/json">>), ContentType = proplists:get_value(<<"content-type">>, Headers, <<"application/json">>),
case safely_parse_body(ContentType, Body) of case safely_parse_body(ContentType, Body) of
{ok, NBody} -> {ok, NBody} ->
%% TODO: Return by user property %% TODO: Return by user property
{ok, #{superuser => maps:get(<<"superuser">>, NBody, false), {ok, #{is_superuser => maps:get(<<"is_superuser">>, NBody, false),
user_property => NBody}}; user_property => NBody}};
{error, _Reason} -> {error, _Reason} ->
{ok, #{superuser => false}} {ok, #{is_superuser => false}}
end; end;
{error, _Reason} -> {error, _Reason} ->
ignore ignore

View File

@ -249,7 +249,7 @@ verify(JWS, [JWK | More], VerifyClaims) ->
Claims = emqx_json:decode(Payload, [return_maps]), Claims = emqx_json:decode(Payload, [return_maps]),
case verify_claims(Claims, VerifyClaims) of case verify_claims(Claims, VerifyClaims) of
ok -> ok ->
{ok, #{superuser => maps:get(<<"superuser">>, Claims, false)}}; {ok, #{is_superuser => maps:get(<<"is_superuser">>, Claims, false)}};
{error, Reason} -> {error, Reason} ->
{error, Reason} {error, Reason}
end; end;

View File

@ -51,7 +51,7 @@
{ user_id :: {user_group(), user_id()} { user_id :: {user_group(), user_id()}
, password_hash :: binary() , password_hash :: binary()
, salt :: binary() , salt :: binary()
, superuser :: boolean() , is_superuser :: boolean()
}). }).
-reflect_type([ user_id_type/0 ]). -reflect_type([ user_id_type/0 ]).
@ -158,13 +158,13 @@ authenticate(#{password := Password} = Credential,
case mnesia:dirty_read(?TAB, {UserGroup, UserID}) of case mnesia:dirty_read(?TAB, {UserGroup, UserID}) of
[] -> [] ->
ignore; ignore;
[#user_info{password_hash = PasswordHash, salt = Salt0, superuser = Superuser}] -> [#user_info{password_hash = PasswordHash, salt = Salt0, is_superuser = IsSuperuser}] ->
Salt = case Algorithm of Salt = case Algorithm of
bcrypt -> PasswordHash; bcrypt -> PasswordHash;
_ -> Salt0 _ -> Salt0
end, end,
case PasswordHash =:= hash(Algorithm, Password, Salt) of case PasswordHash =:= hash(Algorithm, Password, Salt) of
true -> {ok, #{superuser => Superuser}}; true -> {ok, #{is_superuser => IsSuperuser}};
false -> {error, bad_username_or_password} false -> {error, bad_username_or_password}
end end
end. end.
@ -197,9 +197,9 @@ add_user(#{user_id := UserID,
case mnesia:read(?TAB, {UserGroup, UserID}, write) of case mnesia:read(?TAB, {UserGroup, UserID}, write) of
[] -> [] ->
{PasswordHash, Salt} = hash(Password, State), {PasswordHash, Salt} = hash(Password, State),
Superuser = maps:get(superuser, UserInfo, false), IsSuperuser = maps:get(is_superuser, UserInfo, false),
insert_user(UserGroup, UserID, PasswordHash, Salt, Superuser), insert_user(UserGroup, UserID, PasswordHash, Salt, IsSuperuser),
{ok, #{user_id => UserID, superuser => Superuser}}; {ok, #{user_id => UserID, is_superuser => IsSuperuser}};
[_] -> [_] ->
{error, already_exist} {error, already_exist}
end end
@ -225,8 +225,8 @@ update_user(UserID, UserInfo,
{error, not_found}; {error, not_found};
[#user_info{ password_hash = PasswordHash [#user_info{ password_hash = PasswordHash
, salt = Salt , salt = Salt
, superuser = Superuser}] -> , is_superuser = IsSuperuser}] ->
NSuperuser = maps:get(superuser, UserInfo, Superuser), NSuperuser = maps:get(is_superuser, UserInfo, IsSuperuser),
{NPasswordHash, NSalt} = case maps:get(password, UserInfo, undefined) of {NPasswordHash, NSalt} = case maps:get(password, UserInfo, undefined) of
undefined -> undefined ->
{PasswordHash, Salt}; {PasswordHash, Salt};
@ -234,7 +234,7 @@ update_user(UserID, UserInfo,
hash(Password, State) hash(Password, State)
end, end,
insert_user(UserGroup, UserID, NPasswordHash, NSalt, NSuperuser), insert_user(UserGroup, UserID, NPasswordHash, NSalt, NSuperuser),
{ok, #{user_id => UserID, superuser => NSuperuser}} {ok, #{user_id => UserID, is_superuser => NSuperuser}}
end end
end). end).
@ -290,8 +290,8 @@ import(UserGroup, [#{<<"user_id">> := UserID,
<<"password_hash">> := PasswordHash} = UserInfo | More]) <<"password_hash">> := PasswordHash} = UserInfo | More])
when is_binary(UserID) andalso is_binary(PasswordHash) -> when is_binary(UserID) andalso is_binary(PasswordHash) ->
Salt = maps:get(<<"salt">>, UserInfo, <<>>), Salt = maps:get(<<"salt">>, UserInfo, <<>>),
Superuser = maps:get(<<"superuser">>, UserInfo, false), IsSuperuser = maps:get(<<"is_superuser">>, UserInfo, false),
insert_user(UserGroup, UserID, PasswordHash, Salt, Superuser), insert_user(UserGroup, UserID, PasswordHash, Salt, IsSuperuser),
import(UserGroup, More); import(UserGroup, More);
import(_UserGroup, [_ | _More]) -> import(_UserGroup, [_ | _More]) ->
{error, bad_format}. {error, bad_format}.
@ -305,8 +305,8 @@ import(UserGroup, File, Seq) ->
{ok, #{user_id := UserID, {ok, #{user_id := UserID,
password_hash := PasswordHash} = UserInfo} -> password_hash := PasswordHash} = UserInfo} ->
Salt = maps:get(salt, UserInfo, <<>>), Salt = maps:get(salt, UserInfo, <<>>),
Superuser = maps:get(superuser, UserInfo, false), IsSuperuser = maps:get(is_superuser, UserInfo, false),
insert_user(UserGroup, UserID, PasswordHash, Salt, Superuser), insert_user(UserGroup, UserID, PasswordHash, Salt, IsSuperuser),
import(UserGroup, File, Seq); import(UserGroup, File, Seq);
{error, Reason} -> {error, Reason} ->
{error, Reason} {error, Reason}
@ -341,10 +341,10 @@ get_user_info_by_seq([PasswordHash | More1], [<<"password_hash">> | More2], Acc)
get_user_info_by_seq(More1, More2, Acc#{password_hash => PasswordHash}); get_user_info_by_seq(More1, More2, Acc#{password_hash => PasswordHash});
get_user_info_by_seq([Salt | More1], [<<"salt">> | More2], Acc) -> get_user_info_by_seq([Salt | More1], [<<"salt">> | More2], Acc) ->
get_user_info_by_seq(More1, More2, Acc#{salt => Salt}); get_user_info_by_seq(More1, More2, Acc#{salt => Salt});
get_user_info_by_seq([<<"true">> | More1], [<<"superuser">> | More2], Acc) -> get_user_info_by_seq([<<"true">> | More1], [<<"is_superuser">> | More2], Acc) ->
get_user_info_by_seq(More1, More2, Acc#{superuser => true}); get_user_info_by_seq(More1, More2, Acc#{is_superuser => true});
get_user_info_by_seq([<<"false">> | More1], [<<"superuser">> | More2], Acc) -> get_user_info_by_seq([<<"false">> | More1], [<<"is_superuser">> | More2], Acc) ->
get_user_info_by_seq(More1, More2, Acc#{superuser => false}); get_user_info_by_seq(More1, More2, Acc#{is_superuser => false});
get_user_info_by_seq(_, _, _) -> get_user_info_by_seq(_, _, _) ->
{error, bad_format}. {error, bad_format}.
@ -368,11 +368,11 @@ hash(Password, #{password_hash_algorithm := Algorithm} = State) ->
PasswordHash = hash(Algorithm, Password, Salt), PasswordHash = hash(Algorithm, Password, Salt),
{PasswordHash, Salt}. {PasswordHash, Salt}.
insert_user(UserGroup, UserID, PasswordHash, Salt, Superuser) -> insert_user(UserGroup, UserID, PasswordHash, Salt, IsSuperuser) ->
UserInfo = #user_info{user_id = {UserGroup, UserID}, UserInfo = #user_info{user_id = {UserGroup, UserID},
password_hash = PasswordHash, password_hash = PasswordHash,
salt = Salt, salt = Salt,
superuser = Superuser}, is_superuser = IsSuperuser},
mnesia:write(?TAB, UserInfo, write). mnesia:write(?TAB, UserInfo, write).
delete_user2(UserInfo) -> delete_user2(UserInfo) ->
@ -400,5 +400,5 @@ to_binary(B) when is_binary(B) ->
to_binary(L) when is_list(L) -> to_binary(L) when is_list(L) ->
iolist_to_binary(L). iolist_to_binary(L).
serialize_user_info(#user_info{user_id = {_, UserID}, superuser = Superuser}) -> serialize_user_info(#user_info{user_id = {_, UserID}, is_superuser = IsSuperuser}) ->
#{user_id => UserID, superuser => Superuser}. #{user_id => UserID, is_superuser => IsSuperuser}.

View File

@ -64,6 +64,7 @@ common_fields() ->
, {selector, fun selector/1} , {selector, fun selector/1}
, {password_hash_field, fun password_hash_field/1} , {password_hash_field, fun password_hash_field/1}
, {salt_field, fun salt_field/1} , {salt_field, fun salt_field/1}
, {is_superuser_field, fun is_superuser_field/1}
, {password_hash_algorithm, fun password_hash_algorithm/1} , {password_hash_algorithm, fun password_hash_algorithm/1}
, {salt_position, fun salt_position/1} , {salt_position, fun salt_position/1}
] ++ emqx_authn_schema:common_fields(). ] ++ emqx_authn_schema:common_fields().
@ -84,6 +85,10 @@ salt_field(type) -> binary();
salt_field(nullable) -> true; salt_field(nullable) -> true;
salt_field(_) -> undefined. salt_field(_) -> undefined.
is_superuser_field(type) -> binary();
is_superuser_field(nullable) -> true;
is_superuser_field(_) -> undefined.
password_hash_algorithm(type) -> {enum, [plain, md5, sha, sha256, sha512, bcrypt]}; password_hash_algorithm(type) -> {enum, [plain, md5, sha, sha256, sha512, bcrypt]};
password_hash_algorithm(default) -> sha256; password_hash_algorithm(default) -> sha256;
password_hash_algorithm(_) -> undefined. password_hash_algorithm(_) -> undefined.
@ -109,6 +114,7 @@ create(#{ selector := Selector
State = maps:with([ collection State = maps:with([ collection
, password_hash_field , password_hash_field
, salt_field , salt_field
, is_superuser_field
, password_hash_algorithm , password_hash_algorithm
, salt_position , salt_position
, '_unique'], Config), , '_unique'], Config),
@ -149,7 +155,7 @@ authenticate(#{password := Password} = Credential,
Doc -> Doc ->
case check_password(Password, Doc, State) of case check_password(Password, Doc, State) of
ok -> ok ->
{ok, #{superuser => superuser(Doc, State)}}; {ok, #{is_superuser => is_superuser(Doc, State)}};
{error, {cannot_find_password_hash_field, PasswordHashField}} -> {error, {cannot_find_password_hash_field, PasswordHashField}} ->
?LOG(error, "['~s'] Can't find password hash field: ~s", [Unique, PasswordHashField]), ?LOG(error, "['~s'] Can't find password hash field: ~s", [Unique, PasswordHashField]),
{error, bad_username_or_password}; {error, bad_username_or_password};
@ -230,9 +236,9 @@ check_password(Password,
end end
end. end.
superuser(Doc, #{superuser_field := SuperuserField}) -> is_superuser(Doc, #{is_superuser_field := IsSuperuserField}) ->
maps:get(SuperuserField, Doc, false); maps:get(IsSuperuserField, Doc, false);
superuser(_, _) -> is_superuser(_, _) ->
false. false.
hash(Algorithm, Password, Salt, prefix) -> hash(Algorithm, Password, Salt, prefix) ->

View File

@ -123,7 +123,7 @@ authenticate(#{password := Password} = Credential,
Selected = maps:from_list(lists:zip(Columns, Rows)), Selected = maps:from_list(lists:zip(Columns, Rows)),
case check_password(Password, Selected, State) of case check_password(Password, Selected, State) of
ok -> ok ->
{ok, #{superuser => maps:get(<<"superuser">>, Selected, false)}}; {ok, #{is_superuser => maps:get(<<"is_superuser">>, Selected, false)}};
{error, Reason} -> {error, Reason} ->
{error, Reason} {error, Reason}
end; end;

View File

@ -113,7 +113,7 @@ authenticate(#{password := Password} = Credential,
Selected = maps:from_list(lists:zip(NColumns, Rows)), Selected = maps:from_list(lists:zip(NColumns, Rows)),
case check_password(Password, Selected, State) of case check_password(Password, Selected, State) of
ok -> ok ->
{ok, #{superuser => maps:get(<<"superuser">>, Selected, false)}}; {ok, #{is_superuser => maps:get(<<"is_superuser">>, Selected, false)}};
{error, Reason} -> {error, Reason} ->
{error, Reason} {error, Reason}
end; end;

View File

@ -135,7 +135,7 @@ authenticate(#{password := Password} = Credential,
Selected = merge(Fields, Values), Selected = merge(Fields, Values),
case check_password(Password, Selected, State) of case check_password(Password, Selected, State) of
ok -> ok ->
{ok, #{superuser => maps:get("superuser", Selected, false)}}; {ok, #{is_superuser => maps:get("is_superuser", Selected, false)}};
{error, Reason} -> {error, Reason} ->
{error, Reason} {error, Reason}
end; end;
@ -180,7 +180,7 @@ check_fields(["password_hash" | More], false) ->
check_fields(More, true); check_fields(More, true);
check_fields(["salt" | More], HasPassHash) -> check_fields(["salt" | More], HasPassHash) ->
check_fields(More, HasPassHash); check_fields(More, HasPassHash);
check_fields(["superuser" | More], HasPassHash) -> check_fields(["is_superuser" | More], HasPassHash) ->
check_fields(More, HasPassHash); check_fields(More, HasPassHash);
check_fields([Field | _], _) -> check_fields([Field | _], _) ->
error({unsupported_field, Field}). error({unsupported_field, Field}).

View File

@ -1,3 +1,3 @@
user_id,password_hash,salt,superuser user_id,password_hash,salt,is_superuser
myuser3,b6c743545a7817ae8c8f624371d5f5f0373234bb0ff36b8ffbf19bce0e06ab75,de1024f462fb83910fd13151bd4bd235,true myuser3,b6c743545a7817ae8c8f624371d5f5f0373234bb0ff36b8ffbf19bce0e06ab75,de1024f462fb83910fd13151bd4bd235,true
myuser4,ee68c985a69208b6eda8c6c9b4c7c2d2b15ee2352cdd64a903171710a99182e8,ad773b5be9dd0613fe6c2f4d8c403139,false myuser4,ee68c985a69208b6eda8c6c9b4c7c2d2b15ee2352cdd64a903171710a99182e8,ad773b5be9dd0613fe6c2f4d8c403139,false

1 user_id password_hash salt superuser is_superuser
2 myuser3 b6c743545a7817ae8c8f624371d5f5f0373234bb0ff36b8ffbf19bce0e06ab75 de1024f462fb83910fd13151bd4bd235 true true
3 myuser4 ee68c985a69208b6eda8c6c9b4c7c2d2b15ee2352cdd64a903171710a99182e8 ad773b5be9dd0613fe6c2f4d8c403139 false false

View File

@ -3,12 +3,12 @@
"user_id":"myuser1", "user_id":"myuser1",
"password_hash":"c5e46903df45e5dc096dc74657610dbee8deaacae656df88a1788f1847390242", "password_hash":"c5e46903df45e5dc096dc74657610dbee8deaacae656df88a1788f1847390242",
"salt": "e378187547bf2d6f0545a3f441aa4d8a", "salt": "e378187547bf2d6f0545a3f441aa4d8a",
"superuser": true "is_superuser": true
}, },
{ {
"user_id":"myuser2", "user_id":"myuser2",
"password_hash":"f4d17f300b11e522fd33f497c11b126ef1ea5149c74d2220f9a16dc876d4567b", "password_hash":"f4d17f300b11e522fd33f497c11b126ef1ea5149c74d2220f9a16dc876d4567b",
"salt": "6d3f9bd5b54d94b98adbcfe10b6d181f", "salt": "6d3f9bd5b54d94b98adbcfe10b6d181f",
"superuser": false "is_superuser": false
} }
] ]

View File

@ -52,13 +52,13 @@ all() ->
% JWS = generate_jws('hmac-based', Payload, <<"abcdef">>), % JWS = generate_jws('hmac-based', Payload, <<"abcdef">>),
% ClientInfo = #{username => <<"myuser">>, % ClientInfo = #{username => <<"myuser">>,
% password => JWS}, % password => JWS},
% ?assertEqual({stop, {ok, #{superuser => false}}}, ?AUTH:authenticate(ClientInfo, ignored)), % ?assertEqual({stop, {ok, #{is_superuser => false}}}, ?AUTH:authenticate(ClientInfo, ignored)),
% Payload1 = #{<<"username">> => <<"myuser">>, <<"superuser">> => true}, % Payload1 = #{<<"username">> => <<"myuser">>, <<"is_superuser">> => true},
% JWS1 = generate_jws('hmac-based', Payload1, <<"abcdef">>), % JWS1 = generate_jws('hmac-based', Payload1, <<"abcdef">>),
% ClientInfo1 = #{username => <<"myuser">>, % ClientInfo1 = #{username => <<"myuser">>,
% password => JWS1}, % password => JWS1},
% ?assertEqual({stop, {ok, #{superuser => true}}}, ?AUTH:authenticate(ClientInfo1, ignored)), % ?assertEqual({stop, {ok, #{is_superuser => true}}}, ?AUTH:authenticate(ClientInfo1, ignored)),
% BadJWS = generate_jws('hmac-based', Payload, <<"bad_secret">>), % BadJWS = generate_jws('hmac-based', Payload, <<"bad_secret">>),
% ClientInfo2 = ClientInfo#{password => BadJWS}, % ClientInfo2 = ClientInfo#{password => BadJWS},
@ -68,11 +68,11 @@ all() ->
% 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, ID, Config2)), % ?assertMatch({ok, _}, ?AUTH:update_authenticator(?CHAIN, ID, Config2)),
% ?assertEqual({stop, {ok, #{superuser => false}}}, ?AUTH:authenticate(ClientInfo, ignored)), % ?assertEqual({stop, {ok, #{is_superuser => false}}}, ?AUTH:authenticate(ClientInfo, ignored)),
% Config3 = Config#{verify_claims => [{<<"username">>, <<"${mqtt-username}">>}]}, % Config3 = Config#{verify_claims => [{<<"username">>, <<"${mqtt-username}">>}]},
% ?assertMatch({ok, _}, ?AUTH:update_authenticator(?CHAIN, ID, Config3)), % ?assertMatch({ok, _}, ?AUTH:update_authenticator(?CHAIN, ID, Config3)),
% ?assertEqual({stop, {ok, #{superuser => false}}}, ?AUTH:authenticate(ClientInfo, ignored)), % ?assertEqual({stop, {ok, #{is_superuser => false}}}, ?AUTH:authenticate(ClientInfo, ignored)),
% ?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)),
% %% Expiration % %% Expiration
@ -86,14 +86,14 @@ all() ->
% , <<"exp">> => erlang:system_time(second) + 60}, % , <<"exp">> => erlang:system_time(second) + 60},
% JWS4 = generate_jws('hmac-based', Payload4, <<"abcdef">>), % JWS4 = generate_jws('hmac-based', Payload4, <<"abcdef">>),
% ClientInfo4 = ClientInfo#{password => JWS4}, % ClientInfo4 = ClientInfo#{password => JWS4},
% ?assertEqual({stop, {ok, #{superuser => false}}}, ?AUTH:authenticate(ClientInfo4, ignored)), % ?assertEqual({stop, {ok, #{is_superuser => false}}}, ?AUTH:authenticate(ClientInfo4, ignored)),
% %% Issued At % %% Issued At
% Payload5 = #{ <<"username">> => <<"myuser">> % Payload5 = #{ <<"username">> => <<"myuser">>
% , <<"iat">> => erlang:system_time(second) - 60}, % , <<"iat">> => erlang:system_time(second) - 60},
% JWS5 = generate_jws('hmac-based', Payload5, <<"abcdef">>), % JWS5 = generate_jws('hmac-based', Payload5, <<"abcdef">>),
% ClientInfo5 = ClientInfo#{password => JWS5}, % ClientInfo5 = ClientInfo#{password => JWS5},
% ?assertEqual({stop, {ok, #{superuser => false}}}, ?AUTH:authenticate(ClientInfo5, ignored)), % ?assertEqual({stop, {ok, #{is_superuser => false}}}, ?AUTH:authenticate(ClientInfo5, ignored)),
% Payload6 = #{ <<"username">> => <<"myuser">> % Payload6 = #{ <<"username">> => <<"myuser">>
% , <<"iat">> => erlang:system_time(second) + 60}, % , <<"iat">> => erlang:system_time(second) + 60},
@ -106,7 +106,7 @@ all() ->
% , <<"nbf">> => erlang:system_time(second) - 60}, % , <<"nbf">> => erlang:system_time(second) - 60},
% JWS7 = generate_jws('hmac-based', Payload7, <<"abcdef">>), % JWS7 = generate_jws('hmac-based', Payload7, <<"abcdef">>),
% ClientInfo7 = ClientInfo#{password => JWS7}, % ClientInfo7 = ClientInfo#{password => JWS7},
% ?assertEqual({stop, {ok, #{superuser => false}}}, ?AUTH:authenticate(ClientInfo7, ignored)), % ?assertEqual({stop, {ok, #{is_superuser => false}}}, ?AUTH:authenticate(ClientInfo7, ignored)),
% Payload8 = #{ <<"username">> => <<"myuser">> % Payload8 = #{ <<"username">> => <<"myuser">>
% , <<"nbf">> => erlang:system_time(second) + 60}, % , <<"nbf">> => erlang:system_time(second) + 60},
@ -134,7 +134,7 @@ all() ->
% JWS = generate_jws('public-key', Payload, PrivateKey), % JWS = generate_jws('public-key', Payload, PrivateKey),
% ClientInfo = #{username => <<"myuser">>, % ClientInfo = #{username => <<"myuser">>,
% password => JWS}, % password => JWS},
% ?assertEqual({stop, {ok, #{superuser => false}}}, ?AUTH:authenticate(ClientInfo, ignored)), % ?assertEqual({stop, {ok, #{is_superuser => false}}}, ?AUTH:authenticate(ClientInfo, ignored)),
% ?assertEqual({stop, {error, not_authorized}}, ?AUTH:authenticate(ClientInfo#{password => <<"badpassword">>}, ignored)), % ?assertEqual({stop, {error, not_authorized}}, ?AUTH:authenticate(ClientInfo#{password => <<"badpassword">>}, ignored)),
% ?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID)), % ?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID)),

View File

@ -56,9 +56,9 @@ all() ->
% ClientInfo = #{zone => external, % ClientInfo = #{zone => external,
% username => <<"myuser">>, % username => <<"myuser">>,
% password => <<"mypass">>}, % password => <<"mypass">>},
% ?assertEqual({stop, {ok, #{superuser => false}}}, ?AUTH:authenticate(ClientInfo, ignored)), % ?assertEqual({stop, {ok, #{is_superuser => false}}}, ?AUTH:authenticate(ClientInfo, ignored)),
% ?AUTH:enable(), % ?AUTH:enable(),
% ?assertEqual({ok, #{superuser => false}}, emqx_access_control:authenticate(ClientInfo)), % ?assertEqual({ok, #{is_superuser => false}}, emqx_access_control:authenticate(ClientInfo)),
% ClientInfo2 = ClientInfo#{username => <<"baduser">>}, % ClientInfo2 = ClientInfo#{username => <<"baduser">>},
% ?assertEqual({stop, {error, not_authorized}}, ?AUTH:authenticate(ClientInfo2, ignored)), % ?assertEqual({stop, {error, not_authorized}}, ?AUTH:authenticate(ClientInfo2, ignored)),
@ -71,10 +71,10 @@ all() ->
% UserInfo2 = UserInfo#{password => <<"mypass2">>}, % UserInfo2 = UserInfo#{password => <<"mypass2">>},
% ?assertMatch({ok, #{user_id := <<"myuser">>}}, ?AUTH:update_user(?CHAIN, ID, <<"myuser">>, UserInfo2)), % ?assertMatch({ok, #{user_id := <<"myuser">>}}, ?AUTH:update_user(?CHAIN, ID, <<"myuser">>, UserInfo2)),
% ClientInfo4 = ClientInfo#{password => <<"mypass2">>}, % ClientInfo4 = ClientInfo#{password => <<"mypass2">>},
% ?assertEqual({stop, {ok, #{superuser => false}}}, ?AUTH:authenticate(ClientInfo4, ignored)), % ?assertEqual({stop, {ok, #{is_superuser => false}}}, ?AUTH:authenticate(ClientInfo4, ignored)),
% ?assertMatch({ok, #{user_id := <<"myuser">>}}, ?AUTH:update_user(?CHAIN, ID, <<"myuser">>, #{superuser => true})), % ?assertMatch({ok, #{user_id := <<"myuser">>}}, ?AUTH:update_user(?CHAIN, ID, <<"myuser">>, #{is_superuser => true})),
% ?assertEqual({stop, {ok, #{superuser => true}}}, ?AUTH:authenticate(ClientInfo4, ignored)), % ?assertEqual({stop, {ok, #{is_superuser => true}}}, ?AUTH:authenticate(ClientInfo4, ignored)),
% ?assertEqual(ok, ?AUTH:delete_user(?CHAIN, ID, <<"myuser">>)), % ?assertEqual(ok, ?AUTH:delete_user(?CHAIN, ID, <<"myuser">>)),
% ?assertEqual({error, not_found}, ?AUTH:lookup_user(?CHAIN, ID, <<"myuser">>)), % ?assertEqual({error, not_found}, ?AUTH:lookup_user(?CHAIN, ID, <<"myuser">>)),
@ -107,15 +107,15 @@ all() ->
% ClientInfo1 = #{username => <<"myuser1">>, % ClientInfo1 = #{username => <<"myuser1">>,
% password => <<"mypassword1">>}, % password => <<"mypassword1">>},
% ?assertEqual({stop, {ok, #{superuser => true}}}, ?AUTH:authenticate(ClientInfo1, ignored)), % ?assertEqual({stop, {ok, #{is_superuser => true}}}, ?AUTH:authenticate(ClientInfo1, ignored)),
% ClientInfo2 = ClientInfo1#{username => <<"myuser2">>, % ClientInfo2 = ClientInfo1#{username => <<"myuser2">>,
% password => <<"mypassword2">>}, % password => <<"mypassword2">>},
% ?assertEqual({stop, {ok, #{superuser => false}}}, ?AUTH:authenticate(ClientInfo2, ignored)), % ?assertEqual({stop, {ok, #{is_superuser => false}}}, ?AUTH:authenticate(ClientInfo2, ignored)),
% ClientInfo3 = ClientInfo1#{username => <<"myuser3">>, % ClientInfo3 = ClientInfo1#{username => <<"myuser3">>,
% password => <<"mypassword3">>}, % password => <<"mypassword3">>},
% ?assertEqual({stop, {ok, #{superuser => true}}}, ?AUTH:authenticate(ClientInfo3, ignored)), % ?assertEqual({stop, {ok, #{is_superuser => true}}}, ?AUTH:authenticate(ClientInfo3, ignored)),
% ?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID)), % ?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID)),
% ok. % ok.
@ -152,12 +152,12 @@ all() ->
% ClientInfo1 = #{username => <<"myuser">>, % ClientInfo1 = #{username => <<"myuser">>,
% clientid => <<"myclient">>, % clientid => <<"myclient">>,
% password => <<"mypass1">>}, % password => <<"mypass1">>},
% ?assertEqual({stop, {ok, #{superuser => false}}}, ?AUTH:authenticate(ClientInfo1, ignored)), % ?assertEqual({stop, {ok, #{is_superuser => false}}}, ?AUTH:authenticate(ClientInfo1, ignored)),
% ?assertEqual(ok, ?AUTH:move_authenticator(?CHAIN, ID2, top)), % ?assertEqual(ok, ?AUTH:move_authenticator(?CHAIN, ID2, top)),
% ?assertEqual({stop, {error, bad_username_or_password}}, ?AUTH:authenticate(ClientInfo1, ignored)), % ?assertEqual({stop, {error, bad_username_or_password}}, ?AUTH:authenticate(ClientInfo1, ignored)),
% ClientInfo2 = ClientInfo1#{password => <<"mypass2">>}, % ClientInfo2 = ClientInfo1#{password => <<"mypass2">>},
% ?assertEqual({stop, {ok, #{superuser => false}}}, ?AUTH:authenticate(ClientInfo2, ignored)), % ?assertEqual({stop, {ok, #{is_superuser => false}}}, ?AUTH:authenticate(ClientInfo2, ignored)),
% ?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID1)), % ?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID1)),
% ?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID2)), % ?assertEqual(ok, ?AUTH:delete_authenticator(?CHAIN, ID2)),