Merge tag 'v4.4.14' into sync-v4414-into-v44

This commit is contained in:
Thales Macedo Garitezi 2023-01-06 13:34:34 -03:00
commit 53ab67285a
11 changed files with 206 additions and 84 deletions

View File

@ -1,6 +1,6 @@
{application, emqx_management, {application, emqx_management,
[{description, "EMQ X Management API and CLI"}, [{description, "EMQ X Management API and CLI"},
{vsn, "4.4.11"}, % strict semver, bump manually! {vsn, "4.4.12"}, % strict semver, bump manually!
{modules, []}, {modules, []},
{registered, [emqx_management_sup]}, {registered, [emqx_management_sup]},
{applications, [kernel,stdlib,emqx_plugin_libs,minirest]}, {applications, [kernel,stdlib,emqx_plugin_libs,minirest]},

View File

@ -36,7 +36,6 @@
, del_app/1 , del_app/1
, list_apps/0 , list_apps/0
, init_bootstrap_apps/0 , init_bootstrap_apps/0
, need_bootstrap/0
, clear_bootstrap_apps/0 , clear_bootstrap_apps/0
]). ]).
@ -83,21 +82,8 @@ add_default_app() ->
end. end.
init_bootstrap_apps() -> init_bootstrap_apps() ->
case need_bootstrap() of
true ->
Bootstrap = application:get_env(emqx_management, bootstrap_apps_file, undefined), Bootstrap = application:get_env(emqx_management, bootstrap_apps_file, undefined),
init_bootstrap_apps(Bootstrap); init_bootstrap_apps(Bootstrap).
false ->
ok
end.
need_bootstrap() ->
{atomic, Res} = mnesia:transaction(
fun() ->
Spec = [{#mqtt_app{id = '$1', desc = ?BOOTSTRAP_TAG, _ = '_'}, [], ['$1']}],
mnesia:select(mqtt_app, Spec, 1, read) =:= '$end_of_table'
end),
Res.
clear_bootstrap_apps() -> clear_bootstrap_apps() ->
{atomic, ok} = {atomic, ok} =
@ -113,13 +99,7 @@ init_bootstrap_apps(File) ->
case file:open(File, [read, binary]) of case file:open(File, [read, binary]) of
{ok, Dev} -> {ok, Dev} ->
{ok, MP} = re:compile(<<"(\.+):(\.+$)">>, [ungreedy]), {ok, MP} = re:compile(<<"(\.+):(\.+$)">>, [ungreedy]),
case init_bootstrap_apps(File, Dev, MP) of init_bootstrap_apps(File, Dev, MP);
ok -> ok;
Error ->
%% if failed add bootstrap users, we should clear all bootstrap apps
clear_bootstrap_apps(),
Error
end;
{error, Reason} = Error -> {error, Reason} = Error ->
?LOG(error, ?LOG(error,
"failed to open the mgmt bootstrap apps file(~s) for ~p", "failed to open the mgmt bootstrap apps file(~s) for ~p",
@ -145,8 +125,8 @@ add_bootstrap_app(File, Dev, MP, Line) ->
case re:run(Bin, MP, [global, {capture, all_but_first, binary}]) of case re:run(Bin, MP, [global, {capture, all_but_first, binary}]) of
{match, [[AppId, AppSecret]]} -> {match, [[AppId, AppSecret]]} ->
Name = <<"bootstraped">>, Name = <<"bootstraped">>,
case add_app(AppId, Name, AppSecret, ?BOOTSTRAP_TAG, true, undefined) of case force_add_app(AppId, Name, AppSecret, ?BOOTSTRAP_TAG, true, undefined) of
{ok, _} -> ok ->
add_bootstrap_app(File, Dev, MP, Line + 1); add_bootstrap_app(File, Dev, MP, Line + 1);
{error, Reason} -> {error, Reason} ->
throw(#{file => File, line => Line, content => Bin, reason => Reason}) throw(#{file => File, line => Line, content => Bin, reason => Reason})

View File

@ -66,6 +66,70 @@ t_importee430(_) ->
{ok, _} = emqx_mgmt_data_backup:export(), {ok, _} = emqx_mgmt_data_backup:export(),
remove_all_users_and_acl(). remove_all_users_and_acl().
t_import_test(_) ->
SimpleAdmin = <<"simpleAdmin">>,
SimplePassword = <<"simplepassword">>,
SimplePasswordHash = emqx_dashboard_admin:hash(SimplePassword),
Admins = [<<"Admin1">>, <<"Admin2">>, <<"Admin3">>, <<"Admin4">>, <<"Admin5">>],
Passwords = [<<"password1">>, <<"PAssword2">>,<<"3&*)dkdKlkd">>,<<"&*qwl4kd>">>,<<"PASSWORD5D">>],
%% add some users
add_admins(Admins, Passwords),
%% Allow force import simple password.
ok = emqx_dashboard_admin:force_add_user(SimpleAdmin, SimplePasswordHash, <<"test">>),
ct:pal("1111~p~n", [ets:info(mqtt_admin)]),
ct:pal("~p~n", [ets:tab2list(mqtt_admin)]),
check_admins_ok(Admins, Passwords),
{ok, #{filename := FileName}} = emqx_mgmt_data_backup:export(),
remove_admins(Admins),
ok = emqx_dashboard_admin:remove_user(SimpleAdmin),
ct:pal("0000~n"),
check_admins_failed(Admins, Passwords),
{error, _} = emqx_dashboard_admin:check(SimpleAdmin, SimplePassword),
ok = emqx_mgmt_data_backup:import(FileName, <<"{}">>),
ct:pal("2222~n"),
check_admins_ok(Admins, Passwords),
ok = emqx_dashboard_admin:check(SimpleAdmin, SimplePassword),
remove_admins(Admins),
ok = emqx_dashboard_admin:remove_user(SimpleAdmin),
remove_all_users_and_acl(),
ok.
add_admins(Admins, Passwords) ->
lists:foreach(
fun({Admin, Password}) ->
ok = emqx_dashboard_admin:add_user(Admin, Password, <<"test">>)
end, lists:zip(Admins, Passwords)),
ok.
check_admins_ok(Admins, Passwords) ->
lists:foreach(
fun({Admin, Password}) ->
?assertMatch(ok, emqx_dashboard_admin:check(Admin, Password), {Admin, Password})
end, lists:zip(Admins, Passwords)),
ok.
check_admins_failed(Admins, Passwords) ->
lists:foreach(
fun({Admin, Password}) ->
?assertMatch({error, _}, emqx_dashboard_admin:check(Admin, Password), {Admin, Password})
end, lists:zip(Admins, Passwords)),
ok.
remove_admins(Admins) ->
lists:foreach(
fun(Admin) ->
ok = emqx_dashboard_admin:remove_user(Admin)
end, Admins),
ok.
remove_all_users_and_acl() -> remove_all_users_and_acl() ->
mnesia:delete_table(emqx_user), mnesia:delete_table(emqx_user),
mnesia:delete_table(emqx_acl). mnesia:delete_table(emqx_acl).

View File

@ -68,10 +68,10 @@ t_load_ok(_) ->
ok = file:write_file(File, Bin1), ok = file:write_file(File, Bin1),
application:set_env(emqx_management, bootstrap_apps_file, File), application:set_env(emqx_management, bootstrap_apps_file, File),
{ok, _} = application:ensure_all_started(emqx_management), {ok, _} = application:ensure_all_started(emqx_management),
?assert(emqx_mgmt_auth:is_authorized(<<"test-1">>, <<"secret-1">>)), ?assertNot(emqx_mgmt_auth:is_authorized(<<"test-1">>, <<"secret-1">>)),
?assert(emqx_mgmt_auth:is_authorized(<<"test-2">>, <<"secret-2">>)), ?assertNot(emqx_mgmt_auth:is_authorized(<<"test-2">>, <<"secret-2">>)),
?assertNot(emqx_mgmt_auth:is_authorized(<<"test-1">>, <<"new-secret-1">>)), ?assert(emqx_mgmt_auth:is_authorized(<<"test-1">>, <<"new-secret-1">>)),
?assertNot(emqx_mgmt_auth:is_authorized(<<"test-2">>, <<"new-secret-2">>)), ?assert(emqx_mgmt_auth:is_authorized(<<"test-2">>, <<"new-secret-2">>)),
application:stop(emqx_management). application:stop(emqx_management).
t_bootstrap_user_file_not_found(_) -> t_bootstrap_user_file_not_found(_) ->
@ -84,6 +84,8 @@ t_load_invalid_username_failed(_) ->
File = "./bootstrap_apps.txt", File = "./bootstrap_apps.txt",
ok = file:write_file(File, Bin), ok = file:write_file(File, Bin),
check_load_failed(File), check_load_failed(File),
?assert(emqx_mgmt_auth:is_authorized(<<"test-1">>, <<"password-1">>)),
?assertNot(emqx_mgmt_auth:is_authorized(<<"test&2">>, <<"password-2">>)),
ok. ok.
t_load_invalid_format_failed(_) -> t_load_invalid_format_failed(_) ->
@ -91,6 +93,8 @@ t_load_invalid_format_failed(_) ->
File = "./bootstrap_apps.txt", File = "./bootstrap_apps.txt",
ok = file:write_file(File, Bin), ok = file:write_file(File, Bin),
check_load_failed(File), check_load_failed(File),
?assert(emqx_mgmt_auth:is_authorized(<<"test-1">>, <<"password-1">>)),
?assertNot(emqx_mgmt_auth:is_authorized(<<"test-2">>, <<"password-2">>)),
ok. ok.
check_load_failed(File) -> check_load_failed(File) ->
@ -98,5 +102,4 @@ check_load_failed(File) ->
application:stop(emqx_management), application:stop(emqx_management),
application:set_env(emqx_management, bootstrap_apps_file, File), application:set_env(emqx_management, bootstrap_apps_file, File),
?assertMatch({error, _}, application:ensure_all_started(emqx_management)), ?assertMatch({error, _}, application:ensure_all_started(emqx_management)),
?assertNot(lists:member(emqx_management, application:which_applications())), ?assertNot(lists:member(emqx_management, application:which_applications())).
?assert(emqx_mgmt_auth:need_bootstrap()).

View File

@ -1,7 +1,11 @@
# v4.4.14 # v4.4.14
## Enhancements ## Enhancements
- Add a password complexity requirement when adding or modifying Dashboard users via the API. Now passwords must contain at least 2 of alphabetic, numeric and special characters, and must be 8 to 64 characters long [#9696](https://github.com/emqx/emqx/pull/9696).
## Bug fixes
## Bug Fixes - Fix dashboard password validator is too simple. Now dashboard password must contain at least two different kind of characters from groups of letters, numbers and special characters. [#9696](https://github.com/emqx/emqx/pull/9696).
- Fix the problem that adding or importing Dashboard users via the API fails to add complex passwords due to incorrect checksum of the passwords [#9692](https://github.com/emqx/emqx/pull/9692).
- Fix load bootstrap_app_file's apps is not sync when reboot. [#9692](https://github.com/emqx/emqx/pull/9692).

View File

@ -2,5 +2,10 @@
## 增强 ## 增强
- 通过 API 添加、修改 Dashboard 用户时,增加对密码复杂度的要求。现在密码必须包含字母、数字以及特殊字符中的至少 2 种,并且长度范围必须是 8~64 个字符 [#9696](https://github.com/emqx/emqx/pull/9696)。
## 修复 ## 修复
- 修复通过 API 添加或者导入 Dashboard 用户时,对密码进行了错误的校验,导致复杂密码添加失败的问题 [#9696](https://github.com/emqx/emqx/pull/9696)。
- 修复 boostrap_apps_file 文件更新后未同步至数据库,导致变更的 apps 无法生效 [#9692](https://github.com/emqx/emqx/pull/9692)。

View File

@ -29,7 +29,7 @@
-ifndef(EMQX_ENTERPRISE). -ifndef(EMQX_ENTERPRISE).
-define(EMQX_RELEASE, {opensource, "4.4.14-alpha.1"}). -define(EMQX_RELEASE, {opensource, "4.4.14"}).
-else. -else.

View File

@ -23,6 +23,7 @@
-include("emqx_dashboard.hrl"). -include("emqx_dashboard.hrl").
-include_lib("emqx/include/logger.hrl"). -include_lib("emqx/include/logger.hrl").
-define(DEFAULT_PASSWORD, <<"public">>). -define(DEFAULT_PASSWORD, <<"public">>).
-define(INVALID_PASSWORD_MSG, <<"The password must contain at least two different kind of characters from groups of letters, numbers, and special characters. For example, if password is composed from letters, it must contain at least one number or a special character.">>).
-boot_mnesia({mnesia, [boot]}). -boot_mnesia({mnesia, [boot]}).
-copy_mnesia({mnesia, [copy]}). -copy_mnesia({mnesia, [copy]}).
@ -41,8 +42,10 @@
, lookup_user/1 , lookup_user/1
, change_password/2 , change_password/2
, change_password/3 , change_password/3
, force_change_password/2
, all_users/0 , all_users/0
, check/2 , check/2
, hash/1
]). ]).
%% gen_server Function Exports %% gen_server Function Exports
@ -79,7 +82,7 @@ start_link() ->
-spec(add_user(binary(), binary(), binary()) -> ok | {error, any()}). -spec(add_user(binary(), binary(), binary()) -> ok | {error, any()}).
add_user(Username, Password, Tags) when is_binary(Username), is_binary(Password) -> add_user(Username, Password, Tags) when is_binary(Username), is_binary(Password) ->
case {emqx_misc:is_sane_id(Username), emqx_misc:is_sane_id(Password, 2, 32)} of case {emqx_misc:is_sane_id(Username), is_valid_pwd(Password)} of
{ok, ok} -> {ok, ok} ->
Admin = #mqtt_admin{username = Username, password = hash(Password), tags = Tags}, Admin = #mqtt_admin{username = Username, password = hash(Password), tags = Tags},
return(mnesia:transaction(fun add_user_/1, [Admin])); return(mnesia:transaction(fun add_user_/1, [Admin]));
@ -88,17 +91,12 @@ add_user(Username, Password, Tags) when is_binary(Username), is_binary(Password)
end. end.
force_add_user(Username, Password, Tags) -> force_add_user(Username, Password, Tags) ->
case {emqx_misc:is_sane_id(Username), emqx_misc:is_sane_id(Password, 2, 32)} of
{ok, ok} ->
AddFun = fun() -> AddFun = fun() ->
mnesia:write(#mqtt_admin{username = Username, password = Password, tags = Tags}) mnesia:write(#mqtt_admin{username = Username, password = Password, tags = Tags})
end, end,
case mnesia:transaction(AddFun) of case mnesia:transaction(AddFun) of
{atomic, ok} -> ok; {atomic, ok} -> ok;
{aborted, Reason} -> {error, Reason} {aborted, Reason} -> {error, Reason}
end;
{{error, Reason}, _} -> {error, Reason};
{_, {error, Reason}} -> {error, Reason}
end. end.
%% @private %% @private
@ -138,6 +136,12 @@ change_password(Username, OldPasswd, NewPasswd) when is_binary(Username) ->
end. end.
change_password(Username, Password) when is_binary(Username), is_binary(Password) -> change_password(Username, Password) when is_binary(Username), is_binary(Password) ->
case is_valid_pwd(Password) of
ok -> change_password_hash(Username, hash(Password));
{error, Error} -> {error, Error}
end.
force_change_password(Username, Password) when is_binary(Username), is_binary(Password) ->
change_password_hash(Username, hash(Password)). change_password_hash(Username, hash(Password)).
change_password_hash(Username, PasswordHash) -> change_password_hash(Username, PasswordHash) ->
@ -145,6 +149,37 @@ change_password_hash(Username, PasswordHash) ->
User#mqtt_admin{password = PasswordHash} User#mqtt_admin{password = PasswordHash}
end). end).
-define(LOW_LETTER_CHARS, "abcdefghijklmnopqrstuvwxyz").
-define(UPPER_LETTER_CHARS, "ABCDEFGHIJKLMNOPQRSTUVWXYZ").
-define(LETTER, ?LOW_LETTER_CHARS ++ ?UPPER_LETTER_CHARS).
-define(NUMBER, "0123456789").
-define(SPECIAL_CHARS, "!@#$%^&*()_+-=[]{}\"|;':,./<>?`~ ").
is_valid_pwd(Password) when is_binary(Password) ->
is_valid_pwd(binary_to_list(Password));
is_valid_pwd(Password) ->
Len = erlang:length(Password),
case Len >= 8 andalso Len =< 64 of
true ->
Letter = contain(Password, ?LETTER),
Number = contain(Password, ?NUMBER),
Special = contain(Password, ?SPECIAL_CHARS),
OK = lists:filter(fun(C) -> C end, [Letter, Number, Special]),
case length(OK) >= 2 of
true ->
%% regex-any-ascii-character
case re:run(Password, "[^\\x00-\\x7F]+", [unicode, {capture, none}]) of
match -> {error, <<"only ascii characters are allowed in the password">>};
nomatch -> ok
end;
_ -> {error, ?INVALID_PASSWORD_MSG}
end;
false ->
{error, <<"The password length: 8-64">>}
end.
contain(Xs, Spec) -> lists:any(fun(X) -> lists:member(X, Spec) end, Xs).
update_pwd(Username, Fun) -> update_pwd(Username, Fun) ->
Trans = fun() -> Trans = fun() ->
User = User =
@ -317,3 +352,36 @@ maybe_warn_default_pwd() ->
false -> false ->
ok ok
end. end.
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
password_test() ->
?assertEqual({error, <<"The password length: 8-64">>}, is_valid_pwd(<<"123">>)),
MaxPassword = iolist_to_binary([lists:duplicate(63, "x"), "1"]),
?assertEqual(ok, is_valid_pwd(MaxPassword)),
TooLongPassword = lists:duplicate(65, "y"),
?assertEqual({error, <<"The password length: 8-64">>}, is_valid_pwd(TooLongPassword)),
?assertEqual({error, ?INVALID_PASSWORD_MSG}, is_valid_pwd(<<"12345678">>)),
?assertEqual({error, ?INVALID_PASSWORD_MSG}, is_valid_pwd(?LETTER)),
?assertEqual({error, ?INVALID_PASSWORD_MSG}, is_valid_pwd(?NUMBER)),
?assertEqual({error, ?INVALID_PASSWORD_MSG}, is_valid_pwd(?SPECIAL_CHARS)),
?assertEqual({error, ?INVALID_PASSWORD_MSG}, is_valid_pwd(<<"映映映映无天在请"/utf8>>)),
?assertEqual({error, <<"only ascii characters are allowed in the password">>}, is_valid_pwd(<<"test_for_non_ascii1中"/utf8>>)),
?assertEqual({error, <<"only ascii characters are allowed in the password">>}, is_valid_pwd(<<"云☁test_for_unicode"/utf8>>)),
?assertEqual(ok, is_valid_pwd(?LOW_LETTER_CHARS ++ ?NUMBER)),
?assertEqual(ok, is_valid_pwd(?UPPER_LETTER_CHARS ++ ?NUMBER)),
?assertEqual(ok, is_valid_pwd(?LOW_LETTER_CHARS ++ ?SPECIAL_CHARS)),
?assertEqual(ok, is_valid_pwd(?UPPER_LETTER_CHARS ++ ?SPECIAL_CHARS)),
?assertEqual(ok, is_valid_pwd(?SPECIAL_CHARS ++ ?NUMBER)),
?assertEqual(ok, is_valid_pwd(<<"abckldiekflkdf12">>)),
?assertEqual(ok, is_valid_pwd(<<"abckldiekflkdf w">>)),
?assertEqual(ok, is_valid_pwd(<<"# abckldiekflkdf w">>)),
?assertEqual(ok, is_valid_pwd(<<"# 12344858">>)),
?assertEqual(ok, is_valid_pwd(<<"# %12344858">>)),
ok.
-endif.

View File

@ -71,7 +71,7 @@ init_per_testcase(Case, Config) ->
end_per_testcase(Case, Config) -> end_per_testcase(Case, Config) ->
%% revert to default password %% revert to default password
emqx_dashboard_admin:change_password(<<"admin">>, <<"public">>), emqx_dashboard_admin:force_change_password(<<"admin">>, <<"public">>),
?MODULE:Case({'end', Config}). ?MODULE:Case({'end', Config}).
t_overview({init, Config}) -> Config; t_overview({init, Config}) -> Config;
@ -85,35 +85,36 @@ t_admins_add_delete(_) ->
?assertEqual({error,<<"0 < Length =< 256">>}, ?assertEqual({error,<<"0 < Length =< 256">>},
emqx_dashboard_admin:add_user(<<"">>, <<"password">>, <<"tag1">>)), emqx_dashboard_admin:add_user(<<"">>, <<"password">>, <<"tag1">>)),
?assertEqual({error,<<"2 < Length =< 32">>}, ?assertEqual({error,<<"The password length: 8-64">>},
emqx_dashboard_admin:add_user(<<"badusername">>, <<"">>, <<"tag1">>)), emqx_dashboard_admin:add_user(<<"badusername">>, <<"">>, <<"tag1">>)),
?assertEqual({error,<<"2 < Length =< 32">>}, ?assertEqual({error,<<"The password length: 8-64">>},
emqx_dashboard_admin:add_user(<<"badusername">>, <<"p">>, <<"tag1">>)), emqx_dashboard_admin:add_user(<<"badusername">>, <<"p">>, <<"tag1">>)),
P33 = iolist_to_binary(lists:duplicate(33, <<"p">>)), P65 = iolist_to_binary(lists:duplicate(65, <<"p">>)),
?assertEqual({error,<<"2 < Length =< 32">>}, ?assertEqual({error,<<"The password length: 8-64">>},
emqx_dashboard_admin:add_user(<<"badusername">>, P33, <<"tag1">>)), emqx_dashboard_admin:add_user(<<"badusername">>, P65, <<"tag1">>)),
P32 = iolist_to_binary(lists:duplicate(32, <<"p">>)), P64 = iolist_to_binary([<<"1">> | lists:duplicate(63, <<"p">>)]),
?assertEqual(ok, emqx_dashboard_admin:add_user(<<"goodusername">>, P32, <<"tag1">>)), ?assertEqual(ok, emqx_dashboard_admin:add_user(<<"goodusername">>, P64, <<"tag1">>)),
ok = emqx_dashboard_admin:remove_user(<<"goodusername">>), ok = emqx_dashboard_admin:remove_user(<<"goodusername">>),
ok = emqx_dashboard_admin:add_user(<<"username">>, <<"password">>, <<"tag">>), ok = emqx_dashboard_admin:add_user(<<"username1">>, <<"password1">>, <<"tag">>),
ok = emqx_dashboard_admin:add_user(<<"username1">>, <<"password1">>, <<"tag1">>), ok = emqx_dashboard_admin:add_user(<<"username2">>, <<"password2">>, <<"tag1">>),
ok = emqx_dashboard_admin:add_user(<<"1username1">>, <<"password1">>, <<"tag1">>), ok = emqx_dashboard_admin:add_user(<<"1username1">>, <<"password1">>, <<"tag1">>),
{error, _} = emqx_dashboard_admin:add_user(<<"u/sername1">>, <<"password1">>, <<"tag1">>), {error, _} = emqx_dashboard_admin:add_user(<<"u/sername1">>, <<"password1">>, <<"tag1">>),
{error, _} = emqx_dashboard_admin:add_user(<<"/username1">>, <<"password1">>, <<"tag1">>), {error, _} = emqx_dashboard_admin:add_user(<<"/username1">>, <<"password1">>, <<"tag1">>),
Admins = emqx_dashboard_admin:all_users(), Admins = emqx_dashboard_admin:all_users(),
?assertEqual(4, length(Admins)), ?assertEqual(4, length(Admins)),
ok = emqx_dashboard_admin:remove_user(<<"username1">>), ok = emqx_dashboard_admin:remove_user(<<"username2">>),
ok = emqx_dashboard_admin:remove_user(<<"1username1">>), ok = emqx_dashboard_admin:remove_user(<<"1username1">>),
Users = emqx_dashboard_admin:all_users(), Users = emqx_dashboard_admin:all_users(),
?assertEqual(2, length(Users)), ?assertEqual(2, length(Users)),
ok = emqx_dashboard_admin:change_password(<<"username">>, <<"password">>, <<"pwd">>), {error, _} = emqx_dashboard_admin:change_password(<<"username1">>, <<"password1">>, <<"password">>),
ok = emqx_dashboard_admin:change_password(<<"username1">>, <<"password1">>, <<"password+">>),
timer:sleep(10), timer:sleep(10),
?assert(request_dashboard(get, api_path("brokers"), auth_header_("username", "pwd"))), ?assert(request_dashboard(get, api_path("brokers"), auth_header_("username1", "password+"))),
ok = emqx_dashboard_admin:remove_user(<<"username">>), ok = emqx_dashboard_admin:remove_user(<<"username1">>),
?assertNotEqual(true, request_dashboard(get, api_path("brokers"), ?assertNotEqual(true, request_dashboard(get, api_path("brokers"),
auth_header_("username", "pwd"))). auth_header_("username1", "password+"))).
t_admins_persist_default_password({init, Config}) -> Config; t_admins_persist_default_password({init, Config}) -> Config;
t_admins_persist_default_password({'end', _Config}) -> ok; t_admins_persist_default_password({'end', _Config}) -> ok;
@ -212,16 +213,19 @@ t_rest_api(_Config) ->
?assert(lists:member(#{<<"username">> => <<"admin">>, <<"tags">> => <<"administrator">>}, ?assert(lists:member(#{<<"username">> => <<"admin">>, <<"tags">> => <<"administrator">>},
Users)), Users)),
{ok, ErrorRes} = http_put("change_pwd/admin", [{<<"old_pwd">>, <<"public">>}, {<<"new_pwd">>, <<"simplepwd">>}]),
?assertMatch(#{<<"message">> := _}, json(ErrorRes)),
AssertSuccess = fun({ok, Res}) -> AssertSuccess = fun({ok, Res}) ->
?assertEqual(#{<<"code">> => 0}, json(Res)) ?assertEqual(#{<<"code">> => 0}, json(Res))
end, end,
[AssertSuccess(R) [AssertSuccess(R)
|| R <- [ http_put("users/admin", #{<<"tags">> => <<"a_new_tag">>}) || R <- [ http_put("users/admin", #{<<"tags">> => <<"a_new_tag">>})
, http_post("users", #{<<"username">> => <<"usera">>, <<"password">> => <<"passwd">>}) , http_post("users", #{<<"username">> => <<"username1">>, <<"password">> => <<"passwd+123">>})
, http_post("auth", #{<<"username">> => <<"usera">>, <<"password">> => <<"passwd">>}) , http_post("auth", #{<<"username">> => <<"username1">>, <<"password">> => <<"passwd+123">>})
, http_delete("users/usera") , http_delete("users/username1")
, http_put("change_pwd/admin", #{<<"old_pwd">> => <<"public">>, <<"new_pwd">> => <<"newpwd">>}) , http_put("change_pwd/admin", #{<<"old_pwd">> => <<"public">>, <<"new_pwd">> => <<"newpwd+123">>})
, http_post("auth", #{<<"username">> => <<"admin">>, <<"password">> => <<"newpwd">>}) , http_post("auth", #{<<"username">> => <<"admin">>, <<"password">> => <<"newpwd+123">>})
]], ]],
ok. ok.
@ -236,18 +240,18 @@ t_cli({init, Config}) -> Config;
t_cli({'end', _Config}) -> ok; t_cli({'end', _Config}) -> ok;
t_cli(_Config) -> t_cli(_Config) ->
[mnesia:dirty_delete({mqtt_admin, Admin}) || Admin <- mnesia:dirty_all_keys(mqtt_admin)], [mnesia:dirty_delete({mqtt_admin, Admin}) || Admin <- mnesia:dirty_all_keys(mqtt_admin)],
emqx_dashboard_cli:admins(["add", "username", "password"]), emqx_dashboard_cli:admins(["add", "username", "password1"]),
[{mqtt_admin, <<"username">>, <<Salt:4/binary, Hash/binary>>, _}] = [{mqtt_admin, <<"username">>, <<Salt:4/binary, Hash/binary>>, _}] =
emqx_dashboard_admin:lookup_user(<<"username">>), emqx_dashboard_admin:lookup_user(<<"username">>),
?assertEqual(Hash, erlang:md5(<<Salt/binary, <<"password">>/binary>>)), ?assertEqual(Hash, erlang:md5(<<Salt/binary, <<"password1">>/binary>>)),
emqx_dashboard_cli:admins(["passwd", "username", "newpassword"]), emqx_dashboard_cli:admins(["passwd", "username", "newpassword1"]),
[{mqtt_admin, <<"username">>, <<Salt1:4/binary, Hash1/binary>>, _}] = [{mqtt_admin, <<"username">>, <<Salt1:4/binary, Hash1/binary>>, _}] =
emqx_dashboard_admin:lookup_user(<<"username">>), emqx_dashboard_admin:lookup_user(<<"username">>),
?assertEqual(Hash1, erlang:md5(<<Salt1/binary, <<"newpassword">>/binary>>)), ?assertEqual(Hash1, erlang:md5(<<Salt1/binary, <<"newpassword1">>/binary>>)),
emqx_dashboard_cli:admins(["del", "username"]), emqx_dashboard_cli:admins(["del", "username"]),
[] = emqx_dashboard_admin:lookup_user(<<"username">>), [] = emqx_dashboard_admin:lookup_user(<<"username">>),
emqx_dashboard_cli:admins(["add", "admin1", "pass1"]), emqx_dashboard_cli:admins(["add", "admin1", "password+1"]),
emqx_dashboard_cli:admins(["add", "admin2", "passw2"]), emqx_dashboard_cli:admins(["add", "admin2", "password+2"]),
AdminList = emqx_dashboard_admin:all_users(), AdminList = emqx_dashboard_admin:all_users(),
?assertEqual(2, length(AdminList)). ?assertEqual(2, length(AdminList)).

View File

@ -13,7 +13,7 @@ case "${PKG_VSN}" in
;; ;;
4.4*) 4.4*)
# keep the above 4.3 untouched, otherwise conflicts! # keep the above 4.3 untouched, otherwise conflicts!
EMQX_CE_DASHBOARD_VERSION='v4.4.7' EMQX_CE_DASHBOARD_VERSION='v4.4.8'
EMQX_EE_DASHBOARD_VERSION='v4.4.18' EMQX_EE_DASHBOARD_VERSION='v4.4.18'
;; ;;
*) *)

View File

@ -2,14 +2,11 @@
%% Unless you know what you are doing, DO NOT edit manually!! %% Unless you know what you are doing, DO NOT edit manually!!
{VSN, {VSN,
[{"4.4.13", [{"4.4.13",
[{load_module,emqx_rule_actions_trans,brutal_purge,soft_purge,[]}, [{load_module,emqx_cm,brutal_purge,soft_purge,[]},
{load_module,emqx_misc,brutal_purge,soft_purge,[]},
{load_module,emqx_relup,brutal_purge,soft_purge,[]}, {load_module,emqx_relup,brutal_purge,soft_purge,[]},
{load_module,emqx_app,brutal_purge,soft_purge,[]}]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}]},
{"4.4.12", {"4.4.12",
[{load_module,emqx_rule_actions_trans,brutal_purge,soft_purge,[]}, [{load_module,emqx_cm,brutal_purge,soft_purge,[]},
{load_module,emqx_misc,brutal_purge,soft_purge,[]},
{load_module,emqx_cm,brutal_purge,soft_purge,[]},
{load_module,emqx_relup,brutal_purge,soft_purge,[]}, {load_module,emqx_relup,brutal_purge,soft_purge,[]},
{load_module,emqx_app,brutal_purge,soft_purge,[]}]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}]},
{"4.4.11", {"4.4.11",
@ -413,14 +410,11 @@
[gen_rpc,insecure_auth_fallback_allowed,true]}}]}, [gen_rpc,insecure_auth_fallback_allowed,true]}}]},
{<<".*">>,[]}], {<<".*">>,[]}],
[{"4.4.13", [{"4.4.13",
[{load_module,emqx_rule_actions_trans,brutal_purge,soft_purge,[]}, [{load_module,emqx_cm,brutal_purge,soft_purge,[]},
{load_module,emqx_misc,brutal_purge,soft_purge,[]},
{load_module,emqx_relup,brutal_purge,soft_purge,[]}, {load_module,emqx_relup,brutal_purge,soft_purge,[]},
{load_module,emqx_app,brutal_purge,soft_purge,[]}]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}]},
{"4.4.12", {"4.4.12",
[{load_module,emqx_rule_actions_trans,brutal_purge,soft_purge,[]}, [{load_module,emqx_cm,brutal_purge,soft_purge,[]},
{load_module,emqx_misc,brutal_purge,soft_purge,[]},
{load_module,emqx_cm,brutal_purge,soft_purge,[]},
{load_module,emqx_relup,brutal_purge,soft_purge,[]}, {load_module,emqx_relup,brutal_purge,soft_purge,[]},
{load_module,emqx_app,brutal_purge,soft_purge,[]}]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}]},
{"4.4.11", {"4.4.11",