feat(dashboard): dashboard admin password persistence

This commit is contained in:
Georgy Sychev 2022-03-23 19:14:44 +04:00
parent a9255032cd
commit b3de664c64
3 changed files with 37 additions and 8 deletions

View File

@ -158,7 +158,8 @@ update_pwd(Username, Fun) ->
lookup_user(Username) when is_binary(Username) -> lookup_user(Username) when is_binary(Username) ->
case binenv(default_user_username) of case binenv(default_user_username) of
Username -> Username ->
[#mqtt_admin{username=Username, password=hashed_default_passwd()}]; Password = hashed_default_passwd(),
[#mqtt_admin{username=Username, password=Password, tags= <<"administrator">>}];
_ -> _ ->
mnesia:dirty_read(mqtt_admin, Username) mnesia:dirty_read(mqtt_admin, Username)
end. end.
@ -193,6 +194,7 @@ check(Username, Password) ->
init([]) -> init([]) ->
%% Add default admin user %% Add default admin user
{ok, _} = mnesia:subscribe({table, mqtt_admin, simple}), {ok, _} = mnesia:subscribe({table, mqtt_admin, simple}),
add_default_user_hashed(binenv(default_user_username), hashed_default_passwd()),
{ok, state}. {ok, state}.
handle_call(_Req, _From, State) -> handle_call(_Req, _From, State) ->
@ -240,13 +242,24 @@ salt() ->
binenv(Key) -> binenv(Key) ->
iolist_to_binary(application:get_env(emqx_dashboard, Key, <<>>)). iolist_to_binary(application:get_env(emqx_dashboard, Key, <<>>)).
add_default_user_hashed(Username, HashedPassword) ->
case mnesia:dirty_read(mqtt_admin, Username) of
[] ->
Admin = #mqtt_admin{username=Username, password=HashedPassword, tags= <<"administrator">>},
return(mnesia:transaction(fun add_user_/1, [Admin]));
_ -> ok
end.
hashed_default_passwd() -> hashed_default_passwd() ->
case binenv(default_user_passwd_hashed) of case binenv(default_user_passwd_hashed) of
Empty0 when ?EMPTY_KEY(Empty0) -> Empty0 when ?EMPTY_KEY(Empty0) ->
case binenv(default_user_passwd) of case binenv(default_user_passwd) of
Empty when ?EMPTY_KEY(Empty) -> Empty when ?EMPTY_KEY(Empty) ->
undefined; undefined;
Password -> hash(Password) Password ->
Hashed = hash(Password),
application:set_env(emqx_dashboard, default_user_passwd_hashed, Hashed),
Hashed
end; end;
HashedPassword -> HashedPassword HashedPassword -> HashedPassword
end. end.

View File

@ -52,8 +52,8 @@ all() ->
groups() -> groups() ->
[ [
{overview, [sequence], [t_overview]}, {overview, [sequence], [t_overview]},
{admins, [sequence], [t_default_password_persists_after_leaving_cluster]}, {admins, [sequence], [t_admins_add_delete, t_admins_persist_default_password, t_default_password_persists_after_leaving_cluster]},
{rest, [sequence], [t_rest_api, t_auth_exhaustive_attack]}, {rest, [sequence], [t_rest_api]},
{cli, [sequence], [t_cli]} {cli, [sequence], [t_cli]}
]. ].
@ -90,10 +90,14 @@ t_admins_add_delete(_) ->
t_admins_persist_default_password(_) -> t_admins_persist_default_password(_) ->
emqx_dashboard_admin:change_password(<<"admin">>, <<"new_password">>), emqx_dashboard_admin:change_password(<<"admin">>, <<"new_password">>),
[#mqtt_admin{password=Password}] = emqx_dashboard_admin:lookup_user(<<"admin">>), ct:sleep(100),
[#mqtt_admin{password=Password, tags= <<"administrator">>}] = emqx_dashboard_admin:lookup_user(<<"admin">>),
%% To ensure that state persists even if the process dies %% To ensure that state persists even if the process dies
exit(whereis(emqx_dashboard_admin), kill), application:stop(emqx_dashboard),
application:start(emqx_dashboard),
ct:sleep(100),
%% It get's restarted by the app automatically %% It get's restarted by the app automatically
[#mqtt_admin{password=PasswordAfterRestart}] = emqx_dashboard_admin:lookup_user(<<"admin">>), [#mqtt_admin{password=PasswordAfterRestart}] = emqx_dashboard_admin:lookup_user(<<"admin">>),
@ -147,6 +151,14 @@ t_default_password_persists_after_leaving_cluster(_) ->
debug(2, Slave), debug(2, Slave),
rpc:call(Slave, application, stop, [emqx_dashboard]),
debug(3, Slave),
rpc:call(Slave, application, start, [emqx_dashboard]),
debug(4, Slave),
?assertEqual( ?assertEqual(
ok, ok,
rpc:call(Slave, emqx_dashboard_admin, check, [<<"admin">>, <<"new_password">>])), rpc:call(Slave, emqx_dashboard_admin, check, [<<"admin">>, <<"new_password">>])),
@ -162,6 +174,7 @@ t_default_password_persists_after_leaving_cluster(_) ->
t_rest_api(_Config) -> t_rest_api(_Config) ->
{ok, Res0} = http_get("users"), {ok, Res0} = http_get("users"),
Users = get_http_data(Res0), Users = get_http_data(Res0),
ct:pal("~p", [emqx_dashboard_admin:all_users()]),
?assert(lists:member(#{<<"username">> => <<"admin">>, <<"tags">> => <<"administrator">>}, ?assert(lists:member(#{<<"username">> => <<"admin">>, <<"tags">> => <<"administrator">>},
Users)), Users)),
@ -277,7 +290,6 @@ setup_node(Node, Apps) ->
fun(emqx) -> fun(emqx) ->
application:set_env(emqx, listeners, []), application:set_env(emqx, listeners, []),
application:set_env(gen_rpc, port_discovery, manual), application:set_env(gen_rpc, port_discovery, manual),
mnesia:info(),
ok; ok;
(emqx_management) -> (emqx_management) ->
application:set_env(emqx_management, listeners, []), application:set_env(emqx_management, listeners, []),

View File

@ -234,7 +234,7 @@ shutdown(Reason) ->
). ).
reboot() -> reboot() ->
case application_controller:is_running(emqx_dashboard) of case is_application_running(emqx_dashboard) of
true -> true ->
application:stop(emqx_dashboard), %% dashboard must be started after mnesia application:stop(emqx_dashboard), %% dashboard must be started after mnesia
lists:foreach(fun application:start/1 , default_started_applications()), lists:foreach(fun application:start/1 , default_started_applications()),
@ -244,6 +244,10 @@ reboot() ->
lists:foreach(fun application:start/1 , default_started_applications()) lists:foreach(fun application:start/1 , default_started_applications())
end. end.
is_application_running(App) ->
StartedApps = proplists:get_value(started, application:info()),
proplists:is_defined(App, StartedApps).
-ifdef(EMQX_ENTERPRISE). -ifdef(EMQX_ENTERPRISE).
default_started_applications() -> default_started_applications() ->
[gproc, esockd, ranch, cowboy, ekka, emqx]. [gproc, esockd, ranch, cowboy, ekka, emqx].