Merge pull request #8286 from DDDHuang/fix_rm_dashboard_user

fix: dashboard users api, cannot delete self
This commit is contained in:
DDDHuang 2022-07-01 09:42:46 +08:00 committed by GitHub
commit c64f589ef5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 87 additions and 38 deletions

View File

@ -1,7 +1,13 @@
%% -*- mode: erlang -*- %% -*- mode: erlang -*-
%% Unless you know what you are doing, DO NOT edit manually!! %% Unless you know what you are doing, DO NOT edit manually!!
{VSN, {VSN,
[{"5.0.0",[{load_module,emqx_dashboard_api,brutal_purge,soft_purge,[]}]}, [{"5.0.0", [
{load_module,emqx_dashboard_api,brutal_purge,soft_purge,[]},
{load_module,emqx_dashboard_token,brutal_purge,soft_purge,[]}
]},
{<<".*">>,[]}], {<<".*">>,[]}],
[{"5.0.0",[{load_module,emqx_dashboard_api,brutal_purge,soft_purge,[]}]}, [{"5.0.0", [
{load_module,emqx_dashboard_api,brutal_purge,soft_purge,[]},
{load_module,emqx_dashboard_token,brutal_purge,soft_purge,[]}
]},
{<<".*">>,[]}]}. {<<".*">>,[]}]}.

View File

@ -272,22 +272,59 @@ user(put, #{bindings := #{username := Username}, body := Params}) ->
{error, Reason} -> {error, Reason} ->
{404, ?USER_NOT_FOUND, Reason} {404, ?USER_NOT_FOUND, Reason}
end; end;
user(delete, #{bindings := #{username := Username}}) -> user(delete, #{bindings := #{username := Username}, headers := Headers}) ->
case Username == emqx_dashboard_admin:default_username() of case Username == emqx_dashboard_admin:default_username() of
true -> true ->
?SLOG(info, #{msg => "Dashboard delete admin user failed", username => Username}), ?SLOG(info, #{msg => "Dashboard delete admin user failed", username => Username}),
Message = list_to_binary(io_lib:format("Cannot delete user ~p", [Username])), Message = list_to_binary(io_lib:format("Cannot delete user ~p", [Username])),
{400, ?NOT_ALLOWED, Message}; {400, ?NOT_ALLOWED, Message};
false -> false ->
case emqx_dashboard_admin:remove_user(Username) of case is_self_auth(Username, Headers) of
{error, Reason} -> true ->
{404, ?USER_NOT_FOUND, Reason}; {400, ?NOT_ALLOWED, <<"Cannot delete self">>};
{ok, _} -> false ->
?SLOG(info, #{msg => "Dashboard delete admin user", username => Username}), case emqx_dashboard_admin:remove_user(Username) of
{204} {error, Reason} ->
{404, ?USER_NOT_FOUND, Reason};
{ok, _} ->
?SLOG(info, #{
msg => "Dashboard delete admin user", username => Username
}),
{204}
end
end end
end. end.
is_self_auth(Username, #{<<"authorization">> := Token}) ->
is_self_auth(Username, Token);
is_self_auth(Username, #{<<"Authorization">> := Token}) ->
is_self_auth(Username, Token);
is_self_auth(Username, <<"basic ", Token/binary>>) ->
is_self_auth_basic(Username, Token);
is_self_auth(Username, <<"Basic ", Token/binary>>) ->
is_self_auth_basic(Username, Token);
is_self_auth(Username, <<"bearer ", Token/binary>>) ->
is_self_auth_token(Username, Token);
is_self_auth(Username, <<"Bearer ", Token/binary>>) ->
is_self_auth_token(Username, Token).
is_self_auth_basic(Username, Token) ->
UP = base64:decode(Token),
case binary:match(UP, Username) of
{0, N} ->
binary:part(UP, {N, 1}) == <<":">>;
_ ->
false
end.
is_self_auth_token(Username, Token) ->
case emqx_dashboard_token:owner(Token) of
{ok, Owner} ->
Owner == Username;
{error, _NotFound} ->
false
end.
change_pwd(put, #{bindings := #{username := Username}, body := Params}) -> change_pwd(put, #{bindings := #{username := Username}, body := Params}) ->
LogMeta = #{msg => "Dashboard change password", username => Username}, LogMeta = #{msg => "Dashboard change password", username => Username},
OldPwd = maps:get(<<"old_pwd">>, Params), OldPwd = maps:get(<<"old_pwd">>, Params),

View File

@ -22,6 +22,7 @@
sign/2, sign/2,
verify/1, verify/1,
lookup/1, lookup/1,
owner/1,
destroy/1, destroy/1,
destroy_by_username/1 destroy_by_username/1
]). ]).
@ -161,6 +162,14 @@ lookup_by_username(Username) ->
{atomic, List} = mria:ro_transaction(?DASHBOARD_SHARD, Fun), {atomic, List} = mria:ro_transaction(?DASHBOARD_SHARD, Fun),
List. List.
-spec owner(Token :: binary()) -> {ok, Username :: binary()} | {error, not_found}.
owner(Token) ->
Fun = fun() -> mnesia:read(?TAB, Token) end,
case mria:ro_transaction(?DASHBOARD_SHARD, Fun) of
{atomic, [#?ADMIN_JWT{username = Username}]} -> {ok, Username};
{atomic, []} -> {error, not_found}
end.
jwk(Username, Password, Salt) -> jwk(Username, Password, Salt) ->
Key = crypto:hash(md5, <<Salt/binary, Username/binary, Password/binary>>), Key = crypto:hash(md5, <<Salt/binary, Username/binary, Password/binary>>),
#{ #{

View File

@ -44,23 +44,17 @@
-define(APP_MANAGEMENT, emqx_management). -define(APP_MANAGEMENT, emqx_management).
-define(OVERVIEWS, [ -define(OVERVIEWS, [
'alarms/activated', "alarms",
'alarms/deactivated', "banned",
banned, "stats",
brokers, "metrics",
stats, "listeners",
metrics, "clients",
listeners, "subscriptions"
clients,
subscriptions,
routes,
plugins
]). ]).
all() -> all() ->
%% TODO: V5 API emqx_common_test_helpers:all(?MODULE).
%% emqx_common_test_helpers:all(?MODULE).
[t_cli, t_lookup_by_username_jwt, t_clean_expired_jwt, t_rest_api].
end_suite() -> end_suite() ->
end_suite([]). end_suite([]).
@ -98,37 +92,40 @@ t_overview(_) ->
mnesia:clear_table(?ADMIN), mnesia:clear_table(?ADMIN),
emqx_dashboard_admin:add_user(<<"admin">>, <<"public">>, <<"simple_description">>), emqx_dashboard_admin:add_user(<<"admin">>, <<"public">>, <<"simple_description">>),
[ [
?assert( {ok, _} = request_dashboard(get, api_path([Overview]), auth_header_())
request_dashboard(
get,
api_path(erlang:atom_to_list(Overview)),
auth_header_()
)
)
|| Overview <- ?OVERVIEWS || Overview <- ?OVERVIEWS
]. ].
t_admins_add_delete(_) -> t_admins_add_delete(_) ->
mnesia:clear_table(?ADMIN), mnesia:clear_table(?ADMIN),
Desc = <<"simple description">>, Desc = <<"simple description">>,
ok = emqx_dashboard_admin:add_user(<<"username">>, <<"password">>, Desc), {ok, _} = emqx_dashboard_admin:add_user(<<"username">>, <<"password">>, Desc),
ok = emqx_dashboard_admin:add_user(<<"username1">>, <<"password1">>, Desc), {ok, _} = emqx_dashboard_admin:add_user(<<"username1">>, <<"password1">>, Desc),
Admins = emqx_dashboard_admin:all_users(), Admins = emqx_dashboard_admin:all_users(),
?assertEqual(2, length(Admins)), ?assertEqual(2, length(Admins)),
ok = emqx_dashboard_admin:remove_user(<<"username1">>), {ok, _} = emqx_dashboard_admin:remove_user(<<"username1">>),
Users = emqx_dashboard_admin:all_users(), Users = emqx_dashboard_admin:all_users(),
?assertEqual(1, length(Users)), ?assertEqual(1, length(Users)),
ok = emqx_dashboard_admin:change_password( {ok, _} = emqx_dashboard_admin:change_password(
<<"username">>, <<"username">>,
<<"password">>, <<"password">>,
<<"pwd">> <<"pwd">>
), ),
timer:sleep(10), timer:sleep(10),
Header = auth_header_(<<"username">>, <<"pwd">>), {ok, _} = emqx_dashboard_admin:remove_user(<<"username">>).
?assert(request_dashboard(get, api_path("brokers"), Header)),
ok = emqx_dashboard_admin:remove_user(<<"username">>), t_admin_delete_self_failed(_) ->
?assertNotEqual(true, request_dashboard(get, api_path("brokers"), Header)). mnesia:clear_table(?ADMIN),
Desc = <<"simple description">>,
_ = emqx_dashboard_admin:add_user(<<"username1">>, <<"password">>, Desc),
Admins = emqx_dashboard_admin:all_users(),
?assertEqual(1, length(Admins)),
Header = auth_header_(<<"username1">>, <<"password">>),
{error, {_, 400, _}} = request_dashboard(delete, api_path(["users", "username1"]), Header),
Token = erlang:iolist_to_binary(["Basic ", base64:encode("username1:password")]),
Header2 = {"Authorization", Token},
{error, {_, 400, _}} = request_dashboard(delete, api_path(["users", "username1"]), Header2),
mnesia:clear_table(?ADMIN).
t_rest_api(_Config) -> t_rest_api(_Config) ->
mnesia:clear_table(?ADMIN), mnesia:clear_table(?ADMIN),