diff --git a/apps/emqx_dashboard/include/emqx_dashboard.hrl b/apps/emqx_dashboard/include/emqx_dashboard.hrl index e7fb4557b..712be13a0 100644 --- a/apps/emqx_dashboard/include/emqx_dashboard.hrl +++ b/apps/emqx_dashboard/include/emqx_dashboard.hrl @@ -13,8 +13,9 @@ %% See the License for the specific language governing permissions and %% limitations under the License. %%-------------------------------------------------------------------- +-define(ADMIN, emqx_admin). --record(emqx_admin, { +-record(?ADMIN, { username :: binary(), pwdhash :: binary(), tags :: list() | binary(), @@ -22,17 +23,16 @@ extra = [] :: term() %% not used so far, for future extension }). --define(ADMIN, emqx_admin). --record(emqx_admin_jwt, { +-define(ADMIN_JWT, emqx_admin_jwt). + +-record(?ADMIN_JWT, { token :: binary(), username :: binary(), exptime :: integer(), extra = [] :: term() %% not used so far, fur future extension }). --define(ADMIN_JWT, emqx_admin_jwt). - -define(EMPTY_KEY(Key), ((Key == undefined) orelse (Key == <<>>))). -define(DASHBOARD_SHARD, emqx_dashboard_shard). diff --git a/apps/emqx_dashboard/src/emqx_dashboard_admin.erl b/apps/emqx_dashboard/src/emqx_dashboard_admin.erl index 9622e6ed8..68ddac651 100644 --- a/apps/emqx_dashboard/src/emqx_dashboard_admin.erl +++ b/apps/emqx_dashboard/src/emqx_dashboard_admin.erl @@ -167,7 +167,7 @@ check(_, undefined) -> check(Username, Password) -> case lookup_user(Username) of [#?ADMIN{pwdhash = <>}] -> - case Hash =:= md5_hash(Salt, Password) of + case Hash =:= sha3_hash(Salt, Password) of true -> ok; false -> {error, <<"BAD_USERNAME_OR_PASSWORD">>} end; @@ -201,16 +201,11 @@ destroy_token_by_username(Username, Token) -> %%-------------------------------------------------------------------- hash(Password) -> - SaltBin = salt(), - <>. + SaltBin = emqx_dashboard_token:salt(), + <>. -md5_hash(SaltBin, Password) -> - erlang:md5(<>). - -salt() -> - _ = emqx_misc:rand_seed(), - Salt = rand:uniform(16#ffffffff), - <>. +sha3_hash(SaltBin, Password) -> + crypto:hash('sha3_256', <>). add_default_user() -> add_default_user(binenv(default_username), binenv(default_password)). diff --git a/apps/emqx_dashboard/src/emqx_dashboard_token.erl b/apps/emqx_dashboard/src/emqx_dashboard_token.erl index f8c023b46..ffd45241b 100644 --- a/apps/emqx_dashboard/src/emqx_dashboard_token.erl +++ b/apps/emqx_dashboard/src/emqx_dashboard_token.erl @@ -40,7 +40,7 @@ %% gen server part -behaviour(gen_server). --export([start_link/0]). +-export([start_link/0, salt/0]). -export([ init/1 , handle_call/3 @@ -75,6 +75,12 @@ destroy(Token) when is_binary(Token)-> destroy_by_username(Username) -> do_destroy_by_username(Username). +%% @doc create 4 bytes salt. +-spec(salt() -> binary()). +salt() -> + <> = crypto:strong_rand_bytes(2), + iolist_to_binary(io_lib:format("~4.16.0b", [X])). + mnesia(boot) -> ok = mria:create_table(?TAB, [ {type, set}, @@ -110,7 +116,9 @@ do_verify(Token)-> case ExpTime > erlang:system_time(millisecond) of true -> NewJWT = JWT#?ADMIN_JWT{exptime = jwt_expiration_time()}, - {atomic, Res} = mria:transaction(?DASHBOARD_SHARD, fun mnesia:write/1, [NewJWT]), + {atomic, Res} = mria:transaction(?DASHBOARD_SHARD, + fun mnesia:write/1, + [NewJWT]), Res; _ -> {error, token_timeout} @@ -145,7 +153,7 @@ lookup_by_username(Username) -> List. jwk(Username, Password, Salt) -> - Key = erlang:md5(<>), + Key = crypto:hash(md5, <>), #{ <<"kty">> => <<"oct">>, <<"k">> => jose_base64url:encode(Key) @@ -157,11 +165,6 @@ jwt_expiration_time() -> token_ttl() -> emqx_conf:get([emqx_dashboard, token_expired_time], ?EXPTIME). -salt() -> - _ = emqx_misc:rand_seed(), - Salt = rand:uniform(16#ffffffff), - <>. - format(Token, Username, ExpTime) -> #?ADMIN_JWT{ token = Token, diff --git a/apps/emqx_dashboard/test/emqx_dashboard_SUITE.erl b/apps/emqx_dashboard/test/emqx_dashboard_SUITE.erl index 42ffd7d45..f876f7383 100644 --- a/apps/emqx_dashboard/test/emqx_dashboard_SUITE.erl +++ b/apps/emqx_dashboard/test/emqx_dashboard_SUITE.erl @@ -37,7 +37,18 @@ -define(BASE_PATH, "api"). --define(OVERVIEWS, ['alarms/activated', 'alarms/deactivated', banned, brokers, stats, metrics, listeners, clients, subscriptions, routes, plugins]). +-define(OVERVIEWS, ['alarms/activated', + 'alarms/deactivated', + banned, + brokers, + stats, + metrics, + listeners, + clients, + subscriptions, + routes, + plugins + ]). all() -> %% TODO: V5 API @@ -45,7 +56,8 @@ all() -> [t_cli, t_lookup_by_username_jwt, t_clean_expired_jwt]. init_per_suite(Config) -> - emqx_common_test_helpers:start_apps([emqx_management, emqx_dashboard],fun set_special_configs/1), + emqx_common_test_helpers:start_apps([emqx_management, emqx_dashboard], + fun set_special_configs/1), Config. end_per_suite(_Config) -> @@ -53,7 +65,8 @@ end_per_suite(_Config) -> mria:stop(). set_special_configs(emqx_management) -> - emqx_config:put([emqx_management], #{listeners => [#{protocol => http, port => 8081}], + Listeners = [#{protocol => http, port => 8081}], + emqx_config:put([emqx_management], #{listeners => Listeners, applications =>[#{id => "admin", secret => "public"}]}), ok; set_special_configs(_) -> @@ -62,27 +75,33 @@ set_special_configs(_) -> t_overview(_) -> mnesia:clear_table(?ADMIN), emqx_dashboard_admin:add_user(<<"admin">>, <<"public">>, <<"tag">>), - [?assert(request_dashboard(get, api_path(erlang:atom_to_list(Overview)), auth_header_()))|| Overview <- ?OVERVIEWS]. + [?assert(request_dashboard(get, api_path(erlang:atom_to_list(Overview)), + auth_header_()))|| Overview <- ?OVERVIEWS]. t_admins_add_delete(_) -> mnesia:clear_table(?ADMIN), - ok = emqx_dashboard_admin:add_user(<<"username">>, <<"password">>, <<"tag">>), - ok = emqx_dashboard_admin:add_user(<<"username1">>, <<"password1">>, <<"tag1">>), + Tag = <<"tag">>, + ok = emqx_dashboard_admin:add_user(<<"username">>, <<"password">>, Tag), + ok = emqx_dashboard_admin:add_user(<<"username1">>, <<"password1">>, Tag), Admins = emqx_dashboard_admin:all_users(), ?assertEqual(2, length(Admins)), ok = emqx_dashboard_admin:remove_user(<<"username1">>), Users = emqx_dashboard_admin:all_users(), ?assertEqual(1, length(Users)), - ok = emqx_dashboard_admin:change_password(<<"username">>, <<"password">>, <<"pwd">>), + ok = emqx_dashboard_admin:change_password(<<"username">>, + <<"password">>, + <<"pwd">>), timer:sleep(10), - ?assert(request_dashboard(get, api_path("brokers"), auth_header_("username", "pwd"))), + Header = auth_header_("username", "pwd"), + ?assert(request_dashboard(get, api_path("brokers"), Header)), ok = emqx_dashboard_admin:remove_user(<<"username">>), - ?assertNotEqual(true, request_dashboard(get, api_path("brokers"), auth_header_("username", "pwd"))). + ?assertNotEqual(true, request_dashboard(get, api_path("brokers"), Header)). t_rest_api(_Config) -> mnesia:clear_table(?ADMIN), - emqx_dashboard_admin:add_user(<<"admin">>, <<"public">>, <<"administrator">>), + Tag = <<"administrator">>, + emqx_dashboard_admin:add_user(<<"admin">>, <<"public">>, Tag), {ok, Res0} = http_get("users"), ?assertEqual([#{<<"username">> => <<"admin">>, @@ -93,11 +112,15 @@ t_rest_api(_Config) -> end, [AssertSuccess(R) || R <- [ http_put("users/admin", #{<<"tags">> => <<"a_new_tag">>}) - , http_post("users", #{<<"username">> => <<"usera">>, <<"password">> => <<"passwd">>}) - , http_post("auth", #{<<"username">> => <<"usera">>, <<"password">> => <<"passwd">>}) + , http_post("users", #{<<"username">> => <<"usera">>, + <<"password">> => <<"passwd">>}) + , http_post("auth", #{<<"username">> => <<"usera">>, + <<"password">> => <<"passwd">>}) , http_delete("users/usera") - , http_put("users/admin/change_pwd", #{<<"old_pwd">> => <<"public">>, <<"new_pwd">> => <<"newpwd">>}) - , http_post("auth", #{<<"username">> => <<"admin">>, <<"password">> => <<"newpwd">>}) + , http_put("users/admin/change_pwd", #{<<"old_pwd">> => <<"public">>, + <<"new_pwd">> => <<"newpwd">>}) + , http_post("auth", #{<<"username">> => <<"admin">>, + <<"password">> => <<"newpwd">>}) ]], ok. @@ -106,11 +129,11 @@ t_cli(_Config) -> emqx_dashboard_cli:admins(["add", "username", "password"]), [#?ADMIN{ username = <<"username">>, pwdhash = <>}] = emqx_dashboard_admin:lookup_user(<<"username">>), - ?assertEqual(Hash, erlang:md5(<>/binary>>)), + ?assertEqual(Hash, crypto:hash(sha3_256, <>/binary>>)), emqx_dashboard_cli:admins(["passwd", "username", "newpassword"]), [#?ADMIN{username = <<"username">>, pwdhash = <>}] = emqx_dashboard_admin:lookup_user(<<"username">>), - ?assertEqual(Hash1, erlang:md5(<>/binary>>)), + ?assertEqual(Hash1, crypto:hash(sha3_256, <>/binary>>)), emqx_dashboard_cli:admins(["del", "username"]), [] = emqx_dashboard_admin:lookup_user(<<"username">>), emqx_dashboard_cli:admins(["add", "admin1", "pass1"]),