From 822a51087312dbe42cc6c37f638b2ca39d385dad Mon Sep 17 00:00:00 2001 From: Thales Macedo Garitezi Date: Thu, 5 Jan 2023 13:41:13 -0300 Subject: [PATCH 1/4] chore: sync ee changes back into ce repo --- apps/emqx_management/src/emqx_mgmt_auth.erl | 30 ++------- ...x_auth_mnesia_data_export_import_SUITE.erl | 64 +++++++++++++++++++ .../test/emqx_mgmt_bootstrap_app_SUITE.erl | 17 +++-- changes/v4.4.14-en.md | 11 ++++ changes/v4.4.14-zh.md | 11 ++++ 5 files changed, 101 insertions(+), 32 deletions(-) create mode 100644 changes/v4.4.14-en.md create mode 100644 changes/v4.4.14-zh.md diff --git a/apps/emqx_management/src/emqx_mgmt_auth.erl b/apps/emqx_management/src/emqx_mgmt_auth.erl index 19aa48081..eea93f725 100644 --- a/apps/emqx_management/src/emqx_mgmt_auth.erl +++ b/apps/emqx_management/src/emqx_mgmt_auth.erl @@ -36,7 +36,6 @@ , del_app/1 , list_apps/0 , init_bootstrap_apps/0 - , need_bootstrap/0 , clear_bootstrap_apps/0 ]). @@ -83,21 +82,8 @@ add_default_app() -> end. init_bootstrap_apps() -> - case need_bootstrap() of - true -> - Bootstrap = application:get_env(emqx_management, bootstrap_apps_file, undefined), - 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. + Bootstrap = application:get_env(emqx_management, bootstrap_apps_file, undefined), + init_bootstrap_apps(Bootstrap). clear_bootstrap_apps() -> {atomic, ok} = @@ -113,13 +99,7 @@ init_bootstrap_apps(File) -> case file:open(File, [read, binary]) of {ok, Dev} -> {ok, MP} = re:compile(<<"(\.+):(\.+$)">>, [ungreedy]), - case init_bootstrap_apps(File, Dev, MP) of - ok -> ok; - Error -> - %% if failed add bootstrap users, we should clear all bootstrap apps - clear_bootstrap_apps(), - Error - end; + init_bootstrap_apps(File, Dev, MP); {error, Reason} = Error -> ?LOG(error, "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 {match, [[AppId, AppSecret]]} -> Name = <<"bootstraped">>, - case add_app(AppId, Name, AppSecret, ?BOOTSTRAP_TAG, true, undefined) of - {ok, _} -> + case force_add_app(AppId, Name, AppSecret, ?BOOTSTRAP_TAG, true, undefined) of + ok -> add_bootstrap_app(File, Dev, MP, Line + 1); {error, Reason} -> throw(#{file => File, line => Line, content => Bin, reason => Reason}) diff --git a/apps/emqx_management/test/emqx_auth_mnesia_data_export_import_SUITE.erl b/apps/emqx_management/test/emqx_auth_mnesia_data_export_import_SUITE.erl index da27fa2ee..dc9a3e19f 100644 --- a/apps/emqx_management/test/emqx_auth_mnesia_data_export_import_SUITE.erl +++ b/apps/emqx_management/test/emqx_auth_mnesia_data_export_import_SUITE.erl @@ -66,6 +66,70 @@ t_importee430(_) -> {ok, _} = emqx_mgmt_data_backup:export(), 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() -> mnesia:delete_table(emqx_user), mnesia:delete_table(emqx_acl). diff --git a/apps/emqx_management/test/emqx_mgmt_bootstrap_app_SUITE.erl b/apps/emqx_management/test/emqx_mgmt_bootstrap_app_SUITE.erl index d2c12cc39..734ec8e5e 100644 --- a/apps/emqx_management/test/emqx_mgmt_bootstrap_app_SUITE.erl +++ b/apps/emqx_management/test/emqx_mgmt_bootstrap_app_SUITE.erl @@ -68,10 +68,10 @@ t_load_ok(_) -> ok = file:write_file(File, Bin1), application:set_env(emqx_management, bootstrap_apps_file, File), {ok, _} = application:ensure_all_started(emqx_management), - ?assert(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-1">>, <<"new-secret-1">>)), - ?assertNot(emqx_mgmt_auth:is_authorized(<<"test-2">>, <<"new-secret-2">>)), + ?assertNot(emqx_mgmt_auth:is_authorized(<<"test-1">>, <<"secret-1">>)), + ?assertNot(emqx_mgmt_auth:is_authorized(<<"test-2">>, <<"secret-2">>)), + ?assert(emqx_mgmt_auth:is_authorized(<<"test-1">>, <<"new-secret-1">>)), + ?assert(emqx_mgmt_auth:is_authorized(<<"test-2">>, <<"new-secret-2">>)), application:stop(emqx_management). t_bootstrap_user_file_not_found(_) -> @@ -84,13 +84,17 @@ t_load_invalid_username_failed(_) -> File = "./bootstrap_apps.txt", ok = file:write_file(File, Bin), 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. t_load_invalid_format_failed(_) -> - Bin = <<"test-1:password-1\ntest-2password-2">>, + Bin = <<"test-1:password-1\ntest-2 password-2">>, File = "./bootstrap_apps.txt", ok = file:write_file(File, Bin), 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. check_load_failed(File) -> @@ -98,5 +102,4 @@ check_load_failed(File) -> application:stop(emqx_management), application:set_env(emqx_management, bootstrap_apps_file, File), ?assertMatch({error, _}, application:ensure_all_started(emqx_management)), - ?assertNot(lists:member(emqx_management, application:which_applications())), - ?assert(emqx_mgmt_auth:need_bootstrap()). + ?assertNot(lists:member(emqx_management, application:which_applications())). diff --git a/changes/v4.4.14-en.md b/changes/v4.4.14-en.md new file mode 100644 index 000000000..c4a0cb64d --- /dev/null +++ b/changes/v4.4.14-en.md @@ -0,0 +1,11 @@ +# v4.4.14 + +## 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 [#1696](https://github.com/emqx/emqx-enterprise/pull/1696). + +## 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. [#1696](https://github.com/emqx/emqx-enterprise/pull/1696). +- Fix the problem that adding or importing Dashboard users via the API fails to add complex passwords due to incorrect checksum of the passwords [#1696](https://github.com/emqx/emqx-enterprise/pull/1696). + +- Fix load bootstrap_app_file's apps is not sync when reboot. [#1697](https://github.com/emqx/emqx-enterprise/pull/1697). diff --git a/changes/v4.4.14-zh.md b/changes/v4.4.14-zh.md new file mode 100644 index 000000000..0d6679046 --- /dev/null +++ b/changes/v4.4.14-zh.md @@ -0,0 +1,11 @@ +# v4.4.14 + +## 增强 + +- 通过 API 添加、修改 Dashboard 用户时,增加对密码复杂度的要求。现在密码必须包含字母、数字以及特殊字符中的至少 2 种,并且长度范围必须是 8~64 个字符 [#1696](https://github.com/emqx/emqx-enterprise/pull/1696)。 + +## 修复 + +- 修复通过 API 添加或者导入 Dashboard 用户时,对密码进行了错误的校验,导致复杂密码添加失败的问题 [#1696](https://github.com/emqx/emqx-enterprise/pull/1696)。 + +- 修复 boostrap_apps_file 文件更新后没有同步更新至数据库导致文件中新加 apps 未生效 [#1697](https://github.com/emqx/emqx-enterprise/pull/1697)。 From f6bdd1ffecd28c4b12acfc70eb24928b659505c7 Mon Sep 17 00:00:00 2001 From: Thales Macedo Garitezi Date: Thu, 5 Jan 2023 13:42:14 -0300 Subject: [PATCH 2/4] chore: prepare for v4.4.14 release --- apps/emqx_management/src/emqx_management.app.src | 2 +- apps/emqx_rule_engine/src/emqx_rule_engine.app.src | 2 +- apps/emqx_rule_engine/src/emqx_rule_engine.appup.src | 6 ++++-- data/relup-paths.eterm | 9 ++++++++- deploy/charts/emqx/Chart.yaml | 4 ++-- include/emqx_release.hrl | 2 +- lib-ce/emqx_dashboard/src/emqx_dashboard.app.src | 2 +- src/emqx.app.src | 2 +- src/emqx.appup.src | 12 ++++++++++-- 9 files changed, 29 insertions(+), 12 deletions(-) diff --git a/apps/emqx_management/src/emqx_management.app.src b/apps/emqx_management/src/emqx_management.app.src index 3eb690bdb..fe927c097 100644 --- a/apps/emqx_management/src/emqx_management.app.src +++ b/apps/emqx_management/src/emqx_management.app.src @@ -1,6 +1,6 @@ {application, emqx_management, [{description, "EMQ X Management API and CLI"}, - {vsn, "4.4.11"}, % strict semver, bump manually! + {vsn, "4.4.12"}, % strict semver, bump manually! {modules, []}, {registered, [emqx_management_sup]}, {applications, [kernel,stdlib,emqx_plugin_libs,minirest]}, diff --git a/apps/emqx_rule_engine/src/emqx_rule_engine.app.src b/apps/emqx_rule_engine/src/emqx_rule_engine.app.src index 8c1feb42f..99dfb77a8 100644 --- a/apps/emqx_rule_engine/src/emqx_rule_engine.app.src +++ b/apps/emqx_rule_engine/src/emqx_rule_engine.app.src @@ -1,6 +1,6 @@ {application, emqx_rule_engine, [{description, "EMQ X Rule Engine"}, - {vsn, "4.4.13"}, % strict semver, bump manually! + {vsn, "4.4.14"}, % strict semver, bump manually! {modules, []}, {registered, [emqx_rule_engine_sup, emqx_rule_registry, emqx_rule_engine_jwt_sup]}, {applications, [kernel,stdlib,rulesql,getopt,jose]}, diff --git a/apps/emqx_rule_engine/src/emqx_rule_engine.appup.src b/apps/emqx_rule_engine/src/emqx_rule_engine.appup.src index 6a11776ed..544e21d30 100644 --- a/apps/emqx_rule_engine/src/emqx_rule_engine.appup.src +++ b/apps/emqx_rule_engine/src/emqx_rule_engine.appup.src @@ -1,7 +1,8 @@ %% -*- mode: erlang -*- %% Unless you know what you are doing, DO NOT edit manually!! {VSN, - [{"4.4.12", + [{"4.4.13",[{load_module,emqx_rule_registry,brutal_purge,soft_purge,[]}]}, + {"4.4.12", [{load_module,emqx_rule_registry,brutal_purge,soft_purge,[]}, {load_module,emqx_rule_engine_jwt_worker,brutal_purge,soft_purge,[]}, {update,emqx_rule_engine_jwt_sup,supervisor}, @@ -220,7 +221,8 @@ {load_module,emqx_rule_engine,brutal_purge,soft_purge,[]}, {load_module,emqx_rule_runtime,brutal_purge,soft_purge,[]}]}, {<<".*">>,[]}], - [{"4.4.12", + [{"4.4.13",[{load_module,emqx_rule_registry,brutal_purge,soft_purge,[]}]}, + {"4.4.12", [{load_module,emqx_rule_registry,brutal_purge,soft_purge,[]}, {load_module,emqx_rule_engine_jwt_worker,brutal_purge,soft_purge,[]}, {update,emqx_rule_engine_jwt_sup,supervisor}, diff --git a/data/relup-paths.eterm b/data/relup-paths.eterm index c72c4bc50..1f075e888 100644 --- a/data/relup-paths.eterm +++ b/data/relup-paths.eterm @@ -57,7 +57,14 @@ <<"4.4.2">>,<<"4.4.3">>,<<"4.4.4">>,<<"4.4.5">>,<<"4.4.6">>, <<"4.4.7">>,<<"4.4.8">>,<<"4.4.9">>], otp => <<"24.3.4.2-1">>}}. +{<<"4.4.14">>, + #{from_versions => + [<<"4.4.0">>,<<"4.4.1">>,<<"4.4.10">>,<<"4.4.11">>,<<"4.4.12">>, + <<"4.4.13">>,<<"4.4.2">>,<<"4.4.3">>,<<"4.4.4">>,<<"4.4.5">>, + <<"4.4.6">>,<<"4.4.7">>,<<"4.4.8">>,<<"4.4.9">>], + otp => <<"24.3.4.2-1">>}}. {<<"4.5.0">>, #{from_versions => - [<<"4.4.10">>,<<"4.4.11">>,<<"4.4.13">>,<<"4.4.8">>,<<"4.4.9">>], + [<<"4.4.10">>,<<"4.4.11">>,<<"4.4.13">>,<<"4.4.14">>,<<"4.4.8">>, + <<"4.4.9">>], otp => <<"24.3.4.2-1">>}}. diff --git a/deploy/charts/emqx/Chart.yaml b/deploy/charts/emqx/Chart.yaml index 523793eef..092af1ab7 100644 --- a/deploy/charts/emqx/Chart.yaml +++ b/deploy/charts/emqx/Chart.yaml @@ -13,8 +13,8 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. -version: 4.4.13 +version: 4.4.14 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. -appVersion: 4.4.13 +appVersion: 4.4.14 diff --git a/include/emqx_release.hrl b/include/emqx_release.hrl index f31e4650b..f3d8a27da 100644 --- a/include/emqx_release.hrl +++ b/include/emqx_release.hrl @@ -29,7 +29,7 @@ -ifndef(EMQX_ENTERPRISE). --define(EMQX_RELEASE, {opensource, "4.4.13"}). +-define(EMQX_RELEASE, {opensource, "4.4.14"}). -else. diff --git a/lib-ce/emqx_dashboard/src/emqx_dashboard.app.src b/lib-ce/emqx_dashboard/src/emqx_dashboard.app.src index 38a5ee5a2..37b805ca3 100644 --- a/lib-ce/emqx_dashboard/src/emqx_dashboard.app.src +++ b/lib-ce/emqx_dashboard/src/emqx_dashboard.app.src @@ -1,6 +1,6 @@ {application, emqx_dashboard, [{description, "EMQX Web Dashboard"}, - {vsn, "4.4.12"}, % strict semver, bump manually! + {vsn, "4.4.13"}, % strict semver, bump manually! {modules, []}, {registered, [emqx_dashboard_sup]}, {applications, [kernel,stdlib,mnesia,minirest]}, diff --git a/src/emqx.app.src b/src/emqx.app.src index 6d9aeb76f..1f75f4220 100644 --- a/src/emqx.app.src +++ b/src/emqx.app.src @@ -6,7 +6,7 @@ %% the emqx `release' version, which in turn is comprised of several %% apps, one of which is this. See `emqx_release.hrl' for more %% info. - {vsn, "4.4.13"}, % strict semver, bump manually! + {vsn, "4.4.14"}, % strict semver, bump manually! {modules, []}, {registered, []}, {applications, [ kernel diff --git a/src/emqx.appup.src b/src/emqx.appup.src index b8cbdf684..69feb6a5d 100644 --- a/src/emqx.appup.src +++ b/src/emqx.appup.src @@ -1,7 +1,11 @@ %% -*- mode: erlang -*- %% Unless you know what you are doing, DO NOT edit manually!! {VSN, - [{"4.4.12", + [{"4.4.13", + [{load_module,emqx_cm,brutal_purge,soft_purge,[]}, + {load_module,emqx_relup,brutal_purge,soft_purge,[]}, + {load_module,emqx_app,brutal_purge,soft_purge,[]}]}, + {"4.4.12", [{load_module,emqx_cm,brutal_purge,soft_purge,[]}, {load_module,emqx_relup,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}]}, @@ -393,7 +397,11 @@ {apply,{application,set_env, [gen_rpc,insecure_auth_fallback_allowed,true]}}]}, {<<".*">>,[]}], - [{"4.4.12", + [{"4.4.13", + [{load_module,emqx_cm,brutal_purge,soft_purge,[]}, + {load_module,emqx_relup,brutal_purge,soft_purge,[]}, + {load_module,emqx_app,brutal_purge,soft_purge,[]}]}, + {"4.4.12", [{load_module,emqx_cm,brutal_purge,soft_purge,[]}, {load_module,emqx_relup,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}]}, From 5e717bb31698925dcc253c384282d6473dfec5f1 Mon Sep 17 00:00:00 2001 From: zhongwencool Date: Fri, 6 Jan 2023 17:31:02 +0800 Subject: [PATCH 3/4] fix: update dashboard password validator --- changes/v4.4.14-en.md | 8 +- changes/v4.4.14-zh.md | 6 +- .../src/emqx_dashboard_admin.erl | 92 ++++++++++++++++--- .../test/emqx_dashboard_SUITE.erl | 56 +++++------ scripts/get-dashboard.sh | 2 +- 5 files changed, 118 insertions(+), 46 deletions(-) diff --git a/changes/v4.4.14-en.md b/changes/v4.4.14-en.md index c4a0cb64d..838cc1c54 100644 --- a/changes/v4.4.14-en.md +++ b/changes/v4.4.14-en.md @@ -1,11 +1,11 @@ # v4.4.14 ## 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 [#1696](https://github.com/emqx/emqx-enterprise/pull/1696). +- 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 -- 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. [#1696](https://github.com/emqx/emqx-enterprise/pull/1696). -- Fix the problem that adding or importing Dashboard users via the API fails to add complex passwords due to incorrect checksum of the passwords [#1696](https://github.com/emqx/emqx-enterprise/pull/1696). +- 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. [#1697](https://github.com/emqx/emqx-enterprise/pull/1697). +- Fix load bootstrap_app_file's apps is not sync when reboot. [#9692](https://github.com/emqx/emqx/pull/9692). diff --git a/changes/v4.4.14-zh.md b/changes/v4.4.14-zh.md index 0d6679046..d0e28290a 100644 --- a/changes/v4.4.14-zh.md +++ b/changes/v4.4.14-zh.md @@ -2,10 +2,10 @@ ## 增强 -- 通过 API 添加、修改 Dashboard 用户时,增加对密码复杂度的要求。现在密码必须包含字母、数字以及特殊字符中的至少 2 种,并且长度范围必须是 8~64 个字符 [#1696](https://github.com/emqx/emqx-enterprise/pull/1696)。 +- 通过 API 添加、修改 Dashboard 用户时,增加对密码复杂度的要求。现在密码必须包含字母、数字以及特殊字符中的至少 2 种,并且长度范围必须是 8~64 个字符 [#9696](https://github.com/emqx/emqx/pull/9696)。 ## 修复 -- 修复通过 API 添加或者导入 Dashboard 用户时,对密码进行了错误的校验,导致复杂密码添加失败的问题 [#1696](https://github.com/emqx/emqx-enterprise/pull/1696)。 +- 修复通过 API 添加或者导入 Dashboard 用户时,对密码进行了错误的校验,导致复杂密码添加失败的问题 [#9696](https://github.com/emqx/emqx/pull/9696)。 -- 修复 boostrap_apps_file 文件更新后没有同步更新至数据库导致文件中新加 apps 未生效 [#1697](https://github.com/emqx/emqx-enterprise/pull/1697)。 +- 修复 boostrap_apps_file 文件更新后未同步至数据库,导致变更的 apps 无法生效 [#9692](https://github.com/emqx/emqx/pull/9692)。 diff --git a/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl b/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl index 49cf84b5b..ea9c4ce81 100644 --- a/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl +++ b/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl @@ -23,6 +23,7 @@ -include("emqx_dashboard.hrl"). -include_lib("emqx/include/logger.hrl"). -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]}). -copy_mnesia({mnesia, [copy]}). @@ -41,8 +42,10 @@ , lookup_user/1 , change_password/2 , change_password/3 + , force_change_password/2 , all_users/0 , check/2 + , hash/1 ]). %% gen_server Function Exports @@ -79,7 +82,7 @@ start_link() -> -spec(add_user(binary(), binary(), binary()) -> ok | {error, any()}). 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} -> Admin = #mqtt_admin{username = Username, password = hash(Password), tags = Tags}, 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. 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() -> - mnesia:write(#mqtt_admin{username = Username, password = Password, tags = Tags}) - end, - case mnesia:transaction(AddFun) of - {atomic, ok} -> ok; - {aborted, Reason} -> {error, Reason} - end; - {{error, Reason}, _} -> {error, Reason}; - {_, {error, Reason}} -> {error, Reason} + AddFun = fun() -> + mnesia:write(#mqtt_admin{username = Username, password = Password, tags = Tags}) + end, + case mnesia:transaction(AddFun) of + {atomic, ok} -> ok; + {aborted, Reason} -> {error, Reason} end. %% @private @@ -138,6 +136,12 @@ change_password(Username, OldPasswd, NewPasswd) when is_binary(Username) -> end. 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, PasswordHash) -> @@ -145,6 +149,37 @@ change_password_hash(Username, PasswordHash) -> User#mqtt_admin{password = PasswordHash} 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) -> Trans = fun() -> User = @@ -317,3 +352,36 @@ maybe_warn_default_pwd() -> false -> ok 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. diff --git a/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl b/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl index 95b8c2c74..4e2a160cf 100644 --- a/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl +++ b/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl @@ -71,7 +71,7 @@ init_per_testcase(Case, Config) -> end_per_testcase(Case, Config) -> %% revert to default password - emqx_dashboard_admin:change_password(<<"admin">>, <<"public">>), + emqx_dashboard_admin:force_change_password(<<"admin">>, <<"public">>), ?MODULE:Case({'end', Config}). t_overview({init, Config}) -> Config; @@ -85,35 +85,36 @@ t_admins_add_delete(_) -> ?assertEqual({error,<<"0 < Length =< 256">>}, 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">>)), - ?assertEqual({error,<<"2 < Length =< 32">>}, + ?assertEqual({error,<<"The password length: 8-64">>}, emqx_dashboard_admin:add_user(<<"badusername">>, <<"p">>, <<"tag1">>)), - P33 = iolist_to_binary(lists:duplicate(33, <<"p">>)), - ?assertEqual({error,<<"2 < Length =< 32">>}, - emqx_dashboard_admin:add_user(<<"badusername">>, P33, <<"tag1">>)), - P32 = iolist_to_binary(lists:duplicate(32, <<"p">>)), - ?assertEqual(ok, emqx_dashboard_admin:add_user(<<"goodusername">>, P32, <<"tag1">>)), + P65 = iolist_to_binary(lists:duplicate(65, <<"p">>)), + ?assertEqual({error,<<"The password length: 8-64">>}, + emqx_dashboard_admin:add_user(<<"badusername">>, P65, <<"tag1">>)), + P64 = iolist_to_binary([<<"1">> | lists:duplicate(63, <<"p">>)]), + ?assertEqual(ok, emqx_dashboard_admin:add_user(<<"goodusername">>, P64, <<"tag1">>)), ok = emqx_dashboard_admin:remove_user(<<"goodusername">>), - ok = emqx_dashboard_admin:add_user(<<"username">>, <<"password">>, <<"tag">>), - ok = emqx_dashboard_admin:add_user(<<"username1">>, <<"password1">>, <<"tag1">>), + ok = emqx_dashboard_admin:add_user(<<"username1">>, <<"password1">>, <<"tag">>), + ok = emqx_dashboard_admin:add_user(<<"username2">>, <<"password2">>, <<"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(<<"/username1">>, <<"password1">>, <<"tag1">>), Admins = emqx_dashboard_admin:all_users(), ?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">>), Users = emqx_dashboard_admin:all_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), - ?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"), - auth_header_("username", "pwd"))). + auth_header_("username1", "password+"))). t_admins_persist_default_password({init, Config}) -> Config; t_admins_persist_default_password({'end', _Config}) -> ok; @@ -212,16 +213,19 @@ t_rest_api(_Config) -> ?assert(lists:member(#{<<"username">> => <<"admin">>, <<"tags">> => <<"administrator">>}, Users)), + {ok, ErrorRes} = http_put("change_pwd/admin", [{<<"old_pwd">>, <<"public">>}, {<<"new_pwd">>, <<"simplepwd">>}]), + ?assertMatch(#{<<"message">> := _}, json(ErrorRes)), + AssertSuccess = fun({ok, Res}) -> ?assertEqual(#{<<"code">> => 0}, json(Res)) 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_delete("users/usera") - , http_put("change_pwd/admin", #{<<"old_pwd">> => <<"public">>, <<"new_pwd">> => <<"newpwd">>}) - , http_post("auth", #{<<"username">> => <<"admin">>, <<"password">> => <<"newpwd">>}) + , http_post("users", #{<<"username">> => <<"username1">>, <<"password">> => <<"passwd+123">>}) + , http_post("auth", #{<<"username">> => <<"username1">>, <<"password">> => <<"passwd+123">>}) + , http_delete("users/username1") + , http_put("change_pwd/admin", #{<<"old_pwd">> => <<"public">>, <<"new_pwd">> => <<"newpwd+123">>}) + , http_post("auth", #{<<"username">> => <<"admin">>, <<"password">> => <<"newpwd+123">>}) ]], ok. @@ -236,18 +240,18 @@ t_cli({init, Config}) -> Config; t_cli({'end', _Config}) -> ok; t_cli(_Config) -> [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">>, <>, _}] = emqx_dashboard_admin:lookup_user(<<"username">>), - ?assertEqual(Hash, erlang:md5(<>/binary>>)), - emqx_dashboard_cli:admins(["passwd", "username", "newpassword"]), + ?assertEqual(Hash, erlang:md5(<>/binary>>)), + emqx_dashboard_cli:admins(["passwd", "username", "newpassword1"]), [{mqtt_admin, <<"username">>, <>, _}] = emqx_dashboard_admin:lookup_user(<<"username">>), - ?assertEqual(Hash1, erlang:md5(<>/binary>>)), + ?assertEqual(Hash1, erlang:md5(<>/binary>>)), emqx_dashboard_cli:admins(["del", "username"]), [] = emqx_dashboard_admin:lookup_user(<<"username">>), - emqx_dashboard_cli:admins(["add", "admin1", "pass1"]), - emqx_dashboard_cli:admins(["add", "admin2", "passw2"]), + emqx_dashboard_cli:admins(["add", "admin1", "password+1"]), + emqx_dashboard_cli:admins(["add", "admin2", "password+2"]), AdminList = emqx_dashboard_admin:all_users(), ?assertEqual(2, length(AdminList)). diff --git a/scripts/get-dashboard.sh b/scripts/get-dashboard.sh index 07bb60382..b4d08c216 100755 --- a/scripts/get-dashboard.sh +++ b/scripts/get-dashboard.sh @@ -13,7 +13,7 @@ case "${PKG_VSN}" in ;; 4.4*) # 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' ;; *) From d6e0ecddbc4cb78926b0232817fcaf0399bab66f Mon Sep 17 00:00:00 2001 From: Thales Macedo Garitezi Date: Fri, 6 Jan 2023 13:46:13 -0300 Subject: [PATCH 4/4] chore: update appups --- .../emqx_dashboard/src/emqx_dashboard.app.src | 2 +- src/emqx.app.src | 2 +- src/emqx.appup.src | 26 ++++++++++++++----- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib-ce/emqx_dashboard/src/emqx_dashboard.app.src b/lib-ce/emqx_dashboard/src/emqx_dashboard.app.src index 37b805ca3..a870ede61 100644 --- a/lib-ce/emqx_dashboard/src/emqx_dashboard.app.src +++ b/lib-ce/emqx_dashboard/src/emqx_dashboard.app.src @@ -1,6 +1,6 @@ {application, emqx_dashboard, [{description, "EMQX Web Dashboard"}, - {vsn, "4.4.13"}, % strict semver, bump manually! + {vsn, "4.4.14"}, % strict semver, bump manually! {modules, []}, {registered, [emqx_dashboard_sup]}, {applications, [kernel,stdlib,mnesia,minirest]}, diff --git a/src/emqx.app.src b/src/emqx.app.src index 1f75f4220..4c082b971 100644 --- a/src/emqx.app.src +++ b/src/emqx.app.src @@ -6,7 +6,7 @@ %% the emqx `release' version, which in turn is comprised of several %% apps, one of which is this. See `emqx_release.hrl' for more %% info. - {vsn, "4.4.14"}, % strict semver, bump manually! + {vsn, "4.4.15"}, % strict semver, bump manually! {modules, []}, {registered, []}, {applications, [ kernel diff --git a/src/emqx.appup.src b/src/emqx.appup.src index e782a181b..ebb567dbd 100644 --- a/src/emqx.appup.src +++ b/src/emqx.appup.src @@ -1,12 +1,19 @@ %% -*- mode: erlang -*- %% Unless you know what you are doing, DO NOT edit manually!! {VSN, - [{"4.4.13", - [{load_module,emqx_cm,brutal_purge,soft_purge,[]}, + [{"4.4.14", + [{load_module,emqx_misc,brutal_purge,soft_purge,[]}, + {load_module,emqx_rule_actions_trans,brutal_purge,soft_purge,[]}]}, + {"4.4.13", + [{load_module,emqx_misc,brutal_purge,soft_purge,[]}, + {load_module,emqx_rule_actions_trans,brutal_purge,soft_purge,[]}, + {load_module,emqx_cm,brutal_purge,soft_purge,[]}, {load_module,emqx_relup,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}]}, {"4.4.12", - [{load_module,emqx_cm,brutal_purge,soft_purge,[]}, + [{load_module,emqx_misc,brutal_purge,soft_purge,[]}, + {load_module,emqx_rule_actions_trans,brutal_purge,soft_purge,[]}, + {load_module,emqx_cm,brutal_purge,soft_purge,[]}, {load_module,emqx_relup,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}]}, {"4.4.11", @@ -409,12 +416,19 @@ {apply,{application,set_env, [gen_rpc,insecure_auth_fallback_allowed,true]}}]}, {<<".*">>,[]}], - [{"4.4.13", - [{load_module,emqx_cm,brutal_purge,soft_purge,[]}, + [{"4.4.14", + [{load_module,emqx_misc,brutal_purge,soft_purge,[]}, + {load_module,emqx_rule_actions_trans,brutal_purge,soft_purge,[]}]}, + {"4.4.13", + [{load_module,emqx_misc,brutal_purge,soft_purge,[]}, + {load_module,emqx_rule_actions_trans,brutal_purge,soft_purge,[]}, + {load_module,emqx_cm,brutal_purge,soft_purge,[]}, {load_module,emqx_relup,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}]}, {"4.4.12", - [{load_module,emqx_cm,brutal_purge,soft_purge,[]}, + [{load_module,emqx_misc,brutal_purge,soft_purge,[]}, + {load_module,emqx_rule_actions_trans,brutal_purge,soft_purge,[]}, + {load_module,emqx_cm,brutal_purge,soft_purge,[]}, {load_module,emqx_relup,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}]}, {"4.4.11",