fix(authn): fix superuser in mongodb authn

This commit is contained in:
zhouzb 2021-09-08 10:58:00 +08:00
parent b5ded1ece0
commit 29cad91a47
5 changed files with 52 additions and 41 deletions

View File

@ -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, #{is_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,6 +73,7 @@
}, },
password_hash_field => <<"password_hash">>, password_hash_field => <<"password_hash">>,
salt_field => <<"salt">>, salt_field => <<"salt">>,
is_superuser_field => <<"is_superuser">>,
password_hash_algorithm => <<"sha256">>, password_hash_algorithm => <<"sha256">>,
salt_position => <<"prefix">> salt_position => <<"prefix">>
}). }).
@ -1398,6 +1399,10 @@ definitions() ->
type => string, type => string,
example => <<"salt">> example => <<"salt">>
}, },
is_superuser_field => #{
type => string,
example => <<"is_superuser">>
},
password_hash_algorithm => #{ password_hash_algorithm => #{
type => string, type => string,
enum => [<<"plain">>, <<"md5">>, <<"sha">>, <<"sha256">>, <<"sha512">>, <<"bcrypt">>], enum => [<<"plain">>, <<"md5">>, <<"sha">>, <<"sha256">>, <<"sha512">>, <<"bcrypt">>],
@ -1882,10 +1887,10 @@ move_authenitcator(ConfKeyPath, ChainName0, AuthenticatorID, Position) ->
add_user(ChainName0, AuthenticatorID, #{<<"user_id">> := UserID, <<"password">> := Password} = UserInfo) -> add_user(ChainName0, AuthenticatorID, #{<<"user_id">> := UserID, <<"password">> := Password} = UserInfo) ->
ChainName = to_atom(ChainName0), ChainName = to_atom(ChainName0),
Superuser = maps:get(<<"is_superuser">>, UserInfo, false), IsSuperuser = maps:get(<<"is_superuser">>, UserInfo, false),
case ?AUTHN:add_user(ChainName, AuthenticatorID, #{ user_id => UserID case ?AUTHN:add_user(ChainName, AuthenticatorID, #{ user_id => UserID
, password => Password , password => Password
, is_superuser => Superuser}) of , is_superuser => IsSuperuser}) of
{ok, User} -> {ok, User} ->
{201, User}; {201, User};
{error, Reason} -> {error, Reason} ->

View File

@ -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(is_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, is_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{is_superuser = Superuser} = UserInfo] -> [#user_info{is_superuser = IsSuperuser} = UserInfo] ->
UserInfo1 = UserInfo#user_info{is_superuser = maps:get(is_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, #{is_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, #{is_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,
is_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,
is_superuser = Superuser}] -> is_superuser = IsSuperuser}] ->
{ok, #{stored_key => StoredKey, {ok, #{stored_key => StoredKey,
server_key => ServerKey, server_key => ServerKey,
salt => Salt, salt => Salt,
is_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}, is_superuser = Superuser}) -> serialize_user_info(#user_info{user_id = {_, UserID}, is_superuser = IsSuperuser}) ->
#{user_id => UserID, is_superuser => Superuser}. #{user_id => UserID, is_superuser => IsSuperuser}.

View File

@ -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, is_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, #{is_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(is_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, is_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
, is_superuser = Superuser}] -> , is_superuser = IsSuperuser}] ->
NSuperuser = maps:get(is_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};
@ -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(<<"is_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(is_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}
@ -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,
is_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}, is_superuser = Superuser}) -> serialize_user_info(#user_info{user_id = {_, UserID}, is_superuser = IsSuperuser}) ->
#{user_id => UserID, is_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),
@ -230,8 +236,8 @@ check_password(Password,
end end
end. end.
is_superuser(Doc, #{superuser_field := SuperuserField}) -> is_superuser(Doc, #{is_superuser_field := IsSuperuserField}) ->
maps:get(SuperuserField, Doc, false); maps:get(IsSuperuserField, Doc, false);
is_superuser(_, _) -> is_superuser(_, _) ->
false. false.