From aedfc8e8c0f49b1b139b8d0294655f1cedde2d87 Mon Sep 17 00:00:00 2001 From: JianBo He Date: Tue, 30 Jan 2024 14:14:20 +0800 Subject: [PATCH 1/2] fix(user_import): ensure the last record overwrites previous one --- .../emqx_authn/emqx_authn_user_import_api.erl | 12 ++++- .../test/data/user-credentials-plain-dup.csv | 3 ++ .../test/data/user-credentials-plain-dup.json | 12 +++++ .../src/emqx_authn_mnesia.erl | 2 +- .../test/emqx_authn_mnesia_SUITE.erl | 49 +++++++++++++++++++ 5 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 apps/emqx_auth/test/data/user-credentials-plain-dup.csv create mode 100644 apps/emqx_auth/test/data/user-credentials-plain-dup.json diff --git a/apps/emqx_auth/src/emqx_authn/emqx_authn_user_import_api.erl b/apps/emqx_auth/src/emqx_authn/emqx_authn_user_import_api.erl index f45923756..2b2ccecac 100644 --- a/apps/emqx_auth/src/emqx_authn/emqx_authn_user_import_api.erl +++ b/apps/emqx_auth/src/emqx_authn/emqx_authn_user_import_api.erl @@ -96,8 +96,16 @@ request_body_schema() -> schema => #{ type => object, example => [ - #{<<"user_id">> => <<"user1">>, <<"password">> => <<"password1">>}, - #{<<"user_id">> => <<"user2">>, <<"password">> => <<"password2">>} + #{ + <<"user_id">> => <<"user1">>, + <<"password">> => <<"password1">>, + <<"is_superuser">> => true + }, + #{ + <<"user_id">> => <<"user2">>, + <<"password">> => <<"password2">>, + <<"is_superuser">> => false + } ] } } diff --git a/apps/emqx_auth/test/data/user-credentials-plain-dup.csv b/apps/emqx_auth/test/data/user-credentials-plain-dup.csv new file mode 100644 index 000000000..9c263c50b --- /dev/null +++ b/apps/emqx_auth/test/data/user-credentials-plain-dup.csv @@ -0,0 +1,3 @@ +user_id,password,is_superuser +myuser3,password3,true +myuser3,password4,false diff --git a/apps/emqx_auth/test/data/user-credentials-plain-dup.json b/apps/emqx_auth/test/data/user-credentials-plain-dup.json new file mode 100644 index 000000000..0bda977c6 --- /dev/null +++ b/apps/emqx_auth/test/data/user-credentials-plain-dup.json @@ -0,0 +1,12 @@ +[ + { + "user_id":"myuser1", + "password":"password1", + "is_superuser": true + }, + { + "user_id":"myuser1", + "password":"password2", + "is_superuser": false + } +] diff --git a/apps/emqx_auth_mnesia/src/emqx_authn_mnesia.erl b/apps/emqx_auth_mnesia/src/emqx_authn_mnesia.erl index bae7dc96b..8cbd8f35e 100644 --- a/apps/emqx_auth_mnesia/src/emqx_authn_mnesia.erl +++ b/apps/emqx_auth_mnesia/src/emqx_authn_mnesia.erl @@ -457,7 +457,7 @@ parse_import_users(Filename, FileData, Convertor) -> end end, ReaderFn = reader_fn(Filename, FileData), - Users = lists:reverse(Eval(ReaderFn)), + Users = Eval(ReaderFn), NewUsersCount = lists:foldl( fun( diff --git a/apps/emqx_auth_mnesia/test/emqx_authn_mnesia_SUITE.erl b/apps/emqx_auth_mnesia/test/emqx_authn_mnesia_SUITE.erl index 479b5cdde..a1cbf362a 100644 --- a/apps/emqx_auth_mnesia/test/emqx_authn_mnesia_SUITE.erl +++ b/apps/emqx_auth_mnesia/test/emqx_authn_mnesia_SUITE.erl @@ -340,6 +340,55 @@ t_import_users_prepared_list(_) -> ) ). +t_import_users_duplicated_records(_) -> + Config0 = config(), + Config = Config0#{password_hash_algorithm => #{name => plain, salt_position => disable}}, + {ok, State} = emqx_authn_mnesia:create(?AUTHN_ID, Config), + + ?assertEqual( + ok, + emqx_authn_mnesia:import_users( + sample_filename_and_data(plain, <<"user-credentials-plain-dup.json">>), + State + ) + ), + ?assertEqual( + ok, + emqx_authn_mnesia:import_users( + sample_filename_and_data(plain, <<"user-credentials-plain-dup.csv">>), + State + ) + ), + Users1 = [ + #{ + <<"user_id">> => <<"myuser5">>, + <<"password">> => <<"password5">>, + <<"is_superuser">> => true + }, + #{ + <<"user_id">> => <<"myuser5">>, + <<"password">> => <<"password6">>, + <<"is_superuser">> => false + } + ], + ?assertEqual( + ok, + emqx_authn_mnesia:import_users( + {plain, prepared_user_list, Users1}, + State + ) + ), + + %% assert: the last record overwrites the previous one + ?assertMatch( + [ + {user_info, {_, <<"myuser1">>}, <<"password2">>, _, false}, + {user_info, {_, <<"myuser3">>}, <<"password4">>, _, false}, + {user_info, {_, <<"myuser5">>}, <<"password6">>, _, false} + ], + ets:tab2list(emqx_authn_mnesia) + ). + %%------------------------------------------------------------------------------ %% Helpers %%------------------------------------------------------------------------------ From 4190682a167e611c217a745e373407fcfecb1ea2 Mon Sep 17 00:00:00 2001 From: JianBo He Date: Tue, 30 Jan 2024 15:29:46 +0800 Subject: [PATCH 2/2] chore: fix the data type and example value for cluster invitation result --- apps/emqx_management/src/emqx_mgmt_api_cluster.erl | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/emqx_management/src/emqx_mgmt_api_cluster.erl b/apps/emqx_management/src/emqx_mgmt_api_cluster.erl index 686a0be71..6a56ef454 100644 --- a/apps/emqx_management/src/emqx_mgmt_api_cluster.erl +++ b/apps/emqx_management/src/emqx_mgmt_api_cluster.erl @@ -206,11 +206,10 @@ fields(node_invitation_succeed) -> [ {finished_at, ?HOCON( - emqx_utils_calendar:epoch_millisecond(), + binary(), #{ - desc => - <<"The time of the async invitation result is received, millisecond precision epoch">>, - example => <<"1705044829915">> + desc => <<"The time of the async invitation result is received">>, + example => <<"2024-01-30T15:24:39.355+08:00">> } )} ]; @@ -223,11 +222,10 @@ fields(node_invitation_in_progress) -> )}, {started_at, ?HOCON( - emqx_utils_calendar:epoch_millisecond(), + binary(), #{ - desc => - <<"The start timestamp of the invitation, millisecond precision epoch">>, - example => <<"1705044829915">> + desc => <<"The time of the async invitation is started">>, + example => <<"2024-01-30T15:24:39.355+08:00">> } )} ].