feat: support bootstrap_file on build-in-db authn
This commit is contained in:
parent
d8963c836e
commit
5265c3cc1f
|
@ -1,7 +1,7 @@
|
||||||
%% -*- mode: erlang -*-
|
%% -*- mode: erlang -*-
|
||||||
{application, emqx_auth_mnesia, [
|
{application, emqx_auth_mnesia, [
|
||||||
{description, "EMQX Buitl-in Database Authentication and Authorization"},
|
{description, "EMQX Buitl-in Database Authentication and Authorization"},
|
||||||
{vsn, "0.1.5"},
|
{vsn, "0.1.6"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{mod, {emqx_auth_mnesia_app, []}},
|
{mod, {emqx_auth_mnesia_app, []}},
|
||||||
{applications, [
|
{applications, [
|
||||||
|
|
|
@ -116,7 +116,7 @@ create(
|
||||||
user_id_type := Type,
|
user_id_type := Type,
|
||||||
password_hash_algorithm := Algorithm,
|
password_hash_algorithm := Algorithm,
|
||||||
user_group := UserGroup
|
user_group := UserGroup
|
||||||
}
|
} = Config
|
||||||
) ->
|
) ->
|
||||||
ok = emqx_authn_password_hashing:init(Algorithm),
|
ok = emqx_authn_password_hashing:init(Algorithm),
|
||||||
State = #{
|
State = #{
|
||||||
|
@ -124,6 +124,7 @@ create(
|
||||||
user_id_type => Type,
|
user_id_type => Type,
|
||||||
password_hash_algorithm => Algorithm
|
password_hash_algorithm => Algorithm
|
||||||
},
|
},
|
||||||
|
ok = boostrap_user_from_file(Config, State),
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
|
||||||
update(Config, _State) ->
|
update(Config, _State) ->
|
||||||
|
@ -537,3 +538,24 @@ find_password_hash(_, _, _) ->
|
||||||
is_superuser(#{<<"is_superuser">> := <<"true">>}) -> true;
|
is_superuser(#{<<"is_superuser">> := <<"true">>}) -> true;
|
||||||
is_superuser(#{<<"is_superuser">> := true}) -> true;
|
is_superuser(#{<<"is_superuser">> := true}) -> true;
|
||||||
is_superuser(_) -> false.
|
is_superuser(_) -> false.
|
||||||
|
|
||||||
|
boostrap_user_from_file(Config, State) ->
|
||||||
|
case maps:get(boostrap_file, Config, <<>>) of
|
||||||
|
<<>> ->
|
||||||
|
ok;
|
||||||
|
FileName ->
|
||||||
|
#{boostrap_type := Type} = Config,
|
||||||
|
case file:read_file(FileName) of
|
||||||
|
{ok, FileData} ->
|
||||||
|
%% if there is a key conflict, override with the key which from the bootstrap file
|
||||||
|
_ = import_users({Type, FileName, FileData}, State),
|
||||||
|
ok;
|
||||||
|
{error, Reason} ->
|
||||||
|
?SLOG(warning, #{
|
||||||
|
msg => "boostrap_authn(built_in_database)_failed",
|
||||||
|
boostrap_file => FileName,
|
||||||
|
boostrap_type => Type,
|
||||||
|
reason => Reason
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
|
@ -46,7 +46,7 @@ select_union_member(_Kind, _Value) ->
|
||||||
fields(builtin_db) ->
|
fields(builtin_db) ->
|
||||||
[
|
[
|
||||||
{password_hash_algorithm, fun emqx_authn_password_hashing:type_rw/1}
|
{password_hash_algorithm, fun emqx_authn_password_hashing:type_rw/1}
|
||||||
] ++ common_fields();
|
] ++ common_fields() ++ bootstrap_fields();
|
||||||
fields(builtin_db_api) ->
|
fields(builtin_db_api) ->
|
||||||
[
|
[
|
||||||
{password_hash_algorithm, fun emqx_authn_password_hashing:type_rw_api/1}
|
{password_hash_algorithm, fun emqx_authn_password_hashing:type_rw_api/1}
|
||||||
|
@ -69,3 +69,24 @@ common_fields() ->
|
||||||
{backend, emqx_authn_schema:backend(?AUTHN_BACKEND)},
|
{backend, emqx_authn_schema:backend(?AUTHN_BACKEND)},
|
||||||
{user_id_type, fun user_id_type/1}
|
{user_id_type, fun user_id_type/1}
|
||||||
] ++ emqx_authn_schema:common_fields().
|
] ++ emqx_authn_schema:common_fields().
|
||||||
|
|
||||||
|
bootstrap_fields() ->
|
||||||
|
[
|
||||||
|
{bootstrap_file,
|
||||||
|
?HOCON(
|
||||||
|
binary(),
|
||||||
|
#{
|
||||||
|
desc => ?DESC(bootstrap_file),
|
||||||
|
required => false,
|
||||||
|
default => <<>>
|
||||||
|
}
|
||||||
|
)},
|
||||||
|
{bootstrap_type,
|
||||||
|
?HOCON(
|
||||||
|
?ENUM([hash, plain]), #{
|
||||||
|
desc => ?DESC(bootstrap_type),
|
||||||
|
required => false,
|
||||||
|
default => <<"plain">>
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
].
|
||||||
|
|
|
@ -54,7 +54,74 @@ t_create(_) ->
|
||||||
{ok, _} = emqx_authn_mnesia:create(?AUTHN_ID, Config0),
|
{ok, _} = emqx_authn_mnesia:create(?AUTHN_ID, Config0),
|
||||||
|
|
||||||
Config1 = Config0#{password_hash_algorithm => #{name => sha256}},
|
Config1 = Config0#{password_hash_algorithm => #{name => sha256}},
|
||||||
{ok, _} = emqx_authn_mnesia:create(?AUTHN_ID, Config1).
|
{ok, _} = emqx_authn_mnesia:create(?AUTHN_ID, Config1),
|
||||||
|
ok.
|
||||||
|
t_bootstrap_file(_) ->
|
||||||
|
Config = config(),
|
||||||
|
%% hash to hash
|
||||||
|
HashConfig = Config#{password_hash_algorithm => #{name => sha256, salt_position => suffix}},
|
||||||
|
?assertMatch(
|
||||||
|
[
|
||||||
|
{user_info, {_, <<"myuser1">>}, _, _, true},
|
||||||
|
{user_info, {_, <<"myuser2">>}, _, _, false}
|
||||||
|
],
|
||||||
|
test_bootstrap_file(HashConfig, hash, <<"user-credentials.json">>)
|
||||||
|
),
|
||||||
|
?assertMatch(
|
||||||
|
[
|
||||||
|
{user_info, {_, <<"myuser3">>}, _, _, true},
|
||||||
|
{user_info, {_, <<"myuser4">>}, _, _, false}
|
||||||
|
],
|
||||||
|
test_bootstrap_file(HashConfig, hash, <<"user-credentials.csv">>)
|
||||||
|
),
|
||||||
|
|
||||||
|
%% plain to plain
|
||||||
|
PlainConfig = Config#{
|
||||||
|
password_hash_algorithm =>
|
||||||
|
#{name => plain, salt_position => disable}
|
||||||
|
},
|
||||||
|
?assertMatch(
|
||||||
|
[
|
||||||
|
{user_info, {_, <<"myuser1">>}, <<"password1">>, _, true},
|
||||||
|
{user_info, {_, <<"myuser2">>}, <<"password2">>, _, false}
|
||||||
|
],
|
||||||
|
test_bootstrap_file(PlainConfig, plain, <<"user-credentials-plain.json">>)
|
||||||
|
),
|
||||||
|
?assertMatch(
|
||||||
|
[
|
||||||
|
{user_info, {_, <<"myuser3">>}, <<"password3">>, _, true},
|
||||||
|
{user_info, {_, <<"myuser4">>}, <<"password4">>, _, false}
|
||||||
|
],
|
||||||
|
test_bootstrap_file(PlainConfig, plain, <<"user-credentials-plain.csv">>)
|
||||||
|
),
|
||||||
|
%% plain to hash
|
||||||
|
?assertMatch(
|
||||||
|
[
|
||||||
|
{user_info, {_, <<"myuser1">>}, _, _, true},
|
||||||
|
{user_info, {_, <<"myuser2">>}, _, _, false}
|
||||||
|
],
|
||||||
|
test_bootstrap_file(HashConfig, plain, <<"user-credentials-plain.json">>)
|
||||||
|
),
|
||||||
|
?assertMatch(
|
||||||
|
[
|
||||||
|
{user_info, {_, <<"myuser3">>}, _, _, true},
|
||||||
|
{user_info, {_, <<"myuser4">>}, _, _, false}
|
||||||
|
],
|
||||||
|
test_bootstrap_file(HashConfig, plain, <<"user-credentials-plain.csv">>)
|
||||||
|
),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
test_bootstrap_file(Config0, Type, File) ->
|
||||||
|
{Type, Filename, _FileData} = sample_filename_and_data(Type, File),
|
||||||
|
Config2 = Config0#{
|
||||||
|
boostrap_file => Filename,
|
||||||
|
boostrap_type => Type
|
||||||
|
},
|
||||||
|
{ok, State0} = emqx_authn_mnesia:create(?AUTHN_ID, Config2),
|
||||||
|
Result = ets:tab2list(emqx_authn_mnesia),
|
||||||
|
ok = emqx_authn_mnesia:destroy(State0),
|
||||||
|
?assertMatch([], ets:tab2list(emqx_authn_mnesia)),
|
||||||
|
Result.
|
||||||
|
|
||||||
t_update(_) ->
|
t_update(_) ->
|
||||||
Config0 = config(),
|
Config0 = config(),
|
||||||
|
|
|
@ -9,4 +9,14 @@ user_id_type.desc:
|
||||||
user_id_type.label:
|
user_id_type.label:
|
||||||
"""Authentication ID Type"""
|
"""Authentication ID Type"""
|
||||||
|
|
||||||
|
bootstrap_file.desc:
|
||||||
|
"""The bootstrap file imports users into the built-in database."""
|
||||||
|
|
||||||
|
bootstrap_file.label:
|
||||||
|
"""Bootstrap File Path"""
|
||||||
|
|
||||||
|
bootstrap_type.desc:
|
||||||
|
"""plain: bootstrap_file.cvs should be `user_id,password,is_superuser`.
|
||||||
|
hash: bootstrap_file.cvs should be `user_id,password_hash,salt,is_superuser`"""
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue