From 5521b7fa71a6070709a28fa073f9009aa5f92d4f Mon Sep 17 00:00:00 2001 From: zhongwencool Date: Wed, 12 Jan 2022 17:18:12 +0800 Subject: [PATCH] fix(auth): force update default mqtt_user when password or hashtype changed. --- .../emqx_auth_mnesia/src/emqx_auth_mnesia.erl | 4 +- .../src/emqx_auth_mnesia_cli.erl | 27 ++++++++- .../test/emqx_auth_mnesia_SUITE.erl | 56 +++++++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl index 905bcaaf0..74c7c71ee 100644 --- a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl +++ b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl @@ -32,6 +32,8 @@ , description/0 ]). +-export([match_password/3]). + init(#{clientid_list := ClientidList, username_list := UsernameList}) -> ok = ekka_mnesia:create_table(?TABLE, [ {disc_copies, [node()]}, @@ -45,7 +47,7 @@ init(#{clientid_list := ClientidList, username_list := UsernameList}) -> %% @private add_default_user({Login, Password}) when is_tuple(Login) -> - emqx_auth_mnesia_cli:add_user(Login, Password). + emqx_auth_mnesia_cli:force_add_user(Login, Password). -spec(register_metrics() -> ok). register_metrics() -> diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_cli.erl b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_cli.erl index d89e6836c..72a932aa1 100644 --- a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_cli.erl +++ b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_cli.erl @@ -22,6 +22,7 @@ -define(TABLE, emqx_user). %% Auth APIs -export([ add_user/2 + , force_add_user/2 , update_user/2 , remove_user/1 , lookup_user/1 @@ -56,6 +57,28 @@ insert_user(User = #emqx_user{login = Login}) -> [_|_] -> mnesia:abort(existed) end. +force_add_user(Login, Password) -> + User = #emqx_user{ + login = Login, + password = encrypted_data(Password), + created_at = erlang:system_time(millisecond) + }, + ret(mnesia:transaction(fun insert_or_update_user/2, [Password, User])). + +insert_or_update_user(NewPwd, User = #emqx_user{login = Login}) -> + case mnesia:read(?TABLE, Login) of + [] -> mnesia:write(User); + [#emqx_user{password = Pwd}] -> + case emqx_auth_mnesia:match_password(NewPwd, hash_type(), [Pwd]) of + true -> ok; + false -> + Res = mnesia:write(User), + ?LOG(warning, "[Mnesia] (~p)'s password has be updated.", [Login]), + Res + end + end. + + %% @doc Update User -spec(update_user(tuple(), binary()) -> ok | {error, any()}). update_user(Login, NewPassword) -> @@ -109,7 +132,7 @@ ret({atomic, ok}) -> ok; ret({aborted, Error}) -> {error, Error}. encrypted_data(Password) -> - HashType = application:get_env(emqx_auth_mnesia, password_hash, sha256), + HashType = hash_type(), SaltBin = salt(), <>. @@ -192,3 +215,5 @@ auth_username_cli(_) -> {"user add ", "Add username auth rule"}, {"user update ", "Update username auth rule"}, {"user delete ", "Delete username auth rule"}]). +hash_type() -> + application:get_env(emqx_auth_mnesia, password_hash, sha256). diff --git a/apps/emqx_auth_mnesia/test/emqx_auth_mnesia_SUITE.erl b/apps/emqx_auth_mnesia/test/emqx_auth_mnesia_SUITE.erl index c5c0eb727..01dff1488 100644 --- a/apps/emqx_auth_mnesia/test/emqx_auth_mnesia_SUITE.erl +++ b/apps/emqx_auth_mnesia/test/emqx_auth_mnesia_SUITE.erl @@ -46,11 +46,15 @@ all() -> groups() -> []. +init_per_suite(t_boot) -> + ok; init_per_suite(Config) -> ok = emqx_ct_helpers:start_apps([emqx_management, emqx_auth_mnesia], fun set_special_configs/1), create_default_app(), Config. +end_per_suite(t_boot) -> + ok; end_per_suite(_Config) -> delete_default_app(), emqx_ct_helpers:stop_apps([emqx_management, emqx_auth_mnesia]). @@ -65,10 +69,62 @@ set_special_configs(emqx) -> set_special_configs(_App) -> ok. +set_default(ClientId, UserName, Pwd, HashType) -> + application:set_env(emqx_auth_mnesia, clientid_list, [{ClientId, Pwd}]), + application:set_env(emqx_auth_mnesia, username_list, [{UserName, Pwd}]), + application:set_env(emqx_auth_mnesia, password_hash, HashType), + ok. %%------------------------------------------------------------------------------ %% Testcases %%------------------------------------------------------------------------------ +t_boot(_Config) -> + clean_all_users(), + emqx_ct_helpers:stop_apps([emqx_auth_mnesia]), + ClientId = <<"clientid-test">>, + UserName = <<"username-test">>, + Pwd = <<"emqx123456">>, + ok = emqx_ct_helpers:start_apps([emqx_auth_mnesia], + fun(_) -> set_default(ClientId, UserName, Pwd, sha256) end), + Ok = {stop, #{anonymous => false, auth_result => success}}, + Failed = {stop, #{anonymous => false, auth_result => password_error}}, + ?assertEqual(Ok, + emqx_auth_mnesia:check(#{clientid => ClientId, password => Pwd}, #{}, #{hash_type => sha256})), + ?assertEqual(Ok, + emqx_auth_mnesia:check(#{clientid => <<"NotExited">>, username => UserName, password => Pwd}, + #{}, #{hash_type => sha256})), + ?assertEqual(Failed, + emqx_auth_mnesia:check(#{clientid => ClientId, password => <>}, + #{}, #{hash_type => sha256})), + ?assertEqual(Failed, + emqx_auth_mnesia:check(#{clientid => ClientId, username => UserName, password => <>}, + #{}, #{hash_type => sha256})), + emqx_ct_helpers:stop_apps([emqx_auth_mnesia]), + + %% change default pwd + NewPwd = <<"emqx654321">>, + ok = emqx_ct_helpers:start_apps([emqx_auth_mnesia], + fun(_) -> set_default(ClientId, UserName, NewPwd, sha256) end), + ?assertEqual(Ok, + emqx_auth_mnesia:check(#{clientid => ClientId, password => NewPwd}, + #{}, #{hash_type => sha256})), + ?assertEqual(Ok, + emqx_auth_mnesia:check(#{clientid => <<"NotExited">>, username => UserName, password => NewPwd}, + #{}, #{hash_type => sha256})), + emqx_ct_helpers:stop_apps([emqx_auth_mnesia]), + + %% change hash_type + NewPwd2 = <<"emqx6543210">>, + ok = emqx_ct_helpers:start_apps([emqx_auth_mnesia], + fun(_) -> set_default(ClientId, UserName, NewPwd2, plain) end), + ?assertEqual(Ok, + emqx_auth_mnesia:check(#{clientid => ClientId, password => NewPwd2}, + #{}, #{hash_type => plain})), + ?assertEqual(Ok, + emqx_auth_mnesia:check(#{clientid => <<"NotExited">>, username => UserName, password => NewPwd2}, + #{}, #{hash_type => plain})), + ok. + t_management(_Config) -> clean_all_users(),