fix(authn): fix superuser in mongodb authn
This commit is contained in:
parent
b5ded1ece0
commit
29cad91a47
|
@ -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.
|
||||||
|
|
|
@ -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} ->
|
||||||
|
|
|
@ -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}.
|
||||||
|
|
|
@ -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}.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue