Merge pull request #10783 from zhongwencool/improve-authn
feat: load changes to the authentication configuration from command line
This commit is contained in:
commit
dd85c981cd
|
@ -20,6 +20,7 @@
|
|||
-include_lib("emqx/include/logger.hrl").
|
||||
|
||||
-define(AUTHN_TRACE_TAG, "AUTHN").
|
||||
-define(GLOBAL, 'mqtt:global').
|
||||
|
||||
-define(TRACE_AUTHN_PROVIDER(Msg), ?TRACE_AUTHN_PROVIDER(Msg, #{})).
|
||||
-define(TRACE_AUTHN_PROVIDER(Msg, Meta), ?TRACE_AUTHN_PROVIDER(debug, Msg, Meta)).
|
||||
|
|
|
@ -60,7 +60,8 @@
|
|||
update_authenticator/3,
|
||||
lookup_authenticator/2,
|
||||
list_authenticators/1,
|
||||
move_authenticator/3
|
||||
move_authenticator/3,
|
||||
reorder_authenticator/2
|
||||
]).
|
||||
|
||||
%% APIs for observer built_in_database
|
||||
|
@ -86,12 +87,6 @@
|
|||
%% utility functions
|
||||
-export([authenticator_id/1, metrics_id/2]).
|
||||
|
||||
%% proxy callback
|
||||
-export([
|
||||
pre_config_update/3,
|
||||
post_config_update/5
|
||||
]).
|
||||
|
||||
-export_type([
|
||||
authenticator_id/0,
|
||||
position/0,
|
||||
|
@ -275,12 +270,6 @@ get_enabled(Authenticators) ->
|
|||
%% APIs
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
pre_config_update(Path, UpdateReq, OldConfig) ->
|
||||
emqx_authentication_config:pre_config_update(Path, UpdateReq, OldConfig).
|
||||
|
||||
post_config_update(Path, UpdateReq, NewConfig, OldConfig, AppEnvs) ->
|
||||
emqx_authentication_config:post_config_update(Path, UpdateReq, NewConfig, OldConfig, AppEnvs).
|
||||
|
||||
%% @doc Get all registered authentication providers.
|
||||
get_providers() ->
|
||||
call(get_providers).
|
||||
|
@ -413,6 +402,12 @@ list_authenticators(ChainName) ->
|
|||
move_authenticator(ChainName, AuthenticatorID, Position) ->
|
||||
call({move_authenticator, ChainName, AuthenticatorID, Position}).
|
||||
|
||||
-spec reorder_authenticator(chain_name(), [authenticator_id()]) -> ok.
|
||||
reorder_authenticator(_ChainName, []) ->
|
||||
ok;
|
||||
reorder_authenticator(ChainName, AuthenticatorIDs) ->
|
||||
call({reorder_authenticator, ChainName, AuthenticatorIDs}).
|
||||
|
||||
-spec import_users(chain_name(), authenticator_id(), {binary(), binary()}) ->
|
||||
ok | {error, term()}.
|
||||
import_users(ChainName, AuthenticatorID, Filename) ->
|
||||
|
@ -447,8 +442,9 @@ list_users(ChainName, AuthenticatorID, FuzzyParams) ->
|
|||
|
||||
init(_Opts) ->
|
||||
process_flag(trap_exit, true),
|
||||
ok = emqx_config_handler:add_handler([?CONF_ROOT], ?MODULE),
|
||||
ok = emqx_config_handler:add_handler([listeners, '?', '?', ?CONF_ROOT], ?MODULE),
|
||||
Module = emqx_authentication_config,
|
||||
ok = emqx_config_handler:add_handler([?CONF_ROOT], Module),
|
||||
ok = emqx_config_handler:add_handler([listeners, '?', '?', ?CONF_ROOT], Module),
|
||||
{ok, #{hooked => false, providers => #{}}}.
|
||||
|
||||
handle_call(get_providers, _From, #{providers := Providers} = State) ->
|
||||
|
@ -504,6 +500,12 @@ handle_call({move_authenticator, ChainName, AuthenticatorID, Position}, _From, S
|
|||
end,
|
||||
Reply = with_chain(ChainName, UpdateFun),
|
||||
reply(Reply, State);
|
||||
handle_call({reorder_authenticator, ChainName, AuthenticatorIDs}, _From, State) ->
|
||||
UpdateFun = fun(Chain) ->
|
||||
handle_reorder_authenticator(Chain, AuthenticatorIDs)
|
||||
end,
|
||||
Reply = with_chain(ChainName, UpdateFun),
|
||||
reply(Reply, State);
|
||||
handle_call({import_users, ChainName, AuthenticatorID, Filename}, _From, State) ->
|
||||
Reply = call_authenticator(ChainName, AuthenticatorID, import_users, [Filename]),
|
||||
reply(Reply, State);
|
||||
|
@ -609,6 +611,24 @@ handle_move_authenticator(Chain, AuthenticatorID, Position) ->
|
|||
{error, Reason}
|
||||
end.
|
||||
|
||||
handle_reorder_authenticator(Chain, AuthenticatorIDs) ->
|
||||
#chain{authenticators = Authenticators} = Chain,
|
||||
NAuthenticators =
|
||||
lists:filtermap(
|
||||
fun(ID) ->
|
||||
case lists:keyfind(ID, #authenticator.id, Authenticators) of
|
||||
false ->
|
||||
?SLOG(error, #{msg => "authenticator_not_found", id => ID}),
|
||||
false;
|
||||
Authenticator ->
|
||||
{true, Authenticator}
|
||||
end
|
||||
end,
|
||||
AuthenticatorIDs
|
||||
),
|
||||
NewChain = Chain#chain{authenticators = NAuthenticators},
|
||||
{ok, ok, NewChain}.
|
||||
|
||||
handle_create_authenticator(Chain, Config, Providers) ->
|
||||
#chain{name = Name, authenticators = Authenticators} = Chain,
|
||||
AuthenticatorID = authenticator_id(Config),
|
||||
|
|
|
@ -65,8 +65,8 @@
|
|||
|
||||
-spec pre_config_update(list(atom()), update_request(), emqx_config:raw_config()) ->
|
||||
{ok, map() | list()} | {error, term()}.
|
||||
pre_config_update(_, UpdateReq, OldConfig) ->
|
||||
try do_pre_config_update(UpdateReq, to_list(OldConfig)) of
|
||||
pre_config_update(Paths, UpdateReq, OldConfig) ->
|
||||
try do_pre_config_update(Paths, UpdateReq, to_list(OldConfig)) of
|
||||
{error, Reason} -> {error, Reason};
|
||||
{ok, NewConfig} -> {ok, NewConfig}
|
||||
catch
|
||||
|
@ -74,9 +74,9 @@ pre_config_update(_, UpdateReq, OldConfig) ->
|
|||
{error, Reason}
|
||||
end.
|
||||
|
||||
do_pre_config_update({create_authenticator, ChainName, Config}, OldConfig) ->
|
||||
do_pre_config_update(_, {create_authenticator, ChainName, Config}, OldConfig) ->
|
||||
NewId = authenticator_id(Config),
|
||||
case lists:filter(fun(OldConfig0) -> authenticator_id(OldConfig0) =:= NewId end, OldConfig) of
|
||||
case filter_authenticator(NewId, OldConfig) of
|
||||
[] ->
|
||||
CertsDir = certs_dir(ChainName, Config),
|
||||
NConfig = convert_certs(CertsDir, Config),
|
||||
|
@ -84,7 +84,7 @@ do_pre_config_update({create_authenticator, ChainName, Config}, OldConfig) ->
|
|||
[_] ->
|
||||
{error, {already_exists, {authenticator, NewId}}}
|
||||
end;
|
||||
do_pre_config_update({delete_authenticator, _ChainName, AuthenticatorID}, OldConfig) ->
|
||||
do_pre_config_update(_, {delete_authenticator, _ChainName, AuthenticatorID}, OldConfig) ->
|
||||
NewConfig = lists:filter(
|
||||
fun(OldConfig0) ->
|
||||
AuthenticatorID =/= authenticator_id(OldConfig0)
|
||||
|
@ -92,7 +92,7 @@ do_pre_config_update({delete_authenticator, _ChainName, AuthenticatorID}, OldCon
|
|||
OldConfig
|
||||
),
|
||||
{ok, NewConfig};
|
||||
do_pre_config_update({update_authenticator, ChainName, AuthenticatorID, Config}, OldConfig) ->
|
||||
do_pre_config_update(_, {update_authenticator, ChainName, AuthenticatorID, Config}, OldConfig) ->
|
||||
CertsDir = certs_dir(ChainName, AuthenticatorID),
|
||||
NewConfig = lists:map(
|
||||
fun(OldConfig0) ->
|
||||
|
@ -104,7 +104,7 @@ do_pre_config_update({update_authenticator, ChainName, AuthenticatorID, Config},
|
|||
OldConfig
|
||||
),
|
||||
{ok, NewConfig};
|
||||
do_pre_config_update({move_authenticator, _ChainName, AuthenticatorID, Position}, OldConfig) ->
|
||||
do_pre_config_update(_, {move_authenticator, _ChainName, AuthenticatorID, Position}, OldConfig) ->
|
||||
case split_by_id(AuthenticatorID, OldConfig) of
|
||||
{error, Reason} ->
|
||||
{error, Reason};
|
||||
|
@ -129,7 +129,18 @@ do_pre_config_update({move_authenticator, _ChainName, AuthenticatorID, Position}
|
|||
{ok, BeforeNFound ++ [FoundRelated, Found | AfterNFound]}
|
||||
end
|
||||
end
|
||||
end.
|
||||
end;
|
||||
do_pre_config_update(_, OldConfig, OldConfig) ->
|
||||
{ok, OldConfig};
|
||||
do_pre_config_update(Paths, NewConfig, _OldConfig) ->
|
||||
ChainName = chain_name(Paths),
|
||||
{ok, [
|
||||
begin
|
||||
CertsDir = certs_dir(ChainName, New),
|
||||
convert_certs(CertsDir, New)
|
||||
end
|
||||
|| New <- to_list(NewConfig)
|
||||
]}.
|
||||
|
||||
-spec post_config_update(
|
||||
list(atom()),
|
||||
|
@ -139,13 +150,16 @@ do_pre_config_update({move_authenticator, _ChainName, AuthenticatorID, Position}
|
|||
emqx_config:app_envs()
|
||||
) ->
|
||||
ok | {ok, map()} | {error, term()}.
|
||||
post_config_update(_, UpdateReq, NewConfig, OldConfig, AppEnvs) ->
|
||||
do_post_config_update(UpdateReq, to_list(NewConfig), OldConfig, AppEnvs).
|
||||
post_config_update(Paths, UpdateReq, NewConfig, OldConfig, AppEnvs) ->
|
||||
do_post_config_update(Paths, UpdateReq, to_list(NewConfig), OldConfig, AppEnvs).
|
||||
|
||||
do_post_config_update({create_authenticator, ChainName, Config}, NewConfig, _OldConfig, _AppEnvs) ->
|
||||
do_post_config_update(
|
||||
_, {create_authenticator, ChainName, Config}, NewConfig, _OldConfig, _AppEnvs
|
||||
) ->
|
||||
NConfig = get_authenticator_config(authenticator_id(Config), NewConfig),
|
||||
emqx_authentication:create_authenticator(ChainName, NConfig);
|
||||
do_post_config_update(
|
||||
_,
|
||||
{delete_authenticator, ChainName, AuthenticatorID},
|
||||
_NewConfig,
|
||||
OldConfig,
|
||||
|
@ -160,6 +174,7 @@ do_post_config_update(
|
|||
{error, Reason}
|
||||
end;
|
||||
do_post_config_update(
|
||||
_,
|
||||
{update_authenticator, ChainName, AuthenticatorID, Config},
|
||||
NewConfig,
|
||||
_OldConfig,
|
||||
|
@ -172,12 +187,57 @@ do_post_config_update(
|
|||
emqx_authentication:update_authenticator(ChainName, AuthenticatorID, NConfig)
|
||||
end;
|
||||
do_post_config_update(
|
||||
_,
|
||||
{move_authenticator, ChainName, AuthenticatorID, Position},
|
||||
_NewConfig,
|
||||
_OldConfig,
|
||||
_AppEnvs
|
||||
) ->
|
||||
emqx_authentication:move_authenticator(ChainName, AuthenticatorID, Position).
|
||||
emqx_authentication:move_authenticator(ChainName, AuthenticatorID, Position);
|
||||
do_post_config_update(_, _UpdateReq, OldConfig, OldConfig, _AppEnvs) ->
|
||||
ok;
|
||||
do_post_config_update(Paths, _UpdateReq, NewConfig0, OldConfig0, _AppEnvs) ->
|
||||
ChainName = chain_name(Paths),
|
||||
OldConfig = to_list(OldConfig0),
|
||||
NewConfig = to_list(NewConfig0),
|
||||
OldIds = lists:map(fun authenticator_id/1, OldConfig),
|
||||
NewIds = lists:map(fun authenticator_id/1, NewConfig),
|
||||
ok = delete_authenticators(NewIds, ChainName, OldConfig),
|
||||
ok = create_or_update_authenticators(OldIds, ChainName, NewConfig),
|
||||
ok = emqx_authentication:reorder_authenticator(ChainName, NewIds),
|
||||
ok.
|
||||
|
||||
%% create new authenticators and update existing ones
|
||||
create_or_update_authenticators(OldIds, ChainName, NewConfig) ->
|
||||
lists:foreach(
|
||||
fun(Conf) ->
|
||||
Id = authenticator_id(Conf),
|
||||
case lists:member(Id, OldIds) of
|
||||
true ->
|
||||
emqx_authentication:update_authenticator(ChainName, Id, Conf);
|
||||
false ->
|
||||
emqx_authentication:create_authenticator(ChainName, Conf)
|
||||
end
|
||||
end,
|
||||
NewConfig
|
||||
).
|
||||
|
||||
%% delete authenticators that are not in the new config
|
||||
delete_authenticators(NewIds, ChainName, OldConfig) ->
|
||||
lists:foreach(
|
||||
fun(Conf) ->
|
||||
Id = authenticator_id(Conf),
|
||||
case lists:member(Id, NewIds) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
_ = emqx_authentication:delete_authenticator(ChainName, Id),
|
||||
CertsDir = certs_dir(ChainName, Conf),
|
||||
ok = clear_certs(CertsDir, Conf)
|
||||
end
|
||||
end,
|
||||
OldConfig
|
||||
).
|
||||
|
||||
to_list(undefined) -> [];
|
||||
to_list(M) when M =:= #{} -> [];
|
||||
|
@ -213,14 +273,15 @@ clear_certs(CertsDir, Config) ->
|
|||
ok = emqx_tls_lib:delete_ssl_files(CertsDir, undefined, OldSSL).
|
||||
|
||||
get_authenticator_config(AuthenticatorID, AuthenticatorsConfig) ->
|
||||
case
|
||||
lists:filter(fun(C) -> AuthenticatorID =:= authenticator_id(C) end, AuthenticatorsConfig)
|
||||
of
|
||||
case filter_authenticator(AuthenticatorID, AuthenticatorsConfig) of
|
||||
[C] -> C;
|
||||
[] -> {error, not_found};
|
||||
_ -> error({duplicated_authenticator_id, AuthenticatorsConfig})
|
||||
end.
|
||||
|
||||
filter_authenticator(ID, Authenticators) ->
|
||||
lists:filter(fun(A) -> ID =:= authenticator_id(A) end, Authenticators).
|
||||
|
||||
split_by_id(ID, AuthenticatorsConfig) ->
|
||||
case
|
||||
lists:foldl(
|
||||
|
@ -287,3 +348,8 @@ dir(ChainName, ID) when is_binary(ID) ->
|
|||
emqx_utils:safe_filename(iolist_to_binary([to_bin(ChainName), "-", ID]));
|
||||
dir(ChainName, Config) when is_map(Config) ->
|
||||
dir(ChainName, authenticator_id(Config)).
|
||||
|
||||
chain_name([authentication]) ->
|
||||
?GLOBAL;
|
||||
chain_name([listeners, Type, Name, authentication]) ->
|
||||
binary_to_existing_atom(<<(atom_to_binary(Type))/binary, ":", (atom_to_binary(Name))/binary>>).
|
||||
|
|
|
@ -288,12 +288,14 @@ get_default_value([RootName | _] = KeyPath) ->
|
|||
end.
|
||||
|
||||
-spec get_raw(emqx_utils_maps:config_key_path()) -> term().
|
||||
get_raw([Root | T]) when is_atom(Root) -> get_raw([bin(Root) | T]);
|
||||
get_raw(KeyPath) -> do_get_raw(KeyPath).
|
||||
get_raw([Root | _] = KeyPath) when is_binary(Root) -> do_get_raw(KeyPath);
|
||||
get_raw([Root | T]) -> get_raw([bin(Root) | T]);
|
||||
get_raw([]) -> do_get_raw([]).
|
||||
|
||||
-spec get_raw(emqx_utils_maps:config_key_path(), term()) -> term().
|
||||
get_raw([Root | T], Default) when is_atom(Root) -> get_raw([bin(Root) | T], Default);
|
||||
get_raw(KeyPath, Default) -> do_get_raw(KeyPath, Default).
|
||||
get_raw([Root | _] = KeyPath, Default) when is_binary(Root) -> do_get_raw(KeyPath, Default);
|
||||
get_raw([Root | T], Default) -> get_raw([bin(Root) | T], Default);
|
||||
get_raw([], Default) -> do_get_raw([], Default).
|
||||
|
||||
-spec put_raw(map()) -> ok.
|
||||
put_raw(Config) ->
|
||||
|
|
|
@ -174,7 +174,7 @@ t_authenticator(Config) when is_list(Config) ->
|
|||
register_provider(AuthNType1, ?MODULE),
|
||||
ID1 = <<"password_based:built_in_database">>,
|
||||
|
||||
% CRUD of authencaticator
|
||||
% CRUD of authenticator
|
||||
?assertMatch(
|
||||
{ok, #{id := ID1, state := #{mark := 1}}},
|
||||
?AUTHN:create_authenticator(ChainName, AuthenticatorConfig1)
|
||||
|
@ -296,8 +296,10 @@ t_update_config({init, Config}) ->
|
|||
| Config
|
||||
];
|
||||
t_update_config(Config) when is_list(Config) ->
|
||||
emqx_config_handler:add_handler([?CONF_ROOT], emqx_authentication),
|
||||
ok = emqx_config_handler:add_handler([listeners, '?', '?', ?CONF_ROOT], emqx_authentication),
|
||||
emqx_config_handler:add_handler([?CONF_ROOT], emqx_authentication_config),
|
||||
ok = emqx_config_handler:add_handler(
|
||||
[listeners, '?', '?', ?CONF_ROOT], emqx_authentication_config
|
||||
),
|
||||
ok = register_provider(?config("auth1"), ?MODULE),
|
||||
ok = register_provider(?config("auth2"), ?MODULE),
|
||||
Global = ?config(global),
|
||||
|
@ -356,6 +358,10 @@ t_update_config(Config) when is_list(Config) ->
|
|||
|
||||
?assertMatch({ok, [#{id := ID2}, #{id := ID1}]}, ?AUTHN:list_authenticators(Global)),
|
||||
|
||||
[Raw2, Raw1] = emqx:get_raw_config([?CONF_ROOT]),
|
||||
?assertMatch({ok, _}, update_config([?CONF_ROOT], [Raw1, Raw2])),
|
||||
?assertMatch({ok, [#{id := ID1}, #{id := ID2}]}, ?AUTHN:list_authenticators(Global)),
|
||||
|
||||
?assertMatch({ok, _}, update_config([?CONF_ROOT], {delete_authenticator, Global, ID1})),
|
||||
?assertEqual(
|
||||
{error, {not_found, {authenticator, ID1}}},
|
||||
|
@ -418,11 +424,16 @@ t_update_config(Config) when is_list(Config) ->
|
|||
{ok, _},
|
||||
update_config(ConfKeyPath, {move_authenticator, ListenerID, ID2, ?CMD_MOVE_FRONT})
|
||||
),
|
||||
|
||||
?assertMatch(
|
||||
{ok, [#{id := ID2}, #{id := ID1}]},
|
||||
?AUTHN:list_authenticators(ListenerID)
|
||||
),
|
||||
[LRaw2, LRaw1] = emqx:get_raw_config(ConfKeyPath),
|
||||
?assertMatch({ok, _}, update_config(ConfKeyPath, [LRaw1, LRaw2])),
|
||||
?assertMatch(
|
||||
{ok, [#{id := ID1}, #{id := ID2}]},
|
||||
?AUTHN:list_authenticators(ListenerID)
|
||||
),
|
||||
|
||||
?assertMatch(
|
||||
{ok, _},
|
||||
|
|
|
@ -23,8 +23,6 @@
|
|||
|
||||
-define(AUTHN, emqx_authentication).
|
||||
|
||||
-define(GLOBAL, 'mqtt:global').
|
||||
|
||||
-define(RE_PLACEHOLDER, "\\$\\{[a-z0-9\\-]+\\}").
|
||||
|
||||
-define(AUTH_SHARD, emqx_authn_shard).
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
%% -*- mode: erlang -*-
|
||||
{application, emqx_authn, [
|
||||
{description, "EMQX Authentication"},
|
||||
{vsn, "0.1.20"},
|
||||
{vsn, "0.1.21"},
|
||||
{modules, []},
|
||||
{registered, [emqx_authn_sup, emqx_authn_registry]},
|
||||
{applications, [kernel, stdlib, emqx_resource, emqx_connector, ehttpc, epgsql, mysql, jose]},
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
-define(NOT_FOUND, 'NOT_FOUND').
|
||||
-define(ALREADY_EXISTS, 'ALREADY_EXISTS').
|
||||
-define(INTERNAL_ERROR, 'INTERNAL_ERROR').
|
||||
-define(CONFIG, emqx_authentication_config).
|
||||
|
||||
% Swagger
|
||||
|
||||
|
@ -833,12 +834,12 @@ with_chain(ListenerID, Fun) ->
|
|||
create_authenticator(ConfKeyPath, ChainName, Config) ->
|
||||
case update_config(ConfKeyPath, {create_authenticator, ChainName, Config}) of
|
||||
{ok, #{
|
||||
post_config_update := #{emqx_authentication := #{id := ID}},
|
||||
post_config_update := #{?CONFIG := #{id := ID}},
|
||||
raw_config := AuthenticatorsConfig
|
||||
}} ->
|
||||
{ok, AuthenticatorConfig} = find_config(ID, AuthenticatorsConfig),
|
||||
{200, maps:put(id, ID, convert_certs(fill_defaults(AuthenticatorConfig)))};
|
||||
{error, {_PrePostConfigUpdate, emqx_authentication, Reason}} ->
|
||||
{error, {_PrePostConfigUpdate, ?CONFIG, Reason}} ->
|
||||
serialize_error(Reason);
|
||||
{error, Reason} ->
|
||||
serialize_error(Reason)
|
||||
|
@ -1017,7 +1018,7 @@ update_authenticator(ConfKeyPath, ChainName, AuthenticatorID, Config) ->
|
|||
of
|
||||
{ok, _} ->
|
||||
{204};
|
||||
{error, {_PrePostConfigUpdate, emqx_authentication, Reason}} ->
|
||||
{error, {_PrePostConfigUpdate, ?CONFIG, Reason}} ->
|
||||
serialize_error(Reason);
|
||||
{error, Reason} ->
|
||||
serialize_error(Reason)
|
||||
|
@ -1027,7 +1028,7 @@ delete_authenticator(ConfKeyPath, ChainName, AuthenticatorID) ->
|
|||
case update_config(ConfKeyPath, {delete_authenticator, ChainName, AuthenticatorID}) of
|
||||
{ok, _} ->
|
||||
{204};
|
||||
{error, {_PrePostConfigUpdate, emqx_authentication, Reason}} ->
|
||||
{error, {_PrePostConfigUpdate, ?CONFIG, Reason}} ->
|
||||
serialize_error(Reason);
|
||||
{error, Reason} ->
|
||||
serialize_error(Reason)
|
||||
|
@ -1044,7 +1045,7 @@ move_authenticator(ConfKeyPath, ChainName, AuthenticatorID, Position) ->
|
|||
of
|
||||
{ok, _} ->
|
||||
{204};
|
||||
{error, {_PrePostConfigUpdate, emqx_authentication, Reason}} ->
|
||||
{error, {_PrePostConfigUpdate, ?CONFIG, Reason}} ->
|
||||
serialize_error(Reason);
|
||||
{error, Reason} ->
|
||||
serialize_error(Reason)
|
||||
|
|
|
@ -200,6 +200,127 @@ t_union_selector_errors(Config) when is_list(Config) ->
|
|||
),
|
||||
ok.
|
||||
|
||||
t_update_conf({init, Config}) ->
|
||||
emqx_common_test_helpers:start_apps([emqx_conf, emqx_authn]),
|
||||
{ok, _} = emqx:update_config([authentication], []),
|
||||
Config;
|
||||
t_update_conf({'end', _Config}) ->
|
||||
{ok, _} = emqx:update_config([authentication], []),
|
||||
emqx_common_test_helpers:stop_apps([emqx_authn, emqx_conf]),
|
||||
ok;
|
||||
t_update_conf(Config) when is_list(Config) ->
|
||||
Authn1 = #{
|
||||
<<"mechanism">> => <<"password_based">>,
|
||||
<<"backend">> => <<"built_in_database">>,
|
||||
<<"user_id_type">> => <<"clientid">>,
|
||||
<<"enable">> => true
|
||||
},
|
||||
Authn2 = #{
|
||||
<<"mechanism">> => <<"password_based">>,
|
||||
<<"backend">> => <<"http">>,
|
||||
<<"method">> => <<"post">>,
|
||||
<<"url">> => <<"http://127.0.0.1:18083">>,
|
||||
<<"headers">> => #{
|
||||
<<"content-type">> => <<"application/json">>
|
||||
},
|
||||
<<"enable">> => true
|
||||
},
|
||||
Authn3 = #{
|
||||
<<"mechanism">> => <<"jwt">>,
|
||||
<<"use_jwks">> => false,
|
||||
<<"algorithm">> => <<"hmac-based">>,
|
||||
<<"secret">> => <<"mysecret">>,
|
||||
<<"secret_base64_encoded">> => false,
|
||||
<<"verify_claims">> => #{<<"username">> => <<"${username}">>},
|
||||
<<"enable">> => true
|
||||
},
|
||||
Chain = 'mqtt:global',
|
||||
{ok, _} = emqx:update_config([authentication], [Authn1]),
|
||||
?assertMatch(
|
||||
{ok, #{
|
||||
authenticators := [
|
||||
#{
|
||||
enable := true,
|
||||
id := <<"password_based:built_in_database">>,
|
||||
provider := emqx_authn_mnesia
|
||||
}
|
||||
]
|
||||
}},
|
||||
emqx_authentication:lookup_chain(Chain)
|
||||
),
|
||||
|
||||
{ok, _} = emqx:update_config([authentication], [Authn1, Authn2, Authn3]),
|
||||
?assertMatch(
|
||||
{ok, #{
|
||||
authenticators := [
|
||||
#{
|
||||
enable := true,
|
||||
id := <<"password_based:built_in_database">>,
|
||||
provider := emqx_authn_mnesia
|
||||
},
|
||||
#{
|
||||
enable := true,
|
||||
id := <<"password_based:http">>,
|
||||
provider := emqx_authn_http
|
||||
},
|
||||
#{
|
||||
enable := true,
|
||||
id := <<"jwt">>,
|
||||
provider := emqx_authn_jwt
|
||||
}
|
||||
]
|
||||
}},
|
||||
emqx_authentication:lookup_chain(Chain)
|
||||
),
|
||||
{ok, _} = emqx:update_config([authentication], [Authn2, Authn1]),
|
||||
?assertMatch(
|
||||
{ok, #{
|
||||
authenticators := [
|
||||
#{
|
||||
enable := true,
|
||||
id := <<"password_based:http">>,
|
||||
provider := emqx_authn_http
|
||||
},
|
||||
#{
|
||||
enable := true,
|
||||
id := <<"password_based:built_in_database">>,
|
||||
provider := emqx_authn_mnesia
|
||||
}
|
||||
]
|
||||
}},
|
||||
emqx_authentication:lookup_chain(Chain)
|
||||
),
|
||||
|
||||
{ok, _} = emqx:update_config([authentication], [Authn3, Authn2, Authn1]),
|
||||
?assertMatch(
|
||||
{ok, #{
|
||||
authenticators := [
|
||||
#{
|
||||
enable := true,
|
||||
id := <<"jwt">>,
|
||||
provider := emqx_authn_jwt
|
||||
},
|
||||
#{
|
||||
enable := true,
|
||||
id := <<"password_based:http">>,
|
||||
provider := emqx_authn_http
|
||||
},
|
||||
#{
|
||||
enable := true,
|
||||
id := <<"password_based:built_in_database">>,
|
||||
provider := emqx_authn_mnesia
|
||||
}
|
||||
]
|
||||
}},
|
||||
emqx_authentication:lookup_chain(Chain)
|
||||
),
|
||||
{ok, _} = emqx:update_config([authentication], []),
|
||||
?assertMatch(
|
||||
{error, {not_found, {chain, Chain}}},
|
||||
emqx_authentication:lookup_chain(Chain)
|
||||
),
|
||||
ok.
|
||||
|
||||
parse(Bytes) ->
|
||||
{ok, Frame, <<>>, {none, _}} = emqx_frame:parse(Bytes),
|
||||
Frame.
|
||||
|
|
|
@ -18,16 +18,40 @@
|
|||
-export([
|
||||
load/0,
|
||||
admins/1,
|
||||
conf/1,
|
||||
unload/0
|
||||
]).
|
||||
|
||||
-define(CMD, cluster_call).
|
||||
-define(CLUSTER_CALL, cluster_call).
|
||||
-define(CONF, conf).
|
||||
|
||||
load() ->
|
||||
emqx_ctl:register_command(?CMD, {?MODULE, admins}, []).
|
||||
emqx_ctl:register_command(?CLUSTER_CALL, {?MODULE, admins}, []),
|
||||
emqx_ctl:register_command(?CONF, {?MODULE, conf}, []).
|
||||
|
||||
unload() ->
|
||||
emqx_ctl:unregister_command(?CMD).
|
||||
emqx_ctl:unregister_command(?CLUSTER_CALL),
|
||||
emqx_ctl:unregister_command(?CONF).
|
||||
|
||||
conf(["show", "--keys-only"]) ->
|
||||
print(emqx_config:get_root_names());
|
||||
conf(["show"]) ->
|
||||
print_hocon(get_config());
|
||||
conf(["show", Key]) ->
|
||||
print_hocon(get_config(Key));
|
||||
conf(["load", Path]) ->
|
||||
load_config(Path);
|
||||
conf(_) ->
|
||||
emqx_ctl:usage(
|
||||
[
|
||||
%% TODO add reload
|
||||
%{"conf reload", "reload etc/emqx.conf on local node"},
|
||||
{"conf show --keys-only", "print all keys"},
|
||||
{"conf show", "print all running configures"},
|
||||
{"conf show <key>", "print a specific configuration"},
|
||||
{"conf load <path>", "load a hocon file to all nodes"}
|
||||
]
|
||||
).
|
||||
|
||||
admins(["status"]) ->
|
||||
status();
|
||||
|
@ -43,7 +67,7 @@ admins(["skip", Node0]) ->
|
|||
status();
|
||||
admins(["tnxid", TnxId0]) ->
|
||||
TnxId = list_to_integer(TnxId0),
|
||||
emqx_ctl:print("~p~n", [emqx_cluster_rpc:query(TnxId)]);
|
||||
print(emqx_cluster_rpc:query(TnxId));
|
||||
admins(["fast_forward"]) ->
|
||||
status(),
|
||||
Nodes = mria:running_nodes(),
|
||||
|
@ -91,3 +115,30 @@ status() ->
|
|||
Status
|
||||
),
|
||||
emqx_ctl:print("-----------------------------------------------\n").
|
||||
|
||||
print(Json) ->
|
||||
emqx_ctl:print("~ts~n", [emqx_logger_jsonfmt:best_effort_json(Json)]).
|
||||
|
||||
print_hocon(Hocon) ->
|
||||
emqx_ctl:print("~ts~n", [hocon_pp:do(Hocon, #{})]).
|
||||
|
||||
get_config() -> emqx_config:fill_defaults(emqx:get_raw_config([])).
|
||||
get_config(Key) -> emqx_config:fill_defaults(#{Key => emqx:get_raw_config([Key])}).
|
||||
|
||||
-define(OPTIONS, #{rawconf_with_defaults => true, override_to => cluster}).
|
||||
load_config(Path) ->
|
||||
case hocon:files([Path]) of
|
||||
{ok, Conf} ->
|
||||
maps:foreach(
|
||||
fun(Key, Value) ->
|
||||
case emqx_conf:update([Key], Value, ?OPTIONS) of
|
||||
{ok, _} -> emqx_ctl:print("load ~ts ok~n", [Key]);
|
||||
{error, Reason} -> emqx_ctl:print("load ~ts failed: ~p~n", [Key, Reason])
|
||||
end
|
||||
end,
|
||||
Conf
|
||||
);
|
||||
{error, Reason} ->
|
||||
emqx_ctl:print("load ~ts failed~n~p~n", [Path, Reason]),
|
||||
{error, bad_hocon_file}
|
||||
end.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{application, emqx_ctl, [
|
||||
{description, "Backend for emqx_ctl script"},
|
||||
{vsn, "0.1.1"},
|
||||
{vsn, "0.1.2"},
|
||||
{registered, []},
|
||||
{mod, {emqx_ctl_app, []}},
|
||||
{applications, [
|
||||
|
|
|
@ -128,16 +128,21 @@ run_command(Cmd, Args) when is_atom(Cmd) ->
|
|||
}),
|
||||
{error, Reason}
|
||||
end;
|
||||
[] ->
|
||||
Error ->
|
||||
help(),
|
||||
{error, cmd_not_found}
|
||||
Error
|
||||
end.
|
||||
|
||||
-spec lookup_command(cmd()) -> [{module(), atom()}].
|
||||
lookup_command(Cmd) when is_atom(Cmd) ->
|
||||
case ets:match(?CMD_TAB, {{'_', Cmd}, '$1', '_'}) of
|
||||
[El] -> El;
|
||||
[] -> []
|
||||
case is_initialized() of
|
||||
true ->
|
||||
case ets:match(?CMD_TAB, {{'_', Cmd}, '$1', '_'}) of
|
||||
[El] -> El;
|
||||
[] -> {error, cmd_not_found}
|
||||
end;
|
||||
false ->
|
||||
{error, cmd_is_initializing}
|
||||
end.
|
||||
|
||||
-spec get_commands() -> list({cmd(), module(), atom()}).
|
||||
|
@ -145,18 +150,23 @@ get_commands() ->
|
|||
[{Cmd, M, F} || {{_Seq, Cmd}, {M, F}, _Opts} <- ets:tab2list(?CMD_TAB)].
|
||||
|
||||
help() ->
|
||||
case ets:tab2list(?CMD_TAB) of
|
||||
[] ->
|
||||
print("No commands available.~n");
|
||||
Cmds ->
|
||||
print("Usage: ~ts~n", ["emqx ctl"]),
|
||||
lists:foreach(
|
||||
fun({_, {Mod, Cmd}, _}) ->
|
||||
print("~110..-s~n", [""]),
|
||||
apply(Mod, Cmd, [usage])
|
||||
end,
|
||||
Cmds
|
||||
)
|
||||
case is_initialized() of
|
||||
true ->
|
||||
case ets:tab2list(?CMD_TAB) of
|
||||
[] ->
|
||||
print("No commands available.~n");
|
||||
Cmds ->
|
||||
print("Usage: ~ts~n", ["emqx ctl"]),
|
||||
lists:foreach(
|
||||
fun({_, {Mod, Cmd}, _}) ->
|
||||
print("~110..-s~n", [""]),
|
||||
apply(Mod, Cmd, [usage])
|
||||
end,
|
||||
Cmds
|
||||
)
|
||||
end;
|
||||
false ->
|
||||
print("Command table is initializing.~n")
|
||||
end.
|
||||
|
||||
-spec print(io:format()) -> ok.
|
||||
|
@ -279,3 +289,6 @@ safe_to_existing_atom(Str) ->
|
|||
_:badarg ->
|
||||
undefined
|
||||
end.
|
||||
|
||||
is_initialized() ->
|
||||
ets:info(?CMD_TAB) =/= undefined.
|
||||
|
|
|
@ -49,8 +49,8 @@ t_reg_unreg_command(_) ->
|
|||
emqx_ctl:unregister_command(cmd1),
|
||||
emqx_ctl:unregister_command(cmd2),
|
||||
ct:sleep(100),
|
||||
?assertEqual([], emqx_ctl:lookup_command(cmd1)),
|
||||
?assertEqual([], emqx_ctl:lookup_command(cmd2)),
|
||||
?assertEqual({error, cmd_not_found}, emqx_ctl:lookup_command(cmd1)),
|
||||
?assertEqual({error, cmd_not_found}, emqx_ctl:lookup_command(cmd2)),
|
||||
?assertEqual([], emqx_ctl:get_commands())
|
||||
end
|
||||
).
|
||||
|
|
Loading…
Reference in New Issue