Merge pull request #11382 from savonarola/0801-auth-refactor
Auth refactor stage 1
This commit is contained in:
commit
ed67303281
|
@ -123,20 +123,4 @@
|
||||||
until :: integer()
|
until :: integer()
|
||||||
}).
|
}).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Authentication
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
-record(authenticator, {
|
|
||||||
id :: binary(),
|
|
||||||
provider :: module(),
|
|
||||||
enable :: boolean(),
|
|
||||||
state :: map()
|
|
||||||
}).
|
|
||||||
|
|
||||||
-record(chain, {
|
|
||||||
name :: atom(),
|
|
||||||
authenticators :: [#authenticator{}]
|
|
||||||
}).
|
|
||||||
|
|
||||||
-endif.
|
-endif.
|
||||||
|
|
|
@ -14,7 +14,9 @@
|
||||||
%% limitations under the License.
|
%% limitations under the License.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
%% config root name all auth providers have to agree on.
|
-ifndef(EMQX_ACCESS_CONTROL_HRL).
|
||||||
|
-define(EMQX_ACCESS_CONTROL_HRL, true).
|
||||||
|
|
||||||
-define(EMQX_AUTHORIZATION_CONFIG_ROOT_NAME, "authorization").
|
-define(EMQX_AUTHORIZATION_CONFIG_ROOT_NAME, "authorization").
|
||||||
-define(EMQX_AUTHORIZATION_CONFIG_ROOT_NAME_ATOM, authorization).
|
-define(EMQX_AUTHORIZATION_CONFIG_ROOT_NAME_ATOM, authorization).
|
||||||
-define(EMQX_AUTHORIZATION_CONFIG_ROOT_NAME_BINARY, <<"authorization">>).
|
-define(EMQX_AUTHORIZATION_CONFIG_ROOT_NAME_BINARY, <<"authorization">>).
|
||||||
|
@ -32,3 +34,7 @@
|
||||||
-define(authz_action(PUBSUB, QOS), #{action_type := PUBSUB, qos := QOS}).
|
-define(authz_action(PUBSUB, QOS), #{action_type := PUBSUB, qos := QOS}).
|
||||||
-define(authz_action(PUBSUB), ?authz_action(PUBSUB, _)).
|
-define(authz_action(PUBSUB), ?authz_action(PUBSUB, _)).
|
||||||
-define(authz_action, ?authz_action(_)).
|
-define(authz_action, ?authz_action(_)).
|
||||||
|
|
||||||
|
-define(AUTHN_TRACE_TAG, "AUTHN").
|
||||||
|
|
||||||
|
-endif.
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
-module(emqx_access_control).
|
-module(emqx_access_control).
|
||||||
|
|
||||||
-include("emqx.hrl").
|
-include("emqx.hrl").
|
||||||
|
-include("emqx_access_control.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
|
@ -29,6 +30,14 @@
|
||||||
-compile(nowarn_export_all).
|
-compile(nowarn_export_all).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
|
-define(TRACE_RESULT(Label, Result, Reason), begin
|
||||||
|
?TRACE(Label, ?AUTHN_TRACE_TAG, #{
|
||||||
|
result => (Result),
|
||||||
|
reason => (Reason)
|
||||||
|
}),
|
||||||
|
Result
|
||||||
|
end).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% APIs
|
%% APIs
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -44,7 +53,7 @@ authenticate(Credential) ->
|
||||||
%% if auth backend returning nothing but just 'ok'
|
%% if auth backend returning nothing but just 'ok'
|
||||||
%% it means it's not a superuser, or there is no way to tell.
|
%% it means it's not a superuser, or there is no way to tell.
|
||||||
NotSuperUser = #{is_superuser => false},
|
NotSuperUser = #{is_superuser => false},
|
||||||
case emqx_authentication:pre_hook_authenticate(Credential) of
|
case pre_hook_authenticate(Credential) of
|
||||||
ok ->
|
ok ->
|
||||||
inc_authn_metrics(anonymous),
|
inc_authn_metrics(anonymous),
|
||||||
{ok, NotSuperUser};
|
{ok, NotSuperUser};
|
||||||
|
@ -99,6 +108,29 @@ authorize(ClientInfo, Action, Topic) ->
|
||||||
inc_authz_metrics(Result),
|
inc_authz_metrics(Result),
|
||||||
Result.
|
Result.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Internal Functions
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-spec pre_hook_authenticate(emqx_types:clientinfo()) ->
|
||||||
|
ok | continue | {error, not_authorized}.
|
||||||
|
pre_hook_authenticate(#{enable_authn := false}) ->
|
||||||
|
?TRACE_RESULT("pre_hook_authenticate", ok, enable_authn_false);
|
||||||
|
pre_hook_authenticate(#{enable_authn := quick_deny_anonymous} = Credential) ->
|
||||||
|
case is_username_defined(Credential) of
|
||||||
|
true ->
|
||||||
|
continue;
|
||||||
|
false ->
|
||||||
|
?TRACE_RESULT("pre_hook_authenticate", {error, not_authorized}, enable_authn_false)
|
||||||
|
end;
|
||||||
|
pre_hook_authenticate(_) ->
|
||||||
|
continue.
|
||||||
|
|
||||||
|
is_username_defined(#{username := undefined}) -> false;
|
||||||
|
is_username_defined(#{username := <<>>}) -> false;
|
||||||
|
is_username_defined(#{username := _Username}) -> true;
|
||||||
|
is_username_defined(_) -> false.
|
||||||
|
|
||||||
check_authorization_cache(ClientInfo, Action, Topic) ->
|
check_authorization_cache(ClientInfo, Action, Topic) ->
|
||||||
case emqx_authz_cache:get_authz_cache(Action, Topic) of
|
case emqx_authz_cache:get_authz_cache(Action, Topic) of
|
||||||
not_found ->
|
not_found ->
|
||||||
|
|
|
@ -49,16 +49,6 @@ init([]) ->
|
||||||
modules => [emqx_shared_sub]
|
modules => [emqx_shared_sub]
|
||||||
},
|
},
|
||||||
|
|
||||||
%% Authentication
|
|
||||||
AuthNSup = #{
|
|
||||||
id => emqx_authentication_sup,
|
|
||||||
start => {emqx_authentication_sup, start_link, []},
|
|
||||||
restart => permanent,
|
|
||||||
shutdown => infinity,
|
|
||||||
type => supervisor,
|
|
||||||
modules => [emqx_authentication_sup]
|
|
||||||
},
|
|
||||||
|
|
||||||
%% Broker helper
|
%% Broker helper
|
||||||
Helper = #{
|
Helper = #{
|
||||||
id => helper,
|
id => helper,
|
||||||
|
@ -69,4 +59,4 @@ init([]) ->
|
||||||
modules => [emqx_broker_helper]
|
modules => [emqx_broker_helper]
|
||||||
},
|
},
|
||||||
|
|
||||||
{ok, {{one_for_all, 0, 1}, [BrokerPool, SharedSub, AuthNSup, Helper]}}.
|
{ok, {{one_for_all, 0, 1}, [BrokerPool, SharedSub, Helper]}}.
|
||||||
|
|
|
@ -53,11 +53,17 @@
|
||||||
|
|
||||||
-optional_callbacks([
|
-optional_callbacks([
|
||||||
pre_config_update/3,
|
pre_config_update/3,
|
||||||
post_config_update/5
|
propagated_pre_config_update/3,
|
||||||
|
post_config_update/5,
|
||||||
|
propagated_post_config_update/5
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-callback pre_config_update([atom()], emqx_config:update_request(), emqx_config:raw_config()) ->
|
-callback pre_config_update([atom()], emqx_config:update_request(), emqx_config:raw_config()) ->
|
||||||
{ok, emqx_config:update_request()} | {error, term()}.
|
ok | {ok, emqx_config:update_request()} | {error, term()}.
|
||||||
|
-callback propagated_pre_config_update(
|
||||||
|
[binary()], emqx_config:update_request(), emqx_config:raw_config()
|
||||||
|
) ->
|
||||||
|
ok | {ok, emqx_config:update_request()} | {error, term()}.
|
||||||
|
|
||||||
-callback post_config_update(
|
-callback post_config_update(
|
||||||
[atom()],
|
[atom()],
|
||||||
|
@ -68,6 +74,15 @@
|
||||||
) ->
|
) ->
|
||||||
ok | {ok, Result :: any()} | {error, Reason :: term()}.
|
ok | {ok, Result :: any()} | {error, Reason :: term()}.
|
||||||
|
|
||||||
|
-callback propagated_post_config_update(
|
||||||
|
[atom()],
|
||||||
|
emqx_config:update_request(),
|
||||||
|
emqx_config:config(),
|
||||||
|
emqx_config:config(),
|
||||||
|
emqx_config:app_envs()
|
||||||
|
) ->
|
||||||
|
ok | {ok, Result :: any()} | {error, Reason :: term()}.
|
||||||
|
|
||||||
-type state() :: #{handlers := any()}.
|
-type state() :: #{handlers := any()}.
|
||||||
|
|
||||||
start_link() ->
|
start_link() ->
|
||||||
|
@ -244,7 +259,14 @@ do_update_config(ConfKeyPath, Handlers, OldRawConf, UpdateReq) ->
|
||||||
do_update_config(ConfKeyPath, Handlers, OldRawConf, UpdateReq, []).
|
do_update_config(ConfKeyPath, Handlers, OldRawConf, UpdateReq, []).
|
||||||
|
|
||||||
do_update_config([], Handlers, OldRawConf, UpdateReq, ConfKeyPath) ->
|
do_update_config([], Handlers, OldRawConf, UpdateReq, ConfKeyPath) ->
|
||||||
call_pre_config_update(Handlers, OldRawConf, UpdateReq, ConfKeyPath);
|
call_pre_config_update(#{
|
||||||
|
handlers => Handlers,
|
||||||
|
old_raw_conf => OldRawConf,
|
||||||
|
update_req => UpdateReq,
|
||||||
|
conf_key_path => ConfKeyPath,
|
||||||
|
callback => pre_config_update,
|
||||||
|
is_propagated => false
|
||||||
|
});
|
||||||
do_update_config(
|
do_update_config(
|
||||||
[ConfKey | SubConfKeyPath],
|
[ConfKey | SubConfKeyPath],
|
||||||
Handlers,
|
Handlers,
|
||||||
|
@ -331,15 +353,16 @@ do_post_config_update(
|
||||||
Result,
|
Result,
|
||||||
ConfKeyPath
|
ConfKeyPath
|
||||||
) ->
|
) ->
|
||||||
call_post_config_update(
|
call_post_config_update(#{
|
||||||
Handlers,
|
handlers => Handlers,
|
||||||
OldConf,
|
old_conf => OldConf,
|
||||||
NewConf,
|
new_conf => NewConf,
|
||||||
AppEnvs,
|
app_envs => AppEnvs,
|
||||||
up_req(UpdateArgs),
|
update_req => up_req(UpdateArgs),
|
||||||
Result,
|
result => Result,
|
||||||
ConfKeyPath
|
conf_key_path => ConfKeyPath,
|
||||||
);
|
callback => post_config_update
|
||||||
|
});
|
||||||
do_post_config_update(
|
do_post_config_update(
|
||||||
[ConfKey | SubConfKeyPath],
|
[ConfKey | SubConfKeyPath],
|
||||||
Handlers,
|
Handlers,
|
||||||
|
@ -365,10 +388,16 @@ do_post_config_update(
|
||||||
ConfKeyPath
|
ConfKeyPath
|
||||||
).
|
).
|
||||||
|
|
||||||
get_sub_handlers(ConfKey, Handlers) ->
|
get_sub_handlers(ConfKey, Handlers) when is_atom(ConfKey) ->
|
||||||
case maps:find(ConfKey, Handlers) of
|
case maps:find(ConfKey, Handlers) of
|
||||||
error -> maps:get(?WKEY, Handlers, #{});
|
error -> maps:get(?WKEY, Handlers, #{});
|
||||||
{ok, SubHandlers} -> SubHandlers
|
{ok, SubHandlers} -> SubHandlers
|
||||||
|
end;
|
||||||
|
get_sub_handlers(ConfKey, Handlers) when is_binary(ConfKey) ->
|
||||||
|
ConcreteHandlerKeys = maps:keys(Handlers) -- [?MOD, ?WKEY],
|
||||||
|
case lists:search(fun(K) -> bin(K) =:= ConfKey end, ConcreteHandlerKeys) of
|
||||||
|
{value, Key} -> maps:get(Key, Handlers);
|
||||||
|
false -> maps:get(?WKEY, Handlers, #{})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_sub_config(ConfKey, Conf) when is_map(Conf) ->
|
get_sub_config(ConfKey, Conf) when is_map(Conf) ->
|
||||||
|
@ -377,56 +406,246 @@ get_sub_config(ConfKey, Conf) when is_map(Conf) ->
|
||||||
get_sub_config(_, _Conf) ->
|
get_sub_config(_, _Conf) ->
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
call_pre_config_update(#{?MOD := HandlerName}, OldRawConf, UpdateReq, ConfKeyPath) ->
|
call_pre_config_update(Ctx) ->
|
||||||
case erlang:function_exported(HandlerName, pre_config_update, 3) of
|
case call_proper_pre_config_update(Ctx) of
|
||||||
|
{ok, NewUpdateReq0} ->
|
||||||
|
case
|
||||||
|
propagate_pre_config_updates_to_subconf(Ctx#{
|
||||||
|
update_req => NewUpdateReq0
|
||||||
|
})
|
||||||
|
of
|
||||||
|
{ok, #{update_req := NewUpdateReq1}} ->
|
||||||
|
{ok, NewUpdateReq1};
|
||||||
|
{error, _} = Error ->
|
||||||
|
Error
|
||||||
|
end;
|
||||||
|
{error, _} = Error ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
call_proper_pre_config_update(
|
||||||
|
#{
|
||||||
|
handlers := #{?MOD := Module},
|
||||||
|
callback := Callback,
|
||||||
|
update_req := UpdateReq,
|
||||||
|
old_raw_conf := OldRawConf
|
||||||
|
} = Ctx
|
||||||
|
) ->
|
||||||
|
case erlang:function_exported(Module, Callback, 3) of
|
||||||
true ->
|
true ->
|
||||||
case HandlerName:pre_config_update(ConfKeyPath, UpdateReq, OldRawConf) of
|
case apply_pre_config_update(Module, Ctx) of
|
||||||
{ok, NewUpdateReq} -> {ok, NewUpdateReq};
|
{ok, NewUpdateReq} ->
|
||||||
{error, Reason} -> {error, {pre_config_update, HandlerName, Reason}}
|
{ok, NewUpdateReq};
|
||||||
|
ok ->
|
||||||
|
{ok, UpdateReq};
|
||||||
|
{error, Reason} ->
|
||||||
|
{error, {pre_config_update, Module, Reason}}
|
||||||
end;
|
end;
|
||||||
false ->
|
false ->
|
||||||
merge_to_old_config(UpdateReq, OldRawConf)
|
merge_to_old_config(UpdateReq, OldRawConf)
|
||||||
end;
|
end;
|
||||||
call_pre_config_update(_Handlers, OldRawConf, UpdateReq, _ConfKeyPath) ->
|
call_proper_pre_config_update(
|
||||||
merge_to_old_config(UpdateReq, OldRawConf).
|
#{update_req := UpdateReq}
|
||||||
|
|
||||||
call_post_config_update(
|
|
||||||
#{?MOD := HandlerName},
|
|
||||||
OldConf,
|
|
||||||
NewConf,
|
|
||||||
AppEnvs,
|
|
||||||
UpdateReq,
|
|
||||||
Result,
|
|
||||||
ConfKeyPath
|
|
||||||
) ->
|
) ->
|
||||||
case erlang:function_exported(HandlerName, post_config_update, 5) of
|
{ok, UpdateReq}.
|
||||||
true ->
|
|
||||||
|
apply_pre_config_update(Module, #{
|
||||||
|
conf_key_path := ConfKeyPath,
|
||||||
|
update_req := UpdateReq,
|
||||||
|
old_raw_conf := OldRawConf,
|
||||||
|
callback := Callback
|
||||||
|
}) ->
|
||||||
|
Module:Callback(
|
||||||
|
ConfKeyPath, UpdateReq, OldRawConf
|
||||||
|
).
|
||||||
|
|
||||||
|
propagate_pre_config_updates_to_subconf(
|
||||||
|
#{handlers := #{?WKEY := _}} = Ctx
|
||||||
|
) ->
|
||||||
|
propagate_pre_config_updates_to_subconf_wkey(Ctx);
|
||||||
|
propagate_pre_config_updates_to_subconf(
|
||||||
|
#{handlers := Handlers} = Ctx
|
||||||
|
) ->
|
||||||
|
Keys = maps:keys(maps:without([?MOD], Handlers)),
|
||||||
|
propagate_pre_config_updates_to_subconf_keys(Keys, Ctx).
|
||||||
|
|
||||||
|
propagate_pre_config_updates_to_subconf_wkey(
|
||||||
|
#{
|
||||||
|
update_req := UpdateReq,
|
||||||
|
old_raw_conf := OldRawConf
|
||||||
|
} = Ctx
|
||||||
|
) ->
|
||||||
|
Keys = propagate_keys(UpdateReq, OldRawConf),
|
||||||
|
propagate_pre_config_updates_to_subconf_keys(Keys, Ctx).
|
||||||
|
|
||||||
|
propagate_pre_config_updates_to_subconf_keys([], Ctx) ->
|
||||||
|
{ok, Ctx};
|
||||||
|
propagate_pre_config_updates_to_subconf_keys([Key | Keys], Ctx0) ->
|
||||||
|
case propagate_pre_config_updates_to_subconf_key(Key, Ctx0) of
|
||||||
|
{ok, Ctx1} ->
|
||||||
|
propagate_pre_config_updates_to_subconf_keys(Keys, Ctx1);
|
||||||
|
{error, _} = Error ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
propagate_pre_config_updates_to_subconf_key(
|
||||||
|
Key,
|
||||||
|
#{
|
||||||
|
handlers := Handlers,
|
||||||
|
old_raw_conf := OldRawConf,
|
||||||
|
update_req := UpdateReq,
|
||||||
|
conf_key_path := ConfKeyPath,
|
||||||
|
is_propagated := IsPropagated
|
||||||
|
} = Ctx
|
||||||
|
) ->
|
||||||
|
BinKey = bin(Key),
|
||||||
|
SubHandlers = get_sub_handlers(BinKey, Handlers),
|
||||||
|
SubUpdateReq = get_sub_config(BinKey, UpdateReq),
|
||||||
|
SubOldConf = get_sub_config(BinKey, OldRawConf),
|
||||||
|
SubConfKeyPath =
|
||||||
|
case IsPropagated of
|
||||||
|
true -> ConfKeyPath ++ [BinKey];
|
||||||
|
false -> bin_path(ConfKeyPath) ++ [BinKey]
|
||||||
|
end,
|
||||||
|
case {SubOldConf, SubUpdateReq} of
|
||||||
|
%% we have handler, but no relevant keys in both configs (new and old),
|
||||||
|
%% so we don't need to go further
|
||||||
|
{undefined, undefined} ->
|
||||||
|
{ok, Ctx};
|
||||||
|
{_, _} ->
|
||||||
case
|
case
|
||||||
HandlerName:post_config_update(
|
call_pre_config_update(Ctx#{
|
||||||
|
handlers := SubHandlers,
|
||||||
|
old_raw_conf := SubOldConf,
|
||||||
|
update_req := SubUpdateReq,
|
||||||
|
conf_key_path := SubConfKeyPath,
|
||||||
|
is_propagated := true,
|
||||||
|
callback := propagated_pre_config_update
|
||||||
|
})
|
||||||
|
of
|
||||||
|
{ok, SubNewConf1} ->
|
||||||
|
%% we update only if the new config is not to be removed
|
||||||
|
%% i.e. SubUpdateReq is not undefined
|
||||||
|
case SubUpdateReq of
|
||||||
|
undefined ->
|
||||||
|
{ok, Ctx};
|
||||||
|
_ ->
|
||||||
|
{ok, Ctx#{
|
||||||
|
update_req := maps:put(BinKey, SubNewConf1, UpdateReq)
|
||||||
|
}}
|
||||||
|
end;
|
||||||
|
{error, _} = Error ->
|
||||||
|
Error
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
call_post_config_update(#{handlers := Handlers} = Ctx) ->
|
||||||
|
case call_proper_post_config_update(Ctx) of
|
||||||
|
{ok, Result} ->
|
||||||
|
SubHandlers = maps:without([?MOD], Handlers),
|
||||||
|
propagate_post_config_updates_to_subconf(Ctx#{
|
||||||
|
handlers := SubHandlers,
|
||||||
|
callback := propagated_post_config_update,
|
||||||
|
result := Result
|
||||||
|
});
|
||||||
|
{error, _} = Error ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
call_proper_post_config_update(
|
||||||
|
#{
|
||||||
|
handlers := #{?MOD := Module},
|
||||||
|
callback := Callback,
|
||||||
|
result := Result
|
||||||
|
} = Ctx
|
||||||
|
) ->
|
||||||
|
case erlang:function_exported(Module, Callback, 5) of
|
||||||
|
true ->
|
||||||
|
case apply_post_config_update(Module, Ctx) of
|
||||||
|
ok -> {ok, Result};
|
||||||
|
{ok, Result1} -> {ok, Result#{Module => Result1}};
|
||||||
|
{error, Reason} -> {error, {post_config_update, Module, Reason}}
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
|
{ok, Result}
|
||||||
|
end;
|
||||||
|
call_proper_post_config_update(
|
||||||
|
#{result := Result} = _Ctx
|
||||||
|
) ->
|
||||||
|
{ok, Result}.
|
||||||
|
|
||||||
|
apply_post_config_update(Module, #{
|
||||||
|
conf_key_path := ConfKeyPath,
|
||||||
|
update_req := UpdateReq,
|
||||||
|
new_conf := NewConf,
|
||||||
|
old_conf := OldConf,
|
||||||
|
app_envs := AppEnvs,
|
||||||
|
callback := Callback
|
||||||
|
}) ->
|
||||||
|
Module:Callback(
|
||||||
ConfKeyPath,
|
ConfKeyPath,
|
||||||
UpdateReq,
|
UpdateReq,
|
||||||
NewConf,
|
NewConf,
|
||||||
OldConf,
|
OldConf,
|
||||||
AppEnvs
|
AppEnvs
|
||||||
)
|
).
|
||||||
of
|
|
||||||
ok -> {ok, Result};
|
propagate_post_config_updates_to_subconf(
|
||||||
{ok, Result1} -> {ok, Result#{HandlerName => Result1}};
|
#{handlers := #{?WKEY := _}} = Ctx
|
||||||
{error, Reason} -> {error, {post_config_update, HandlerName, Reason}}
|
|
||||||
end;
|
|
||||||
false ->
|
|
||||||
{ok, Result}
|
|
||||||
end;
|
|
||||||
call_post_config_update(
|
|
||||||
_Handlers,
|
|
||||||
_OldConf,
|
|
||||||
_NewConf,
|
|
||||||
_AppEnvs,
|
|
||||||
_UpdateReq,
|
|
||||||
Result,
|
|
||||||
_ConfKeyPath
|
|
||||||
) ->
|
) ->
|
||||||
{ok, Result}.
|
propagate_post_config_updates_to_subconf_wkey(Ctx);
|
||||||
|
propagate_post_config_updates_to_subconf(
|
||||||
|
#{handlers := Handlers} = Ctx
|
||||||
|
) ->
|
||||||
|
Keys = maps:keys(Handlers),
|
||||||
|
propagate_post_config_updates_to_subconf_keys(Keys, Ctx).
|
||||||
|
|
||||||
|
propagate_post_config_updates_to_subconf_wkey(
|
||||||
|
#{
|
||||||
|
old_conf := OldConf,
|
||||||
|
new_conf := NewConf
|
||||||
|
} = Ctx
|
||||||
|
) ->
|
||||||
|
Keys = propagate_keys(OldConf, NewConf),
|
||||||
|
propagate_post_config_updates_to_subconf_keys(Keys, Ctx).
|
||||||
|
propagate_post_config_updates_to_subconf_keys([], #{result := Result}) ->
|
||||||
|
{ok, Result};
|
||||||
|
propagate_post_config_updates_to_subconf_keys([Key | Keys], Ctx) ->
|
||||||
|
case propagate_post_config_updates_to_subconf_key(Key, Ctx) of
|
||||||
|
{ok, Result1} ->
|
||||||
|
propagate_post_config_updates_to_subconf_keys(Keys, Ctx#{result := Result1});
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
propagate_keys(OldConf, NewConf) ->
|
||||||
|
sets:to_list(sets:union(propagate_keys(OldConf), propagate_keys(NewConf))).
|
||||||
|
|
||||||
|
propagate_keys(Conf) when is_map(Conf) -> sets:from_list(maps:keys(Conf), [{version, 2}]);
|
||||||
|
propagate_keys(_) -> sets:new([{version, 2}]).
|
||||||
|
|
||||||
|
propagate_post_config_updates_to_subconf_key(
|
||||||
|
Key,
|
||||||
|
#{
|
||||||
|
handlers := Handlers,
|
||||||
|
new_conf := NewConf,
|
||||||
|
old_conf := OldConf,
|
||||||
|
result := Result,
|
||||||
|
conf_key_path := ConfKeyPath
|
||||||
|
} = Ctx
|
||||||
|
) ->
|
||||||
|
SubHandlers = maps:get(Key, Handlers, maps:get(?WKEY, Handlers, undefined)),
|
||||||
|
SubNewConf = get_sub_config(Key, NewConf),
|
||||||
|
SubOldConf = get_sub_config(Key, OldConf),
|
||||||
|
SubConfKeyPath = ConfKeyPath ++ [Key],
|
||||||
|
call_post_config_update(Ctx#{
|
||||||
|
handlers := SubHandlers,
|
||||||
|
new_conf := SubNewConf,
|
||||||
|
old_conf := SubOldConf,
|
||||||
|
result := Result,
|
||||||
|
conf_key_path := SubConfKeyPath,
|
||||||
|
callback := propagated_post_config_update
|
||||||
|
}).
|
||||||
|
|
||||||
%% The default callback of config handlers
|
%% The default callback of config handlers
|
||||||
%% the behaviour is overwriting the old config if:
|
%% the behaviour is overwriting the old config if:
|
||||||
|
@ -517,6 +736,7 @@ remove_empty_leaf(KeyPath, Handlers) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
assert_callback_function(Mod) ->
|
assert_callback_function(Mod) ->
|
||||||
|
_ = Mod:module_info(),
|
||||||
case
|
case
|
||||||
erlang:function_exported(Mod, pre_config_update, 3) orelse
|
erlang:function_exported(Mod, pre_config_update, 3) orelse
|
||||||
erlang:function_exported(Mod, post_config_update, 5)
|
erlang:function_exported(Mod, post_config_update, 5)
|
||||||
|
|
|
@ -531,41 +531,15 @@ post_config_update(_Path, _Request, _NewConf, _OldConf, _AppEnvs) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
create_listener(Type, Name, NewConf) ->
|
create_listener(Type, Name, NewConf) ->
|
||||||
Res = start_listener(Type, Name, NewConf),
|
start_listener(Type, Name, NewConf).
|
||||||
recreate_authenticators(Res, Type, Name, NewConf).
|
|
||||||
|
|
||||||
recreate_authenticators(ok, Type, Name, Conf) ->
|
|
||||||
Chain = listener_id(Type, Name),
|
|
||||||
_ = emqx_authentication:delete_chain(Chain),
|
|
||||||
do_create_authneticators(Chain, maps:get(authentication, Conf, []));
|
|
||||||
recreate_authenticators(Error, _Type, _Name, _NewConf) ->
|
|
||||||
Error.
|
|
||||||
|
|
||||||
do_create_authneticators(Chain, [AuthN | T]) ->
|
|
||||||
case emqx_authentication:create_authenticator(Chain, AuthN) of
|
|
||||||
{ok, _} ->
|
|
||||||
do_create_authneticators(Chain, T);
|
|
||||||
Error ->
|
|
||||||
_ = emqx_authentication:delete_chain(Chain),
|
|
||||||
Error
|
|
||||||
end;
|
|
||||||
do_create_authneticators(_Chain, []) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
remove_listener(Type, Name, OldConf) ->
|
remove_listener(Type, Name, OldConf) ->
|
||||||
ok = unregister_ocsp_stapling_refresh(Type, Name),
|
ok = unregister_ocsp_stapling_refresh(Type, Name),
|
||||||
case stop_listener(Type, Name, OldConf) of
|
stop_listener(Type, Name, OldConf).
|
||||||
ok ->
|
|
||||||
_ = emqx_authentication:delete_chain(listener_id(Type, Name)),
|
|
||||||
ok;
|
|
||||||
Err ->
|
|
||||||
Err
|
|
||||||
end.
|
|
||||||
|
|
||||||
update_listener(Type, Name, {OldConf, NewConf}) ->
|
update_listener(Type, Name, {OldConf, NewConf}) ->
|
||||||
ok = maybe_unregister_ocsp_stapling_refresh(Type, Name, NewConf),
|
ok = maybe_unregister_ocsp_stapling_refresh(Type, Name, NewConf),
|
||||||
Res = restart_listener(Type, Name, {OldConf, NewConf}),
|
restart_listener(Type, Name, {OldConf, NewConf}).
|
||||||
recreate_authenticators(Res, Type, Name, NewConf).
|
|
||||||
|
|
||||||
perform_listener_changes([]) ->
|
perform_listener_changes([]) ->
|
||||||
ok;
|
ok;
|
||||||
|
@ -847,10 +821,9 @@ convert_certs(ListenerConf) ->
|
||||||
fun(Type, Listeners0, Acc) ->
|
fun(Type, Listeners0, Acc) ->
|
||||||
Listeners1 =
|
Listeners1 =
|
||||||
maps:fold(
|
maps:fold(
|
||||||
fun(Name, Conf, Acc1) ->
|
fun(Name, Conf0, Acc1) ->
|
||||||
Conf1 = convert_certs(Type, Name, Conf),
|
Conf1 = convert_certs(Type, Name, Conf0),
|
||||||
Conf2 = convert_authn_certs(Type, Name, Conf1),
|
Acc1#{Name => Conf1}
|
||||||
Acc1#{Name => Conf2}
|
|
||||||
end,
|
end,
|
||||||
#{},
|
#{},
|
||||||
Listeners0
|
Listeners0
|
||||||
|
@ -873,19 +846,6 @@ convert_certs(Type, Name, Conf) ->
|
||||||
throw({bad_ssl_config, Reason})
|
throw({bad_ssl_config, Reason})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
convert_authn_certs(Type, Name, #{<<"authentication">> := AuthNList} = Conf) ->
|
|
||||||
ChainName = listener_id(Type, Name),
|
|
||||||
AuthNList1 = lists:map(
|
|
||||||
fun(AuthN) ->
|
|
||||||
CertsDir = emqx_authentication_config:certs_dir(ChainName, AuthN),
|
|
||||||
emqx_authentication_config:convert_certs(CertsDir, AuthN)
|
|
||||||
end,
|
|
||||||
AuthNList
|
|
||||||
),
|
|
||||||
Conf#{<<"authentication">> => AuthNList1};
|
|
||||||
convert_authn_certs(_Type, _Name, Conf) ->
|
|
||||||
Conf.
|
|
||||||
|
|
||||||
filter_stacktrace({Reason, _Stacktrace}) -> Reason;
|
filter_stacktrace({Reason, _Stacktrace}) -> Reason;
|
||||||
filter_stacktrace(Reason) -> Reason.
|
filter_stacktrace(Reason) -> Reason.
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
-elvis([{elvis_style, invalid_dynamic_call, disable}]).
|
-elvis([{elvis_style, invalid_dynamic_call, disable}]).
|
||||||
|
|
||||||
-include("emqx_schema.hrl").
|
-include("emqx_schema.hrl").
|
||||||
-include("emqx_authentication.hrl").
|
|
||||||
-include("emqx_access_control.hrl").
|
-include("emqx_access_control.hrl").
|
||||||
-include_lib("typerefl/include/types.hrl").
|
-include_lib("typerefl/include/types.hrl").
|
||||||
-include_lib("hocon/include/hoconsc.hrl").
|
-include_lib("hocon/include/hoconsc.hrl").
|
||||||
|
@ -213,8 +212,10 @@ roots(high) ->
|
||||||
desc => ?DESC(zones),
|
desc => ?DESC(zones),
|
||||||
importance => ?IMPORTANCE_HIDDEN
|
importance => ?IMPORTANCE_HIDDEN
|
||||||
}
|
}
|
||||||
)},
|
)}
|
||||||
{?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME, authentication(global)},
|
] ++
|
||||||
|
emqx_schema_hooks:injection_point('roots.high') ++
|
||||||
|
[
|
||||||
%% NOTE: authorization schema here is only to keep emqx app pure
|
%% NOTE: authorization schema here is only to keep emqx app pure
|
||||||
%% the full schema for EMQX node is injected in emqx_conf_schema.
|
%% the full schema for EMQX node is injected in emqx_conf_schema.
|
||||||
{?EMQX_AUTHORIZATION_CONFIG_ROOT_NAME,
|
{?EMQX_AUTHORIZATION_CONFIG_ROOT_NAME,
|
||||||
|
@ -1748,11 +1749,8 @@ mqtt_listener(Bind) ->
|
||||||
desc => ?DESC(mqtt_listener_proxy_protocol_timeout),
|
desc => ?DESC(mqtt_listener_proxy_protocol_timeout),
|
||||||
default => <<"3s">>
|
default => <<"3s">>
|
||||||
}
|
}
|
||||||
)},
|
)}
|
||||||
{?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME, (authentication(listener))#{
|
] ++ emqx_schema_hooks:injection_point('mqtt.listener').
|
||||||
importance => ?IMPORTANCE_HIDDEN
|
|
||||||
}}
|
|
||||||
].
|
|
||||||
|
|
||||||
base_listener(Bind) ->
|
base_listener(Bind) ->
|
||||||
[
|
[
|
||||||
|
@ -2770,41 +2768,6 @@ str(B) when is_binary(B) ->
|
||||||
str(S) when is_list(S) ->
|
str(S) when is_list(S) ->
|
||||||
S.
|
S.
|
||||||
|
|
||||||
authentication(Which) ->
|
|
||||||
{Importance, Desc} =
|
|
||||||
case Which of
|
|
||||||
global ->
|
|
||||||
%% For root level authentication, it is recommended to configure
|
|
||||||
%% from the dashboard or API.
|
|
||||||
%% Hence it's considered a low-importance when it comes to
|
|
||||||
%% configuration importance.
|
|
||||||
{?IMPORTANCE_LOW, ?DESC(global_authentication)};
|
|
||||||
listener ->
|
|
||||||
{?IMPORTANCE_HIDDEN, ?DESC(listener_authentication)}
|
|
||||||
end,
|
|
||||||
%% poor man's dependency injection
|
|
||||||
%% this is due to the fact that authn is implemented outside of 'emqx' app.
|
|
||||||
%% so it can not be a part of emqx_schema since 'emqx' app is supposed to
|
|
||||||
%% work standalone.
|
|
||||||
Type =
|
|
||||||
case persistent_term:get(?EMQX_AUTHENTICATION_SCHEMA_MODULE_PT_KEY, undefined) of
|
|
||||||
undefined ->
|
|
||||||
hoconsc:array(typerefl:map());
|
|
||||||
Module ->
|
|
||||||
Module:root_type()
|
|
||||||
end,
|
|
||||||
hoconsc:mk(Type, #{
|
|
||||||
desc => Desc,
|
|
||||||
converter => fun ensure_array/2,
|
|
||||||
default => [],
|
|
||||||
importance => Importance
|
|
||||||
}).
|
|
||||||
|
|
||||||
%% the older version schema allows individual element (instead of a chain) in config
|
|
||||||
ensure_array(undefined, _) -> undefined;
|
|
||||||
ensure_array(L, _) when is_list(L) -> L;
|
|
||||||
ensure_array(M, _) -> [M].
|
|
||||||
|
|
||||||
-spec qos() -> typerefl:type().
|
-spec qos() -> typerefl:type().
|
||||||
qos() ->
|
qos() ->
|
||||||
typerefl:alias("qos", typerefl:union([0, 1, 2])).
|
typerefl:alias("qos", typerefl:union([0, 1, 2])).
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2017-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%
|
||||||
|
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
%% you may not use this file except in compliance with the License.
|
||||||
|
%% You may obtain a copy of the License at
|
||||||
|
%%
|
||||||
|
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
%%
|
||||||
|
%% Unless required by applicable law or agreed to in writing, software
|
||||||
|
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
%% See the License for the specific language governing permissions and
|
||||||
|
%% limitations under the License.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(emqx_schema_hooks).
|
||||||
|
|
||||||
|
-type hookpoint() :: atom().
|
||||||
|
|
||||||
|
-callback injected_fields() ->
|
||||||
|
#{
|
||||||
|
hookpoint() => [hocon_schema:field()]
|
||||||
|
}.
|
||||||
|
-optional_callbacks([injected_fields/0]).
|
||||||
|
|
||||||
|
-export_type([hookpoint/0]).
|
||||||
|
|
||||||
|
-define(HOOKPOINT_PT_KEY(POINT_NAME), {?MODULE, fields, POINT_NAME}).
|
||||||
|
|
||||||
|
-export([
|
||||||
|
injection_point/1,
|
||||||
|
inject_from_modules/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
%% for tests
|
||||||
|
-export([
|
||||||
|
erase_injections/0,
|
||||||
|
any_injections/0
|
||||||
|
]).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% API
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
injection_point(PointName) ->
|
||||||
|
persistent_term:get(?HOOKPOINT_PT_KEY(PointName), []).
|
||||||
|
|
||||||
|
erase_injections() ->
|
||||||
|
lists:foreach(
|
||||||
|
fun
|
||||||
|
({?HOOKPOINT_PT_KEY(_) = Key, _}) ->
|
||||||
|
persistent_term:erase(Key);
|
||||||
|
(_) ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
persistent_term:get()
|
||||||
|
).
|
||||||
|
|
||||||
|
any_injections() ->
|
||||||
|
lists:any(
|
||||||
|
fun
|
||||||
|
({?HOOKPOINT_PT_KEY(_), _}) ->
|
||||||
|
true;
|
||||||
|
(_) ->
|
||||||
|
false
|
||||||
|
end,
|
||||||
|
persistent_term:get()
|
||||||
|
).
|
||||||
|
|
||||||
|
inject_from_modules(Modules) ->
|
||||||
|
Injections =
|
||||||
|
lists:foldl(
|
||||||
|
fun append_module_injections/2,
|
||||||
|
#{},
|
||||||
|
Modules
|
||||||
|
),
|
||||||
|
ok = inject_fields(maps:to_list(Injections)).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Internal functions
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
append_module_injections(Module, AllInjections) when is_atom(Module) ->
|
||||||
|
append_module_injections(Module:injected_fields(), AllInjections);
|
||||||
|
append_module_injections(ModuleInjections, AllInjections) when is_map(ModuleInjections) ->
|
||||||
|
maps:fold(
|
||||||
|
fun(PointName, Fields, Acc) ->
|
||||||
|
maps:update_with(
|
||||||
|
PointName,
|
||||||
|
fun(Fields0) ->
|
||||||
|
Fields0 ++ Fields
|
||||||
|
end,
|
||||||
|
Fields,
|
||||||
|
Acc
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
AllInjections,
|
||||||
|
ModuleInjections
|
||||||
|
).
|
||||||
|
|
||||||
|
inject_fields([]) ->
|
||||||
|
ok;
|
||||||
|
inject_fields([{PointName, Fields} | Rest]) ->
|
||||||
|
case any_injections(PointName) of
|
||||||
|
true ->
|
||||||
|
inject_fields(Rest);
|
||||||
|
false ->
|
||||||
|
ok = inject_fields(PointName, Fields),
|
||||||
|
inject_fields(Rest)
|
||||||
|
end.
|
||||||
|
|
||||||
|
inject_fields(PointName, Fields) ->
|
||||||
|
Key = ?HOOKPOINT_PT_KEY(PointName),
|
||||||
|
persistent_term:put(Key, Fields).
|
||||||
|
|
||||||
|
any_injections(PointName) ->
|
||||||
|
persistent_term:get(?HOOKPOINT_PT_KEY(PointName), undefined) =/= undefined.
|
|
@ -26,6 +26,7 @@
|
||||||
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
|
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
|
||||||
|
|
||||||
-include_lib("emqx/include/emqx.hrl").
|
-include_lib("emqx/include/emqx.hrl").
|
||||||
|
-include_lib("emqx/include/emqx_hooks.hrl").
|
||||||
-include_lib("emqx/include/emqx_mqtt.hrl").
|
-include_lib("emqx/include/emqx_mqtt.hrl").
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
|
@ -680,28 +681,17 @@ t_connect_client_never_negative({'end', _Config}) ->
|
||||||
|
|
||||||
t_connack_auth_error({init, Config}) ->
|
t_connack_auth_error({init, Config}) ->
|
||||||
process_flag(trap_exit, true),
|
process_flag(trap_exit, true),
|
||||||
ChainName = 'mqtt:global',
|
emqx_hooks:put(
|
||||||
AuthenticatorConfig = #{
|
'client.authenticate',
|
||||||
enable => true,
|
{?MODULE, authenticate_deny, []},
|
||||||
mechanism => password_based,
|
?HP_AUTHN
|
||||||
backend => built_in_database,
|
|
||||||
user_id_type => username,
|
|
||||||
password_hash_algorithm => #{
|
|
||||||
name => plain,
|
|
||||||
salt_position => disable
|
|
||||||
},
|
|
||||||
user_group => <<"global:mqtt">>
|
|
||||||
},
|
|
||||||
ok = emqx_authentication:register_providers(
|
|
||||||
[{{password_based, built_in_database}, emqx_authentication_SUITE}]
|
|
||||||
),
|
),
|
||||||
emqx_authentication:initialize_authentication(ChainName, AuthenticatorConfig),
|
|
||||||
Config;
|
Config;
|
||||||
t_connack_auth_error({'end', _Config}) ->
|
t_connack_auth_error({'end', _Config}) ->
|
||||||
ChainName = 'mqtt:global',
|
emqx_hooks:del(
|
||||||
AuthenticatorID = <<"password_based:built_in_database">>,
|
'client.authenticate',
|
||||||
ok = emqx_authentication:deregister_provider({password_based, built_in_database}),
|
{?MODULE, authenticate_deny, []}
|
||||||
ok = emqx_authentication:delete_authenticator(ChainName, AuthenticatorID),
|
),
|
||||||
ok;
|
ok;
|
||||||
t_connack_auth_error(Config) when is_list(Config) ->
|
t_connack_auth_error(Config) when is_list(Config) ->
|
||||||
%% MQTT 3.1
|
%% MQTT 3.1
|
||||||
|
@ -733,6 +723,9 @@ t_handle_in_empty_client_subscribe_hook(Config) when is_list(Config) ->
|
||||||
emqtt:disconnect(C)
|
emqtt:disconnect(C)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
authenticate_deny(_Credentials, _Default) ->
|
||||||
|
{stop, {error, bad_username_or_password}}.
|
||||||
|
|
||||||
wait_for_events(Action, Kinds) ->
|
wait_for_events(Action, Kinds) ->
|
||||||
wait_for_events(Action, Kinds, 500).
|
wait_for_events(Action, Kinds, 500).
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
|
|
||||||
-module(emqx_common_test_helpers).
|
-module(emqx_common_test_helpers).
|
||||||
|
|
||||||
-include_lib("emqx/include/emqx_authentication.hrl").
|
|
||||||
|
|
||||||
-type special_config_handler() :: fun().
|
-type special_config_handler() :: fun().
|
||||||
|
|
||||||
-type apps() :: list(atom()).
|
-type apps() :: list(atom()).
|
||||||
|
@ -351,7 +349,7 @@ stop_apps(Apps, Opts) ->
|
||||||
%% to avoid inter-suite flakiness
|
%% to avoid inter-suite flakiness
|
||||||
application:unset_env(emqx, config_loader),
|
application:unset_env(emqx, config_loader),
|
||||||
application:unset_env(emqx, boot_modules),
|
application:unset_env(emqx, boot_modules),
|
||||||
persistent_term:erase(?EMQX_AUTHENTICATION_SCHEMA_MODULE_PT_KEY),
|
emqx_schema_hooks:erase_injections(),
|
||||||
case Opts of
|
case Opts of
|
||||||
#{erase_all_configs := false} ->
|
#{erase_all_configs := false} ->
|
||||||
%% FIXME: this means inter-suite or inter-test dependencies
|
%% FIXME: this means inter-suite or inter-test dependencies
|
||||||
|
|
|
@ -26,7 +26,8 @@
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
-include_lib("common_test/include/ct.hrl").
|
-include_lib("common_test/include/ct.hrl").
|
||||||
|
|
||||||
all() -> emqx_common_test_helpers:all(?MODULE).
|
all() ->
|
||||||
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
emqx_common_test_helpers:boot_modules(all),
|
emqx_common_test_helpers:boot_modules(all),
|
||||||
|
@ -223,8 +224,8 @@ t_callback_crash(_Config) ->
|
||||||
ok = emqx_config_handler:remove_handler(CrashPath),
|
ok = emqx_config_handler:remove_handler(CrashPath),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
t_pre_callback_error(_Config) ->
|
t_pre_assert_update_result(_Config) ->
|
||||||
callback_error(
|
assert_update_result(
|
||||||
[sysmon, os, mem_check_interval],
|
[sysmon, os, mem_check_interval],
|
||||||
<<"100s">>,
|
<<"100s">>,
|
||||||
{error, {pre_config_update, ?MODULE, pre_config_update_error}}
|
{error, {pre_config_update, ?MODULE, pre_config_update_error}}
|
||||||
|
@ -232,13 +233,88 @@ t_pre_callback_error(_Config) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
t_post_update_error(_Config) ->
|
t_post_update_error(_Config) ->
|
||||||
callback_error(
|
assert_update_result(
|
||||||
[sysmon, os, sysmem_high_watermark],
|
[sysmon, os, sysmem_high_watermark],
|
||||||
<<"60%">>,
|
<<"60%">>,
|
||||||
{error, {post_config_update, ?MODULE, post_config_update_error}}
|
{error, {post_config_update, ?MODULE, post_config_update_error}}
|
||||||
),
|
),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
t_post_update_propagate_error_wkey(_Config) ->
|
||||||
|
Conf0 = emqx_config:get_raw([sysmon]),
|
||||||
|
Conf1 = emqx_utils_maps:deep_put([<<"os">>, <<"sysmem_high_watermark">>], Conf0, <<"60%">>),
|
||||||
|
assert_update_result(
|
||||||
|
[
|
||||||
|
[sysmon, '?', sysmem_high_watermark],
|
||||||
|
[sysmon]
|
||||||
|
],
|
||||||
|
[sysmon],
|
||||||
|
Conf1,
|
||||||
|
{error, {post_config_update, ?MODULE, post_config_update_error}}
|
||||||
|
),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
t_post_update_propagate_error_key(_Config) ->
|
||||||
|
Conf0 = emqx_config:get_raw([sysmon]),
|
||||||
|
Conf1 = emqx_utils_maps:deep_put([<<"os">>, <<"sysmem_high_watermark">>], Conf0, <<"60%">>),
|
||||||
|
assert_update_result(
|
||||||
|
[
|
||||||
|
[sysmon, os, sysmem_high_watermark],
|
||||||
|
[sysmon]
|
||||||
|
],
|
||||||
|
[sysmon],
|
||||||
|
Conf1,
|
||||||
|
{error, {post_config_update, ?MODULE, post_config_update_error}}
|
||||||
|
),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
t_pre_update_propagate_error_wkey(_Config) ->
|
||||||
|
Conf0 = emqx_config:get_raw([sysmon]),
|
||||||
|
Conf1 = emqx_utils_maps:deep_put([<<"os">>, <<"mem_check_interval">>], Conf0, <<"70s">>),
|
||||||
|
assert_update_result(
|
||||||
|
[
|
||||||
|
[sysmon, '?', mem_check_interval],
|
||||||
|
[sysmon]
|
||||||
|
],
|
||||||
|
[sysmon],
|
||||||
|
Conf1,
|
||||||
|
{error, {pre_config_update, ?MODULE, pre_config_update_error}}
|
||||||
|
),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
t_pre_update_propagate_error_key(_Config) ->
|
||||||
|
Conf0 = emqx_config:get_raw([sysmon]),
|
||||||
|
Conf1 = emqx_utils_maps:deep_put([<<"os">>, <<"mem_check_interval">>], Conf0, <<"70s">>),
|
||||||
|
assert_update_result(
|
||||||
|
[
|
||||||
|
[sysmon, os, mem_check_interval],
|
||||||
|
[sysmon]
|
||||||
|
],
|
||||||
|
[sysmon],
|
||||||
|
Conf1,
|
||||||
|
{error, {pre_config_update, ?MODULE, pre_config_update_error}}
|
||||||
|
),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
t_pre_update_propagate_key_rewrite(_Config) ->
|
||||||
|
Conf0 = emqx_config:get_raw([sysmon]),
|
||||||
|
Conf1 = emqx_utils_maps:deep_put([<<"os">>, <<"cpu_check_interval">>], Conf0, <<"333s">>),
|
||||||
|
with_update_result(
|
||||||
|
[
|
||||||
|
[sysmon, '?', cpu_check_interval],
|
||||||
|
[sysmon]
|
||||||
|
],
|
||||||
|
[sysmon],
|
||||||
|
Conf1,
|
||||||
|
fun(_, Result) ->
|
||||||
|
?assertMatch(
|
||||||
|
{ok, #{config := #{os := #{cpu_check_interval := 444000}}}},
|
||||||
|
Result
|
||||||
|
)
|
||||||
|
end
|
||||||
|
),
|
||||||
|
ok.
|
||||||
|
|
||||||
t_handler_root() ->
|
t_handler_root() ->
|
||||||
%% Don't rely on default emqx_config_handler's merge behaviour.
|
%% Don't rely on default emqx_config_handler's merge behaviour.
|
||||||
RootKey = [],
|
RootKey = [],
|
||||||
|
@ -295,6 +371,17 @@ pre_config_update([sysmon, os, sysmem_high_watermark], UpdateReq, _RawConf) ->
|
||||||
pre_config_update([sysmon, os, mem_check_interval], _UpdateReq, _RawConf) ->
|
pre_config_update([sysmon, os, mem_check_interval], _UpdateReq, _RawConf) ->
|
||||||
{error, pre_config_update_error}.
|
{error, pre_config_update_error}.
|
||||||
|
|
||||||
|
propagated_pre_config_update(
|
||||||
|
[<<"sysmon">>, <<"os">>, <<"cpu_check_interval">>], <<"333s">>, _RawConf
|
||||||
|
) ->
|
||||||
|
{ok, <<"444s">>};
|
||||||
|
propagated_pre_config_update(
|
||||||
|
[<<"sysmon">>, <<"os">>, <<"mem_check_interval">>], _UpdateReq, _RawConf
|
||||||
|
) ->
|
||||||
|
{error, pre_config_update_error};
|
||||||
|
propagated_pre_config_update(_ConfKeyPath, _UpdateReq, _RawConf) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
post_config_update([sysmon], _UpdateReq, _NewConf, _OldConf, _AppEnvs) ->
|
post_config_update([sysmon], _UpdateReq, _NewConf, _OldConf, _AppEnvs) ->
|
||||||
{ok, ok};
|
{ok, ok};
|
||||||
post_config_update([sysmon, os], _UpdateReq, _NewConf, _OldConf, _AppEnvs) ->
|
post_config_update([sysmon, os], _UpdateReq, _NewConf, _OldConf, _AppEnvs) ->
|
||||||
|
@ -308,6 +395,13 @@ post_config_update([sysmon, os, cpu_high_watermark], _UpdateReq, _NewConf, _OldC
|
||||||
post_config_update([sysmon, os, sysmem_high_watermark], _UpdateReq, _NewConf, _OldConf, _AppEnvs) ->
|
post_config_update([sysmon, os, sysmem_high_watermark], _UpdateReq, _NewConf, _OldConf, _AppEnvs) ->
|
||||||
{error, post_config_update_error}.
|
{error, post_config_update_error}.
|
||||||
|
|
||||||
|
propagated_post_config_update(
|
||||||
|
[sysmon, os, sysmem_high_watermark], _UpdateReq, _NewConf, _OldConf, _AppEnvs
|
||||||
|
) ->
|
||||||
|
{error, post_config_update_error};
|
||||||
|
propagated_post_config_update(_ConfKeyPath, _UpdateReq, _NewConf, _OldConf, _AppEnvs) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
wait_for_new_pid() ->
|
wait_for_new_pid() ->
|
||||||
case erlang:whereis(emqx_config_handler) of
|
case erlang:whereis(emqx_config_handler) of
|
||||||
undefined ->
|
undefined ->
|
||||||
|
@ -317,20 +411,34 @@ wait_for_new_pid() ->
|
||||||
Pid
|
Pid
|
||||||
end.
|
end.
|
||||||
|
|
||||||
callback_error(FailedPath, Update, ExpectError) ->
|
assert_update_result(FailedPath, Update, Expect) ->
|
||||||
Opts = #{rawconf_with_defaults => true},
|
assert_update_result([FailedPath], FailedPath, Update, Expect).
|
||||||
ok = emqx_config_handler:add_handler(FailedPath, ?MODULE),
|
|
||||||
Old = emqx:get_raw_config(FailedPath, undefined),
|
assert_update_result(Paths, UpdatePath, Update, Expect) ->
|
||||||
Error = emqx:update_config(FailedPath, Update, Opts),
|
with_update_result(Paths, UpdatePath, Update, fun(Old, Result) ->
|
||||||
case ExpectError of
|
case Expect of
|
||||||
{error, {post_config_update, ?MODULE, post_config_update_error}} ->
|
{error, {post_config_update, ?MODULE, post_config_update_error}} ->
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{error, {post_config_update, ?MODULE, {post_config_update_error, _}}}, Error
|
{error, {post_config_update, ?MODULE, {post_config_update_error, _}}}, Result
|
||||||
);
|
);
|
||||||
_ ->
|
_ ->
|
||||||
?assertEqual(ExpectError, Error)
|
?assertEqual(Expect, Result)
|
||||||
end,
|
end,
|
||||||
New = emqx:get_raw_config(FailedPath, undefined),
|
New = emqx:get_raw_config(UpdatePath, undefined),
|
||||||
?assertEqual(Old, New),
|
?assertEqual(Old, New)
|
||||||
ok = emqx_config_handler:remove_handler(FailedPath),
|
end).
|
||||||
|
|
||||||
|
with_update_result(Paths, UpdatePath, Update, Fun) ->
|
||||||
|
ok = lists:foreach(
|
||||||
|
fun(Path) -> emqx_config_handler:add_handler(Path, ?MODULE) end,
|
||||||
|
Paths
|
||||||
|
),
|
||||||
|
Opts = #{rawconf_with_defaults => true},
|
||||||
|
Old = emqx:get_raw_config(UpdatePath, undefined),
|
||||||
|
Result = emqx:update_config(UpdatePath, Update, Opts),
|
||||||
|
_ = Fun(Old, Result),
|
||||||
|
ok = lists:foreach(
|
||||||
|
fun(Path) -> emqx_config_handler:remove_handler(Path) end,
|
||||||
|
Paths
|
||||||
|
),
|
||||||
ok.
|
ok.
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
-module(emqx_cth_suite).
|
-module(emqx_cth_suite).
|
||||||
|
|
||||||
-include_lib("common_test/include/ct.hrl").
|
-include_lib("common_test/include/ct.hrl").
|
||||||
-include_lib("emqx/include/emqx_authentication.hrl").
|
-include_lib("emqx/include/emqx_access_control.hrl").
|
||||||
|
|
||||||
-export([start/2]).
|
-export([start/2]).
|
||||||
-export([stop/1]).
|
-export([stop/1]).
|
||||||
|
@ -360,12 +360,12 @@ stop_apps(Apps) ->
|
||||||
|
|
||||||
verify_clean_suite_state(#{work_dir := WorkDir}) ->
|
verify_clean_suite_state(#{work_dir := WorkDir}) ->
|
||||||
{ok, []} = file:list_dir(WorkDir),
|
{ok, []} = file:list_dir(WorkDir),
|
||||||
none = persistent_term:get(?EMQX_AUTHENTICATION_SCHEMA_MODULE_PT_KEY, none),
|
false = emqx_schema_hooks:any_injections(),
|
||||||
[] = emqx_config:get_root_names(),
|
[] = emqx_config:get_root_names(),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
clean_suite_state() ->
|
clean_suite_state() ->
|
||||||
_ = persistent_term:erase(?EMQX_AUTHENTICATION_SCHEMA_MODULE_PT_KEY),
|
_ = emqx_schema_hooks:erase_injections(),
|
||||||
_ = emqx_config:erase_all(),
|
_ = emqx_config:erase_all(),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
-define(EMQX_AUTHENTICATION_HRL, true).
|
-define(EMQX_AUTHENTICATION_HRL, true).
|
||||||
|
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
-include_lib("emqx/include/emqx_access_control.hrl").
|
||||||
|
|
||||||
-define(AUTHN_TRACE_TAG, "AUTHN").
|
|
||||||
-define(GLOBAL, 'mqtt:global').
|
-define(GLOBAL, 'mqtt:global').
|
||||||
|
|
||||||
-define(TRACE_AUTHN_PROVIDER(Msg), ?TRACE_AUTHN_PROVIDER(Msg, #{})).
|
-define(TRACE_AUTHN_PROVIDER(Msg), ?TRACE_AUTHN_PROVIDER(Msg, #{})).
|
||||||
|
@ -36,12 +36,6 @@
|
||||||
-define(EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM, authentication).
|
-define(EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM, authentication).
|
||||||
-define(EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY, <<"authentication">>).
|
-define(EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY, <<"authentication">>).
|
||||||
|
|
||||||
%% key to a persistent term which stores a module name in order to inject
|
|
||||||
%% schema module at run-time to keep emqx app's compile time purity.
|
|
||||||
%% see emqx_schema.erl for more details
|
|
||||||
%% and emqx_conf_schema for an examples
|
|
||||||
-define(EMQX_AUTHENTICATION_SCHEMA_MODULE_PT_KEY, emqx_authentication_schema_module).
|
|
||||||
|
|
||||||
%% authentication move cmd
|
%% authentication move cmd
|
||||||
-define(CMD_MOVE_FRONT, front).
|
-define(CMD_MOVE_FRONT, front).
|
||||||
-define(CMD_MOVE_REAR, rear).
|
-define(CMD_MOVE_REAR, rear).
|
|
@ -17,7 +17,7 @@
|
||||||
-ifndef(EMQX_AUTHN_HRL).
|
-ifndef(EMQX_AUTHN_HRL).
|
||||||
-define(EMQX_AUTHN_HRL, true).
|
-define(EMQX_AUTHN_HRL, true).
|
||||||
|
|
||||||
-include_lib("emqx/include/emqx_authentication.hrl").
|
-include_lib("emqx_authentication.hrl").
|
||||||
|
|
||||||
-define(APP, emqx_authn).
|
-define(APP, emqx_authn).
|
||||||
|
|
||||||
|
|
|
@ -34,4 +34,6 @@
|
||||||
{cover_opts, [verbose]}.
|
{cover_opts, [verbose]}.
|
||||||
{cover_export_enabled, true}.
|
{cover_export_enabled, true}.
|
||||||
|
|
||||||
|
{erl_first_files, ["src/emqx_authentication.erl"]}.
|
||||||
|
|
||||||
{project_plugins, [erlfmt]}.
|
{project_plugins, [erlfmt]}.
|
||||||
|
|
|
@ -22,18 +22,27 @@
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
-include("emqx.hrl").
|
|
||||||
-include("logger.hrl").
|
|
||||||
-include("emqx_authentication.hrl").
|
-include("emqx_authentication.hrl").
|
||||||
|
-include_lib("emqx/include/logger.hrl").
|
||||||
-include_lib("emqx/include/emqx_hooks.hrl").
|
-include_lib("emqx/include/emqx_hooks.hrl").
|
||||||
-include_lib("stdlib/include/ms_transform.hrl").
|
-include_lib("stdlib/include/ms_transform.hrl").
|
||||||
|
|
||||||
-define(CONF_ROOT, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM).
|
-define(CONF_ROOT, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM).
|
||||||
-define(IS_UNDEFINED(X), (X =:= undefined orelse X =:= <<>>)).
|
|
||||||
|
-record(authenticator, {
|
||||||
|
id :: binary(),
|
||||||
|
provider :: module(),
|
||||||
|
enable :: boolean(),
|
||||||
|
state :: map()
|
||||||
|
}).
|
||||||
|
|
||||||
|
-record(chain, {
|
||||||
|
name :: atom(),
|
||||||
|
authenticators :: [#authenticator{}]
|
||||||
|
}).
|
||||||
|
|
||||||
%% The authentication entrypoint.
|
%% The authentication entrypoint.
|
||||||
-export([
|
-export([
|
||||||
pre_hook_authenticate/1,
|
|
||||||
authenticate/2
|
authenticate/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
@ -220,21 +229,6 @@ when
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Authenticate
|
%% Authenticate
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec pre_hook_authenticate(emqx_types:clientinfo()) ->
|
|
||||||
ok | continue | {error, not_authorized}.
|
|
||||||
pre_hook_authenticate(#{enable_authn := false}) ->
|
|
||||||
?TRACE_RESULT("authentication_result", ok, enable_authn_false);
|
|
||||||
pre_hook_authenticate(#{enable_authn := quick_deny_anonymous} = Credential) ->
|
|
||||||
case maps:get(username, Credential, undefined) of
|
|
||||||
U when ?IS_UNDEFINED(U) ->
|
|
||||||
?TRACE_RESULT(
|
|
||||||
"authentication_result", {error, not_authorized}, enable_authn_false
|
|
||||||
);
|
|
||||||
_ ->
|
|
||||||
continue
|
|
||||||
end;
|
|
||||||
pre_hook_authenticate(_) ->
|
|
||||||
continue.
|
|
||||||
|
|
||||||
authenticate(#{listener := Listener, protocol := Protocol} = Credential, AuthResult) ->
|
authenticate(#{listener := Listener, protocol := Protocol} = Credential, AuthResult) ->
|
||||||
case get_authenticators(Listener, global_chain(Protocol)) of
|
case get_authenticators(Listener, global_chain(Protocol)) of
|
||||||
|
@ -271,6 +265,7 @@ get_enabled(Authenticators) ->
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
%% @doc Get all registered authentication providers.
|
%% @doc Get all registered authentication providers.
|
||||||
|
-spec get_providers() -> #{authn_type() => module()}.
|
||||||
get_providers() ->
|
get_providers() ->
|
||||||
call(get_providers).
|
call(get_providers).
|
||||||
|
|
|
@ -21,7 +21,9 @@
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
pre_config_update/3,
|
pre_config_update/3,
|
||||||
post_config_update/5
|
post_config_update/5,
|
||||||
|
propagated_pre_config_update/3,
|
||||||
|
propagated_post_config_update/5
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
|
@ -37,7 +39,7 @@
|
||||||
|
|
||||||
-export_type([config/0]).
|
-export_type([config/0]).
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
-include("emqx_authentication.hrl").
|
-include("emqx_authentication.hrl").
|
||||||
|
|
||||||
-type parsed_config() :: #{
|
-type parsed_config() :: #{
|
||||||
|
@ -65,8 +67,8 @@
|
||||||
|
|
||||||
-spec pre_config_update(list(atom()), update_request(), emqx_config:raw_config()) ->
|
-spec pre_config_update(list(atom()), update_request(), emqx_config:raw_config()) ->
|
||||||
{ok, map() | list()} | {error, term()}.
|
{ok, map() | list()} | {error, term()}.
|
||||||
pre_config_update(Paths, UpdateReq, OldConfig) ->
|
pre_config_update(ConfPath, UpdateReq, OldConfig) ->
|
||||||
try do_pre_config_update(Paths, UpdateReq, to_list(OldConfig)) of
|
try do_pre_config_update(ConfPath, UpdateReq, to_list(OldConfig)) of
|
||||||
{error, Reason} -> {error, Reason};
|
{error, Reason} -> {error, Reason};
|
||||||
{ok, NewConfig} -> {ok, NewConfig}
|
{ok, NewConfig} -> {ok, NewConfig}
|
||||||
catch
|
catch
|
||||||
|
@ -130,31 +132,33 @@ do_pre_config_update(_, {move_authenticator, _ChainName, AuthenticatorID, Positi
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
do_pre_config_update(Paths, {merge_authenticators, NewConfig}, OldConfig) ->
|
do_pre_config_update(ConfPath, {merge_authenticators, NewConfig}, OldConfig) ->
|
||||||
MergeConfig = merge_authenticators(OldConfig, NewConfig),
|
MergeConfig = merge_authenticators(OldConfig, NewConfig),
|
||||||
do_pre_config_update(Paths, MergeConfig, OldConfig);
|
do_pre_config_update(ConfPath, MergeConfig, OldConfig);
|
||||||
do_pre_config_update(_, OldConfig, OldConfig) ->
|
do_pre_config_update(_, OldConfig, OldConfig) ->
|
||||||
{ok, OldConfig};
|
{ok, OldConfig};
|
||||||
do_pre_config_update(Paths, NewConfig, _OldConfig) ->
|
do_pre_config_update(ConfPath, NewConfig, _OldConfig) ->
|
||||||
ChainName = chain_name(Paths),
|
convert_certs_for_conf_path(ConfPath, NewConfig).
|
||||||
{ok, [
|
|
||||||
begin
|
%% @doc Handle listener config changes made at higher level.
|
||||||
CertsDir = certs_dir(ChainName, New),
|
|
||||||
convert_certs(CertsDir, New)
|
-spec propagated_pre_config_update(list(binary()), update_request(), emqx_config:raw_config()) ->
|
||||||
end
|
{ok, map() | list()} | {error, term()}.
|
||||||
|| New <- to_list(NewConfig)
|
propagated_pre_config_update(_, OldConfig, OldConfig) ->
|
||||||
]}.
|
{ok, OldConfig};
|
||||||
|
propagated_pre_config_update(ConfPath, NewConfig, _OldConfig) ->
|
||||||
|
convert_certs_for_conf_path(ConfPath, NewConfig).
|
||||||
|
|
||||||
-spec post_config_update(
|
-spec post_config_update(
|
||||||
list(atom()),
|
list(atom()),
|
||||||
update_request(),
|
update_request(),
|
||||||
map() | list(),
|
map() | list() | undefined,
|
||||||
emqx_config:raw_config(),
|
emqx_config:raw_config(),
|
||||||
emqx_config:app_envs()
|
emqx_config:app_envs()
|
||||||
) ->
|
) ->
|
||||||
ok | {ok, map()} | {error, term()}.
|
ok | {ok, map()} | {error, term()}.
|
||||||
post_config_update(Paths, UpdateReq, NewConfig, OldConfig, AppEnvs) ->
|
post_config_update(ConfPath, UpdateReq, NewConfig, OldConfig, AppEnvs) ->
|
||||||
do_post_config_update(Paths, UpdateReq, to_list(NewConfig), OldConfig, AppEnvs).
|
do_post_config_update(ConfPath, UpdateReq, to_list(NewConfig), OldConfig, AppEnvs).
|
||||||
|
|
||||||
do_post_config_update(
|
do_post_config_update(
|
||||||
_, {create_authenticator, ChainName, Config}, NewConfig, _OldConfig, _AppEnvs
|
_, {create_authenticator, ChainName, Config}, NewConfig, _OldConfig, _AppEnvs
|
||||||
|
@ -192,8 +196,8 @@ do_post_config_update(
|
||||||
emqx_authentication:move_authenticator(ChainName, AuthenticatorID, Position);
|
emqx_authentication:move_authenticator(ChainName, AuthenticatorID, Position);
|
||||||
do_post_config_update(_, _UpdateReq, OldConfig, OldConfig, _AppEnvs) ->
|
do_post_config_update(_, _UpdateReq, OldConfig, OldConfig, _AppEnvs) ->
|
||||||
ok;
|
ok;
|
||||||
do_post_config_update(Paths, _UpdateReq, NewConfig0, OldConfig0, _AppEnvs) ->
|
do_post_config_update(ConfPath, _UpdateReq, NewConfig0, OldConfig0, _AppEnvs) ->
|
||||||
ChainName = chain_name(Paths),
|
ChainName = chain_name(ConfPath),
|
||||||
OldConfig = to_list(OldConfig0),
|
OldConfig = to_list(OldConfig0),
|
||||||
NewConfig = to_list(NewConfig0),
|
NewConfig = to_list(NewConfig0),
|
||||||
OldIds = lists:map(fun authenticator_id/1, OldConfig),
|
OldIds = lists:map(fun authenticator_id/1, OldConfig),
|
||||||
|
@ -203,6 +207,20 @@ do_post_config_update(Paths, _UpdateReq, NewConfig0, OldConfig0, _AppEnvs) ->
|
||||||
ok = emqx_authentication:reorder_authenticator(ChainName, NewIds),
|
ok = emqx_authentication:reorder_authenticator(ChainName, NewIds),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
%% @doc Handle listener config changes made at higher level.
|
||||||
|
|
||||||
|
-spec propagated_post_config_update(
|
||||||
|
list(atom()),
|
||||||
|
update_request(),
|
||||||
|
map() | list() | undefined,
|
||||||
|
emqx_config:raw_config(),
|
||||||
|
emqx_config:app_envs()
|
||||||
|
) ->
|
||||||
|
ok.
|
||||||
|
propagated_post_config_update(ConfPath, UpdateReq, NewConfig, OldConfig, AppEnvs) ->
|
||||||
|
ok = post_config_update(ConfPath, UpdateReq, NewConfig, OldConfig, AppEnvs),
|
||||||
|
ok.
|
||||||
|
|
||||||
%% create new authenticators and update existing ones
|
%% create new authenticators and update existing ones
|
||||||
create_or_update_authenticators(OldIds, ChainName, NewConfig) ->
|
create_or_update_authenticators(OldIds, ChainName, NewConfig) ->
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
|
@ -238,6 +256,17 @@ to_list(M) when M =:= #{} -> [];
|
||||||
to_list(M) when is_map(M) -> [M];
|
to_list(M) when is_map(M) -> [M];
|
||||||
to_list(L) when is_list(L) -> L.
|
to_list(L) when is_list(L) -> L.
|
||||||
|
|
||||||
|
convert_certs_for_conf_path(ConfPath, NewConfig) ->
|
||||||
|
ChainName = chain_name_for_filepath(ConfPath),
|
||||||
|
CovertedConfs = lists:map(
|
||||||
|
fun(Conf) ->
|
||||||
|
CertsDir = certs_dir(ChainName, Conf),
|
||||||
|
convert_certs(CertsDir, Conf)
|
||||||
|
end,
|
||||||
|
to_list(NewConfig)
|
||||||
|
),
|
||||||
|
{ok, CovertedConfs}.
|
||||||
|
|
||||||
convert_certs(CertsDir, NewConfig) ->
|
convert_certs(CertsDir, NewConfig) ->
|
||||||
NewSSL = maps:get(<<"ssl">>, NewConfig, undefined),
|
NewSSL = maps:get(<<"ssl">>, NewConfig, undefined),
|
||||||
case emqx_tls_lib:ensure_ssl_files(CertsDir, NewSSL) of
|
case emqx_tls_lib:ensure_ssl_files(CertsDir, NewSSL) of
|
||||||
|
@ -331,7 +360,16 @@ dir(ChainName, Config) when is_map(Config) ->
|
||||||
chain_name([authentication]) ->
|
chain_name([authentication]) ->
|
||||||
?GLOBAL;
|
?GLOBAL;
|
||||||
chain_name([listeners, Type, Name, authentication]) ->
|
chain_name([listeners, Type, Name, authentication]) ->
|
||||||
binary_to_existing_atom(<<(atom_to_binary(Type))/binary, ":", (atom_to_binary(Name))/binary>>).
|
%% Type, Name atoms exist, so let 'Type:Name' exist too.
|
||||||
|
binary_to_atom(<<(atom_to_binary(Type))/binary, ":", (atom_to_binary(Name))/binary>>).
|
||||||
|
|
||||||
|
chain_name_for_filepath(Path) ->
|
||||||
|
do_chain_name_for_filepath([to_bin(Key) || Key <- Path]).
|
||||||
|
|
||||||
|
do_chain_name_for_filepath([<<"authentication">>]) ->
|
||||||
|
to_bin(?GLOBAL);
|
||||||
|
do_chain_name_for_filepath([<<"listeners">>, Type, Name, <<"authentication">>]) ->
|
||||||
|
<<(to_bin(Type))/binary, ":", (to_bin(Name))/binary>>.
|
||||||
|
|
||||||
merge_authenticators(OriginConf0, NewConf0) ->
|
merge_authenticators(OriginConf0, NewConf0) ->
|
||||||
{OriginConf1, NewConf1} =
|
{OriginConf1, NewConf1} =
|
|
@ -21,7 +21,6 @@
|
||||||
-include("emqx_authn.hrl").
|
-include("emqx_authn.hrl").
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||||
-include_lib("emqx/include/emqx_authentication.hrl").
|
|
||||||
-include_lib("hocon/include/hoconsc.hrl").
|
-include_lib("hocon/include/hoconsc.hrl").
|
||||||
|
|
||||||
-import(hoconsc, [mk/2, ref/1, ref/2]).
|
-import(hoconsc, [mk/2, ref/1, ref/2]).
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
stop/1
|
stop/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-include_lib("emqx/include/emqx_authentication.hrl").
|
-include_lib("emqx_authentication.hrl").
|
||||||
|
|
||||||
-dialyzer({nowarn_function, [start/2]}).
|
-dialyzer({nowarn_function, [start/2]}).
|
||||||
|
|
||||||
|
@ -35,8 +35,7 @@
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
start(_StartType, _StartArgs) ->
|
start(_StartType, _StartArgs) ->
|
||||||
%% required by test cases, ensure the injection of
|
%% required by test cases, ensure the injection of schema
|
||||||
%% EMQX_AUTHENTICATION_SCHEMA_MODULE_PT_KEY
|
|
||||||
_ = emqx_conf_schema:roots(),
|
_ = emqx_conf_schema:roots(),
|
||||||
ok = mria_rlog:wait_for_shards([?AUTH_SHARD], infinity),
|
ok = mria_rlog:wait_for_shards([?AUTH_SHARD], infinity),
|
||||||
{ok, Sup} = emqx_authn_sup:start_link(),
|
{ok, Sup} = emqx_authn_sup:start_link(),
|
||||||
|
|
|
@ -19,6 +19,12 @@
|
||||||
-elvis([{elvis_style, invalid_dynamic_call, disable}]).
|
-elvis([{elvis_style, invalid_dynamic_call, disable}]).
|
||||||
-include_lib("hocon/include/hoconsc.hrl").
|
-include_lib("hocon/include/hoconsc.hrl").
|
||||||
-include("emqx_authn.hrl").
|
-include("emqx_authn.hrl").
|
||||||
|
-include("emqx_authentication.hrl").
|
||||||
|
|
||||||
|
-behaviour(emqx_schema_hooks).
|
||||||
|
-export([
|
||||||
|
injected_fields/0
|
||||||
|
]).
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
common_fields/0,
|
common_fields/0,
|
||||||
|
@ -28,13 +34,18 @@
|
||||||
fields/1,
|
fields/1,
|
||||||
authenticator_type/0,
|
authenticator_type/0,
|
||||||
authenticator_type_without_scram/0,
|
authenticator_type_without_scram/0,
|
||||||
root_type/0,
|
|
||||||
mechanism/1,
|
mechanism/1,
|
||||||
backend/1
|
backend/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
roots() -> [].
|
roots() -> [].
|
||||||
|
|
||||||
|
injected_fields() ->
|
||||||
|
#{
|
||||||
|
'mqtt.listener' => global_auth_fields(),
|
||||||
|
'roots.high' => mqtt_listener_auth_fields()
|
||||||
|
}.
|
||||||
|
|
||||||
tags() ->
|
tags() ->
|
||||||
[<<"Authentication">>].
|
[<<"Authentication">>].
|
||||||
|
|
||||||
|
@ -121,12 +132,36 @@ try_select_union_member(Module, Value) ->
|
||||||
Module:refs()
|
Module:refs()
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% authn is a core functionality however implemented outside of emqx app
|
|
||||||
%% in emqx_schema, 'authentication' is a map() type which is to allow
|
|
||||||
%% EMQX more pluggable.
|
|
||||||
root_type() ->
|
root_type() ->
|
||||||
hoconsc:array(authenticator_type()).
|
hoconsc:array(authenticator_type()).
|
||||||
|
|
||||||
|
global_auth_fields() ->
|
||||||
|
[
|
||||||
|
{?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM,
|
||||||
|
hoconsc:mk(root_type(), #{
|
||||||
|
desc => ?DESC(global_authentication),
|
||||||
|
converter => fun ensure_array/2,
|
||||||
|
default => [],
|
||||||
|
importance => ?IMPORTANCE_LOW
|
||||||
|
})}
|
||||||
|
].
|
||||||
|
|
||||||
|
mqtt_listener_auth_fields() ->
|
||||||
|
[
|
||||||
|
{?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM,
|
||||||
|
hoconsc:mk(root_type(), #{
|
||||||
|
desc => ?DESC(listener_authentication),
|
||||||
|
converter => fun ensure_array/2,
|
||||||
|
default => [],
|
||||||
|
importance => ?IMPORTANCE_HIDDEN
|
||||||
|
})}
|
||||||
|
].
|
||||||
|
|
||||||
|
%% the older version schema allows individual element (instead of a chain) in config
|
||||||
|
ensure_array(undefined, _) -> undefined;
|
||||||
|
ensure_array(L, _) when is_list(L) -> L;
|
||||||
|
ensure_array(M, _) -> [M].
|
||||||
|
|
||||||
mechanism(Name) ->
|
mechanism(Name) ->
|
||||||
?HOCON(
|
?HOCON(
|
||||||
Name,
|
Name,
|
||||||
|
|
|
@ -27,5 +27,15 @@ start_link() ->
|
||||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||||
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
ChildSpecs = [],
|
AuthNSup = #{
|
||||||
|
id => emqx_authentication_sup,
|
||||||
|
start => {emqx_authentication_sup, start_link, []},
|
||||||
|
restart => permanent,
|
||||||
|
shutdown => infinity,
|
||||||
|
type => supervisor,
|
||||||
|
modules => [emqx_authentication_sup]
|
||||||
|
},
|
||||||
|
|
||||||
|
ChildSpecs = [AuthNSup],
|
||||||
|
|
||||||
{ok, {{one_for_one, 10, 10}, ChildSpecs}}.
|
{ok, {{one_for_one, 10, 10}, ChildSpecs}}.
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
|
|
||||||
-include("emqx_authn.hrl").
|
-include("emqx_authn.hrl").
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
-include_lib("emqx/include/emqx_authentication.hrl").
|
|
||||||
-include_lib("hocon/include/hoconsc.hrl").
|
-include_lib("hocon/include/hoconsc.hrl").
|
||||||
|
|
||||||
-import(emqx_dashboard_swagger, [error_codes/2]).
|
-import(emqx_dashboard_swagger, [error_codes/2]).
|
||||||
|
|
|
@ -94,19 +94,19 @@ all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
LogLevel = emqx_logger:get_primary_log_level(),
|
Apps = emqx_cth_suite:start(
|
||||||
ok = emqx_logger:set_log_level(debug),
|
[
|
||||||
application:set_env(ekka, strict_mode, true),
|
emqx,
|
||||||
emqx_config:erase_all(),
|
emqx_conf,
|
||||||
emqx_common_test_helpers:stop_apps([]),
|
emqx_authn
|
||||||
emqx_common_test_helpers:boot_modules(all),
|
],
|
||||||
emqx_common_test_helpers:start_apps([]),
|
#{work_dir => ?config(priv_dir)}
|
||||||
[{log_level, LogLevel} | Config].
|
),
|
||||||
|
ok = deregister_providers(),
|
||||||
|
[{apps, Apps} | Config].
|
||||||
|
|
||||||
end_per_suite(Config) ->
|
end_per_suite(Config) ->
|
||||||
emqx_common_test_helpers:stop_apps([]),
|
emqx_cth_suite:stop(?config(apps)),
|
||||||
LogLevel = ?config(log_level),
|
|
||||||
emqx_logger:set_log_level(LogLevel),
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
init_per_testcase(Case, Config) ->
|
init_per_testcase(Case, Config) ->
|
||||||
|
@ -302,15 +302,20 @@ t_update_config(Config) when is_list(Config) ->
|
||||||
ok = register_provider(?config("auth1"), ?MODULE),
|
ok = register_provider(?config("auth1"), ?MODULE),
|
||||||
ok = register_provider(?config("auth2"), ?MODULE),
|
ok = register_provider(?config("auth2"), ?MODULE),
|
||||||
Global = ?config(global),
|
Global = ?config(global),
|
||||||
|
%% We mocked provider implementation, but did't mock the schema
|
||||||
|
%% so we should provide full config
|
||||||
AuthenticatorConfig1 = #{
|
AuthenticatorConfig1 = #{
|
||||||
mechanism => password_based,
|
<<"mechanism">> => <<"password_based">>,
|
||||||
backend => built_in_database,
|
<<"backend">> => <<"built_in_database">>,
|
||||||
enable => true
|
<<"enable">> => true
|
||||||
},
|
},
|
||||||
AuthenticatorConfig2 = #{
|
AuthenticatorConfig2 = #{
|
||||||
mechanism => password_based,
|
<<"mechanism">> => <<"password_based">>,
|
||||||
backend => mysql,
|
<<"backend">> => <<"mysql">>,
|
||||||
enable => true
|
<<"query">> => <<"SELECT password_hash, salt FROM users WHERE username = ?">>,
|
||||||
|
<<"server">> => <<"127.0.0.1:5432">>,
|
||||||
|
<<"database">> => <<"emqx">>,
|
||||||
|
<<"enable">> => true
|
||||||
},
|
},
|
||||||
ID1 = <<"password_based:built_in_database">>,
|
ID1 = <<"password_based:built_in_database">>,
|
||||||
ID2 = <<"password_based:mysql">>,
|
ID2 = <<"password_based:mysql">>,
|
||||||
|
@ -580,3 +585,11 @@ certs(Certs) ->
|
||||||
|
|
||||||
register_provider(Type, Module) ->
|
register_provider(Type, Module) ->
|
||||||
ok = ?AUTHN:register_providers([{Type, Module}]).
|
ok = ?AUTHN:register_providers([{Type, Module}]).
|
||||||
|
|
||||||
|
deregister_providers() ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({Type, _Module}) ->
|
||||||
|
ok = ?AUTHN:deregister_provider(Type)
|
||||||
|
end,
|
||||||
|
maps:to_list(?AUTHN:get_providers())
|
||||||
|
).
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
-include("emqx_authn.hrl").
|
-include("emqx_authn.hrl").
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
-include_lib("common_test/include/ct.hrl").
|
||||||
|
|
||||||
-define(TCP_DEFAULT, 'tcp:default').
|
-define(TCP_DEFAULT, 'tcp:default').
|
||||||
|
|
||||||
|
@ -43,7 +44,6 @@ init_per_testcase(t_authenticator_fail, Config) ->
|
||||||
meck:expect(emqx_authn_proto_v1, lookup_from_all_nodes, 3, [{error, {exception, badarg}}]),
|
meck:expect(emqx_authn_proto_v1, lookup_from_all_nodes, 3, [{error, {exception, badarg}}]),
|
||||||
init_per_testcase(default, Config);
|
init_per_testcase(default, Config);
|
||||||
init_per_testcase(_Case, Config) ->
|
init_per_testcase(_Case, Config) ->
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[?CONF_NS_ATOM],
|
[?CONF_NS_ATOM],
|
||||||
?GLOBAL
|
?GLOBAL
|
||||||
|
@ -64,19 +64,27 @@ end_per_testcase(_, Config) ->
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
emqx_config:erase(?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY),
|
Apps = emqx_cth_suite:start(
|
||||||
_ = application:load(emqx_conf),
|
[
|
||||||
ok = emqx_mgmt_api_test_util:init_suite(
|
emqx,
|
||||||
[emqx_conf, emqx_authn]
|
emqx_conf,
|
||||||
|
emqx_authn,
|
||||||
|
emqx_management,
|
||||||
|
{emqx_dashboard, "dashboard.listeners.http { enable = true, bind = 18083 }"}
|
||||||
|
],
|
||||||
|
#{
|
||||||
|
work_dir => ?config(priv_dir, Config)
|
||||||
|
}
|
||||||
),
|
),
|
||||||
|
_ = emqx_common_test_http:create_default_app(),
|
||||||
?AUTHN:delete_chain(?GLOBAL),
|
?AUTHN:delete_chain(?GLOBAL),
|
||||||
{ok, Chains} = ?AUTHN:list_chains(),
|
{ok, Chains} = ?AUTHN:list_chains(),
|
||||||
?assertEqual(length(Chains), 0),
|
?assertEqual(length(Chains), 0),
|
||||||
Config.
|
[{apps, Apps} | Config].
|
||||||
|
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(Config) ->
|
||||||
emqx_mgmt_api_test_util:end_suite([emqx_authn]),
|
_ = emqx_common_test_http:delete_default_app(),
|
||||||
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
|
@ -24,16 +24,19 @@
|
||||||
-define(PATH, [?CONF_NS_ATOM]).
|
-define(PATH, [?CONF_NS_ATOM]).
|
||||||
|
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
-include_lib("common_test/include/ct.hrl").
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
emqx_common_test_helpers:start_apps([emqx_conf, emqx_authn]),
|
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn], #{
|
||||||
Config.
|
work_dir => ?config(priv_dir, Config)
|
||||||
|
}),
|
||||||
|
[{apps, Apps} | Config].
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(Config) ->
|
||||||
emqx_common_test_helpers:stop_apps([emqx_authn, emqx_conf]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
init_per_testcase(_Case, Config) ->
|
init_per_testcase(_Case, Config) ->
|
||||||
|
@ -42,9 +45,10 @@ init_per_testcase(_Case, Config) ->
|
||||||
<<"backend">> => <<"built_in_database">>,
|
<<"backend">> => <<"built_in_database">>,
|
||||||
<<"user_id_type">> => <<"clientid">>
|
<<"user_id_type">> => <<"clientid">>
|
||||||
},
|
},
|
||||||
{ok, _} = emqx:update_config(
|
{ok, _} = emqx_conf:update(
|
||||||
?PATH,
|
?PATH,
|
||||||
{create_authenticator, ?GLOBAL, AuthnConfig}
|
{create_authenticator, ?GLOBAL, AuthnConfig},
|
||||||
|
#{}
|
||||||
),
|
),
|
||||||
{ok, _} = emqx_conf:update(
|
{ok, _} = emqx_conf:update(
|
||||||
[listeners, tcp, listener_authn_enabled],
|
[listeners, tcp, listener_authn_enabled],
|
||||||
|
|
|
@ -65,18 +65,17 @@ all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
_ = application:load(emqx_conf),
|
Apps = emqx_cth_suite:start([cowboy, emqx, emqx_conf, emqx_authn], #{
|
||||||
emqx_common_test_helpers:start_apps([emqx_authn]),
|
work_dir => ?config(priv_dir, Config)
|
||||||
application:ensure_all_started(cowboy),
|
}),
|
||||||
Config.
|
[{apps, Apps} | Config].
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(Config) ->
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
?GLOBAL
|
?GLOBAL
|
||||||
),
|
),
|
||||||
emqx_common_test_helpers:stop_apps([emqx_authn]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
application:stop(cowboy),
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
init_per_testcase(_Case, Config) ->
|
init_per_testcase(_Case, Config) ->
|
||||||
|
|
|
@ -39,18 +39,17 @@ all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
_ = application:load(emqx_conf),
|
Apps = emqx_cth_suite:start([cowboy, emqx, emqx_conf, emqx_authn], #{
|
||||||
emqx_common_test_helpers:start_apps([emqx_authn]),
|
work_dir => ?config(priv_dir, Config)
|
||||||
application:ensure_all_started(cowboy),
|
}),
|
||||||
Config.
|
[{apps, Apps} | Config].
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(Config) ->
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
?GLOBAL
|
?GLOBAL
|
||||||
),
|
),
|
||||||
emqx_common_test_helpers:stop_apps([emqx_authn]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
application:stop(cowboy),
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
init_per_testcase(_Case, Config) ->
|
init_per_testcase(_Case, Config) ->
|
||||||
|
|
|
@ -31,21 +31,14 @@
|
||||||
all() ->
|
all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
init_per_testcase(_, Config) ->
|
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
|
||||||
Config.
|
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
_ = application:load(emqx_conf),
|
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn], #{
|
||||||
emqx_common_test_helpers:start_apps([emqx_conf, emqx_authn]),
|
work_dir => ?config(priv_dir, Config)
|
||||||
application:ensure_all_started(emqx_resource),
|
}),
|
||||||
application:ensure_all_started(emqx_connector),
|
[{apps, Apps} | Config].
|
||||||
Config.
|
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(Config) ->
|
||||||
application:stop(emqx_connector),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
application:stop(emqx_resource),
|
|
||||||
emqx_common_test_helpers:stop_apps([emqx_authn]),
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
|
@ -0,0 +1,242 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2022-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%
|
||||||
|
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
%% you may not use this file except in compliance with the License.
|
||||||
|
%% You may obtain a copy of the License at
|
||||||
|
%%
|
||||||
|
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
%%
|
||||||
|
%% Unless required by applicable law or agreed to in writing, software
|
||||||
|
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
%% See the License for the specific language governing permissions and
|
||||||
|
%% limitations under the License.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(emqx_authn_listeners_SUITE).
|
||||||
|
|
||||||
|
-compile(export_all).
|
||||||
|
-compile(nowarn_export_all).
|
||||||
|
|
||||||
|
-include("emqx_authn.hrl").
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
-include_lib("common_test/include/ct.hrl").
|
||||||
|
|
||||||
|
all() ->
|
||||||
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
|
init_per_suite(Config) ->
|
||||||
|
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn], #{
|
||||||
|
work_dir => ?config(priv_dir, Config)
|
||||||
|
}),
|
||||||
|
[{apps, Apps} | Config].
|
||||||
|
|
||||||
|
end_per_suite(Config) ->
|
||||||
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
init_per_testcase(_Case, Config) ->
|
||||||
|
Port = emqx_common_test_helpers:select_free_port(tcp),
|
||||||
|
[{port, Port} | Config].
|
||||||
|
|
||||||
|
end_per_testcase(_Case, _Config) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
t_create_update_delete(Config) ->
|
||||||
|
ListenerConf = listener_mqtt_tcp_conf(Config),
|
||||||
|
AuthnConfig0 = #{
|
||||||
|
<<"mechanism">> => <<"password_based">>,
|
||||||
|
<<"backend">> => <<"built_in_database">>,
|
||||||
|
<<"user_id_type">> => <<"clientid">>
|
||||||
|
},
|
||||||
|
%% Create
|
||||||
|
{ok, _} = emqx_conf:update(
|
||||||
|
[listeners],
|
||||||
|
#{
|
||||||
|
<<"tcp">> => #{
|
||||||
|
<<"listener0">> => ListenerConf#{
|
||||||
|
?CONF_NS_BINARY => AuthnConfig0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{}
|
||||||
|
),
|
||||||
|
?assertMatch(
|
||||||
|
{ok, [
|
||||||
|
#{
|
||||||
|
authenticators := [
|
||||||
|
#{
|
||||||
|
id := <<"password_based:built_in_database">>,
|
||||||
|
state := #{
|
||||||
|
user_id_type := clientid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
name := 'tcp:listener0'
|
||||||
|
}
|
||||||
|
]},
|
||||||
|
emqx_authentication:list_chains()
|
||||||
|
),
|
||||||
|
|
||||||
|
%% Drop old, create new
|
||||||
|
{ok, _} = emqx_conf:update(
|
||||||
|
[listeners],
|
||||||
|
#{
|
||||||
|
<<"tcp">> => #{
|
||||||
|
<<"listener1">> => ListenerConf#{
|
||||||
|
?CONF_NS_BINARY => AuthnConfig0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{}
|
||||||
|
),
|
||||||
|
?assertMatch(
|
||||||
|
{ok, [
|
||||||
|
#{
|
||||||
|
authenticators := [
|
||||||
|
#{
|
||||||
|
id := <<"password_based:built_in_database">>,
|
||||||
|
state := #{
|
||||||
|
user_id_type := clientid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
name := 'tcp:listener1'
|
||||||
|
}
|
||||||
|
]},
|
||||||
|
emqx_authentication:list_chains()
|
||||||
|
),
|
||||||
|
|
||||||
|
%% Update
|
||||||
|
{ok, _} = emqx_conf:update(
|
||||||
|
[listeners],
|
||||||
|
#{
|
||||||
|
<<"tcp">> => #{
|
||||||
|
<<"listener1">> => ListenerConf#{
|
||||||
|
?CONF_NS_BINARY => AuthnConfig0#{<<"user_id_type">> => <<"username">>}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{}
|
||||||
|
),
|
||||||
|
?assertMatch(
|
||||||
|
{ok, [
|
||||||
|
#{
|
||||||
|
authenticators := [
|
||||||
|
#{
|
||||||
|
id := <<"password_based:built_in_database">>,
|
||||||
|
state := #{
|
||||||
|
user_id_type := username
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
name := 'tcp:listener1'
|
||||||
|
}
|
||||||
|
]},
|
||||||
|
emqx_authentication:list_chains()
|
||||||
|
),
|
||||||
|
|
||||||
|
%% Update by listener path
|
||||||
|
{ok, _} = emqx_conf:update(
|
||||||
|
[listeners, tcp, listener1],
|
||||||
|
{update, ListenerConf#{
|
||||||
|
?CONF_NS_BINARY => AuthnConfig0#{<<"user_id_type">> => <<"clientid">>}
|
||||||
|
}},
|
||||||
|
#{}
|
||||||
|
),
|
||||||
|
?assertMatch(
|
||||||
|
{ok, [
|
||||||
|
#{
|
||||||
|
authenticators := [
|
||||||
|
#{
|
||||||
|
id := <<"password_based:built_in_database">>,
|
||||||
|
state := #{
|
||||||
|
user_id_type := clientid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
name := 'tcp:listener1'
|
||||||
|
}
|
||||||
|
]},
|
||||||
|
emqx_authentication:list_chains()
|
||||||
|
),
|
||||||
|
|
||||||
|
%% Delete
|
||||||
|
{ok, _} = emqx_conf:tombstone(
|
||||||
|
[listeners, tcp, listener1],
|
||||||
|
#{}
|
||||||
|
),
|
||||||
|
?assertMatch(
|
||||||
|
{ok, []},
|
||||||
|
emqx_authentication:list_chains()
|
||||||
|
).
|
||||||
|
|
||||||
|
t_convert_certs(Config) ->
|
||||||
|
ListenerConf = listener_mqtt_tcp_conf(Config),
|
||||||
|
AuthnConfig0 = #{
|
||||||
|
<<"mechanism">> => <<"password_based">>,
|
||||||
|
<<"password_hash_algorithm">> => #{
|
||||||
|
<<"name">> => <<"plain">>,
|
||||||
|
<<"salt_position">> => <<"suffix">>
|
||||||
|
},
|
||||||
|
<<"enable">> => <<"true">>,
|
||||||
|
|
||||||
|
<<"backend">> => <<"redis">>,
|
||||||
|
<<"cmd">> => <<"HMGET mqtt_user:${username} password_hash salt is_superuser">>,
|
||||||
|
<<"database">> => <<"1">>,
|
||||||
|
<<"password">> => <<"public">>,
|
||||||
|
<<"server">> => <<"127.0.0.1:55555">>,
|
||||||
|
<<"redis_type">> => <<"single">>,
|
||||||
|
<<"ssl">> => #{
|
||||||
|
<<"enable">> => true,
|
||||||
|
<<"cacertfile">> => some_pem(),
|
||||||
|
<<"certfile">> => some_pem(),
|
||||||
|
<<"keyfile">> => some_pem()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ok, _} = emqx_conf:update(
|
||||||
|
[listeners],
|
||||||
|
#{
|
||||||
|
<<"tcp">> => #{
|
||||||
|
<<"listener0">> => ListenerConf#{
|
||||||
|
?CONF_NS_BINARY => AuthnConfig0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{}
|
||||||
|
),
|
||||||
|
lists:foreach(
|
||||||
|
fun(Key) ->
|
||||||
|
[#{ssl := #{Key := FilePath}}] = emqx_config:get([
|
||||||
|
listeners, tcp, listener0, authentication
|
||||||
|
]),
|
||||||
|
?assert(filelib:is_regular(FilePath))
|
||||||
|
end,
|
||||||
|
[cacertfile, certfile, keyfile]
|
||||||
|
).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Helper Functions
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
listener_mqtt_tcp_conf(Config) ->
|
||||||
|
Port = ?config(port, Config),
|
||||||
|
PortS = integer_to_binary(Port),
|
||||||
|
#{
|
||||||
|
<<"acceptors">> => 16,
|
||||||
|
<<"access_rules">> => [<<"allow all">>],
|
||||||
|
<<"bind">> => <<"0.0.0.0:", PortS/binary>>,
|
||||||
|
<<"max_connections">> => 1024000,
|
||||||
|
<<"mountpoint">> => <<>>,
|
||||||
|
<<"proxy_protocol">> => false,
|
||||||
|
<<"proxy_protocol_timeout">> => <<"3s">>,
|
||||||
|
<<"enable_authn">> => true
|
||||||
|
}.
|
||||||
|
|
||||||
|
some_pem() ->
|
||||||
|
Dir = code:lib_dir(emqx_authn, test),
|
||||||
|
Path = filename:join([Dir, "data", "private_key.pem"]),
|
||||||
|
{ok, Pem} = file:read_file(Path),
|
||||||
|
Pem.
|
|
@ -20,8 +20,7 @@
|
||||||
-compile(nowarn_export_all).
|
-compile(nowarn_export_all).
|
||||||
|
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
-include_lib("common_test/include/ct.hrl").
|
||||||
-include("emqx_authn.hrl").
|
|
||||||
|
|
||||||
-define(AUTHN_ID, <<"mechanism:backend">>).
|
-define(AUTHN_ID, <<"mechanism:backend">>).
|
||||||
|
|
||||||
|
@ -29,16 +28,16 @@ all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
_ = application:load(emqx_conf),
|
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn], #{
|
||||||
emqx_common_test_helpers:start_apps([emqx_authn]),
|
work_dir => ?config(priv_dir, Config)
|
||||||
Config.
|
}),
|
||||||
|
[{apps, Apps} | Config].
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(Config) ->
|
||||||
emqx_common_test_helpers:stop_apps([emqx_authn]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
init_per_testcase(_Case, Config) ->
|
init_per_testcase(_Case, Config) ->
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
|
||||||
mria:clear_table(emqx_authn_mnesia),
|
mria:clear_table(emqx_authn_mnesia),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,6 @@ all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
init_per_testcase(_TestCase, Config) ->
|
init_per_testcase(_TestCase, Config) ->
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
|
||||||
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
|
@ -46,23 +45,23 @@ end_per_testcase(_TestCase, _Config) ->
|
||||||
ok = mc_worker_api:disconnect(?MONGO_CLIENT).
|
ok = mc_worker_api:disconnect(?MONGO_CLIENT).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
_ = application:load(emqx_conf),
|
|
||||||
case emqx_common_test_helpers:is_tcp_server_available(?MONGO_HOST, ?MONGO_DEFAULT_PORT) of
|
case emqx_common_test_helpers:is_tcp_server_available(?MONGO_HOST, ?MONGO_DEFAULT_PORT) of
|
||||||
true ->
|
true ->
|
||||||
ok = emqx_common_test_helpers:start_apps([emqx_authn]),
|
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn], #{
|
||||||
ok = start_apps([emqx_resource]),
|
work_dir => ?config(priv_dir, Config)
|
||||||
Config;
|
}),
|
||||||
|
[{apps, Apps} | Config];
|
||||||
false ->
|
false ->
|
||||||
{skip, no_mongo}
|
{skip, no_mongo}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(Config) ->
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
?GLOBAL
|
?GLOBAL
|
||||||
),
|
),
|
||||||
ok = stop_apps([emqx_resource]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
ok = emqx_common_test_helpers:stop_apps([emqx_authn]).
|
ok.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Tests
|
%% Tests
|
||||||
|
|
|
@ -33,7 +33,6 @@ all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
init_per_testcase(_TestCase, Config) ->
|
init_per_testcase(_TestCase, Config) ->
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
|
||||||
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
|
@ -42,23 +41,23 @@ init_per_testcase(_TestCase, Config) ->
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
_ = application:load(emqx_conf),
|
|
||||||
case emqx_common_test_helpers:is_tcp_server_available(?MONGO_HOST, ?MONGO_DEFAULT_PORT) of
|
case emqx_common_test_helpers:is_tcp_server_available(?MONGO_HOST, ?MONGO_DEFAULT_PORT) of
|
||||||
true ->
|
true ->
|
||||||
ok = emqx_common_test_helpers:start_apps([emqx_authn]),
|
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn], #{
|
||||||
ok = start_apps([emqx_resource]),
|
work_dir => ?config(priv_dir, Config)
|
||||||
Config;
|
}),
|
||||||
|
[{apps, Apps} | Config];
|
||||||
false ->
|
false ->
|
||||||
{skip, no_mongo}
|
{skip, no_mongo}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(Config) ->
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
?GLOBAL
|
?GLOBAL
|
||||||
),
|
),
|
||||||
ok = stop_apps([emqx_resource]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
ok = emqx_common_test_helpers:stop_apps([emqx_authn]).
|
ok.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Tests
|
%% Tests
|
||||||
|
|
|
@ -37,7 +37,6 @@ groups() ->
|
||||||
[{require_seeds, [], [t_authenticate, t_update, t_destroy]}].
|
[{require_seeds, [], [t_authenticate, t_update, t_destroy]}].
|
||||||
|
|
||||||
init_per_testcase(_, Config) ->
|
init_per_testcase(_, Config) ->
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
|
||||||
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
|
@ -54,11 +53,11 @@ end_per_group(require_seeds, Config) ->
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
_ = application:load(emqx_conf),
|
|
||||||
case emqx_common_test_helpers:is_tcp_server_available(?MYSQL_HOST, ?MYSQL_DEFAULT_PORT) of
|
case emqx_common_test_helpers:is_tcp_server_available(?MYSQL_HOST, ?MYSQL_DEFAULT_PORT) of
|
||||||
true ->
|
true ->
|
||||||
ok = emqx_common_test_helpers:start_apps([emqx_authn]),
|
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn], #{
|
||||||
ok = start_apps([emqx_resource]),
|
work_dir => ?config(priv_dir, Config)
|
||||||
|
}),
|
||||||
{ok, _} = emqx_resource:create_local(
|
{ok, _} = emqx_resource:create_local(
|
||||||
?MYSQL_RESOURCE,
|
?MYSQL_RESOURCE,
|
||||||
?RESOURCE_GROUP,
|
?RESOURCE_GROUP,
|
||||||
|
@ -66,19 +65,19 @@ init_per_suite(Config) ->
|
||||||
mysql_config(),
|
mysql_config(),
|
||||||
#{}
|
#{}
|
||||||
),
|
),
|
||||||
Config;
|
[{apps, Apps} | Config];
|
||||||
false ->
|
false ->
|
||||||
{skip, no_mysql}
|
{skip, no_mysql}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(Config) ->
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
?GLOBAL
|
?GLOBAL
|
||||||
),
|
),
|
||||||
ok = emqx_resource:remove_local(?MYSQL_RESOURCE),
|
ok = emqx_resource:remove_local(?MYSQL_RESOURCE),
|
||||||
ok = stop_apps([emqx_resource]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
ok = emqx_common_test_helpers:stop_apps([emqx_authn]).
|
ok.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Tests
|
%% Tests
|
||||||
|
|
|
@ -36,7 +36,6 @@ groups() ->
|
||||||
[].
|
[].
|
||||||
|
|
||||||
init_per_testcase(_, Config) ->
|
init_per_testcase(_, Config) ->
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
|
||||||
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
|
@ -45,23 +44,23 @@ init_per_testcase(_, Config) ->
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
_ = application:load(emqx_conf),
|
|
||||||
case emqx_common_test_helpers:is_tcp_server_available(?MYSQL_HOST, ?MYSQL_DEFAULT_PORT) of
|
case emqx_common_test_helpers:is_tcp_server_available(?MYSQL_HOST, ?MYSQL_DEFAULT_PORT) of
|
||||||
true ->
|
true ->
|
||||||
ok = emqx_common_test_helpers:start_apps([emqx_authn]),
|
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn], #{
|
||||||
ok = start_apps([emqx_resource]),
|
work_dir => ?config(priv_dir, Config)
|
||||||
Config;
|
}),
|
||||||
|
[{apps, Apps} | Config];
|
||||||
false ->
|
false ->
|
||||||
{skip, no_mysql_tls}
|
{skip, no_mysql_tls}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(Config) ->
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
?GLOBAL
|
?GLOBAL
|
||||||
),
|
),
|
||||||
ok = stop_apps([emqx_resource]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
ok = emqx_common_test_helpers:stop_apps([emqx_authn]).
|
ok.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Tests
|
%% Tests
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
-include_lib("emqx_authn/include/emqx_authn.hrl").
|
-include_lib("emqx_authn/include/emqx_authn.hrl").
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
-include_lib("common_test/include/ct.hrl").
|
-include_lib("common_test/include/ct.hrl").
|
||||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
|
||||||
|
|
||||||
-define(PGSQL_HOST, "pgsql").
|
-define(PGSQL_HOST, "pgsql").
|
||||||
-define(PGSQL_RESOURCE, <<"emqx_authn_pgsql_SUITE">>).
|
-define(PGSQL_RESOURCE, <<"emqx_authn_pgsql_SUITE">>).
|
||||||
|
@ -42,7 +41,6 @@ groups() ->
|
||||||
[{require_seeds, [], [t_create, t_authenticate, t_update, t_destroy, t_is_superuser]}].
|
[{require_seeds, [], [t_create, t_authenticate, t_update, t_destroy, t_is_superuser]}].
|
||||||
|
|
||||||
init_per_testcase(_, Config) ->
|
init_per_testcase(_, Config) ->
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
|
||||||
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
|
@ -59,11 +57,11 @@ end_per_group(require_seeds, Config) ->
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
_ = application:load(emqx_conf),
|
|
||||||
case emqx_common_test_helpers:is_tcp_server_available(?PGSQL_HOST, ?PGSQL_DEFAULT_PORT) of
|
case emqx_common_test_helpers:is_tcp_server_available(?PGSQL_HOST, ?PGSQL_DEFAULT_PORT) of
|
||||||
true ->
|
true ->
|
||||||
ok = emqx_common_test_helpers:start_apps([emqx_authn]),
|
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn], #{
|
||||||
ok = start_apps([emqx_resource]),
|
work_dir => ?config(priv_dir, Config)
|
||||||
|
}),
|
||||||
{ok, _} = emqx_resource:create_local(
|
{ok, _} = emqx_resource:create_local(
|
||||||
?PGSQL_RESOURCE,
|
?PGSQL_RESOURCE,
|
||||||
?RESOURCE_GROUP,
|
?RESOURCE_GROUP,
|
||||||
|
@ -71,19 +69,19 @@ init_per_suite(Config) ->
|
||||||
pgsql_config(),
|
pgsql_config(),
|
||||||
#{}
|
#{}
|
||||||
),
|
),
|
||||||
Config;
|
[{apps, Apps} | Config];
|
||||||
false ->
|
false ->
|
||||||
{skip, no_pgsql}
|
{skip, no_pgsql}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(Config) ->
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
?GLOBAL
|
?GLOBAL
|
||||||
),
|
),
|
||||||
ok = emqx_resource:remove_local(?PGSQL_RESOURCE),
|
ok = emqx_resource:remove_local(?PGSQL_RESOURCE),
|
||||||
ok = stop_apps([emqx_resource]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
ok = emqx_common_test_helpers:stop_apps([emqx_authn]).
|
ok.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Tests
|
%% Tests
|
||||||
|
|
|
@ -48,20 +48,21 @@ init_per_suite(Config) ->
|
||||||
_ = application:load(emqx_conf),
|
_ = application:load(emqx_conf),
|
||||||
case emqx_common_test_helpers:is_tcp_server_available(?PGSQL_HOST, ?PGSQL_DEFAULT_PORT) of
|
case emqx_common_test_helpers:is_tcp_server_available(?PGSQL_HOST, ?PGSQL_DEFAULT_PORT) of
|
||||||
true ->
|
true ->
|
||||||
ok = emqx_common_test_helpers:start_apps([emqx_authn]),
|
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn], #{
|
||||||
ok = start_apps([emqx_resource]),
|
work_dir => ?config(priv_dir, Config)
|
||||||
Config;
|
}),
|
||||||
|
[{apps, Apps} | Config];
|
||||||
false ->
|
false ->
|
||||||
{skip, no_pgsql_tls}
|
{skip, no_pgsql_tls}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(Config) ->
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
?GLOBAL
|
?GLOBAL
|
||||||
),
|
),
|
||||||
ok = stop_apps([emqx_resource]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
ok = emqx_common_test_helpers:stop_apps([emqx_authn]).
|
ok.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Tests
|
%% Tests
|
||||||
|
|
|
@ -42,7 +42,6 @@ groups() ->
|
||||||
[{require_seeds, [], [t_authenticate, t_update, t_destroy]}].
|
[{require_seeds, [], [t_authenticate, t_update, t_destroy]}].
|
||||||
|
|
||||||
init_per_testcase(_, Config) ->
|
init_per_testcase(_, Config) ->
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
|
||||||
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
|
@ -59,11 +58,11 @@ end_per_group(require_seeds, Config) ->
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
_ = application:load(emqx_conf),
|
|
||||||
case emqx_common_test_helpers:is_tcp_server_available(?REDIS_HOST, ?REDIS_DEFAULT_PORT) of
|
case emqx_common_test_helpers:is_tcp_server_available(?REDIS_HOST, ?REDIS_DEFAULT_PORT) of
|
||||||
true ->
|
true ->
|
||||||
ok = emqx_common_test_helpers:start_apps([emqx_authn]),
|
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn], #{
|
||||||
ok = start_apps([emqx_resource]),
|
work_dir => ?config(priv_dir, Config)
|
||||||
|
}),
|
||||||
{ok, _} = emqx_resource:create_local(
|
{ok, _} = emqx_resource:create_local(
|
||||||
?REDIS_RESOURCE,
|
?REDIS_RESOURCE,
|
||||||
?RESOURCE_GROUP,
|
?RESOURCE_GROUP,
|
||||||
|
@ -71,19 +70,19 @@ init_per_suite(Config) ->
|
||||||
redis_config(),
|
redis_config(),
|
||||||
#{}
|
#{}
|
||||||
),
|
),
|
||||||
Config;
|
[{apps, Apps} | Config];
|
||||||
false ->
|
false ->
|
||||||
{skip, no_redis}
|
{skip, no_redis}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(Config) ->
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
?GLOBAL
|
?GLOBAL
|
||||||
),
|
),
|
||||||
ok = emqx_resource:remove_local(?REDIS_RESOURCE),
|
ok = emqx_resource:remove_local(?REDIS_RESOURCE),
|
||||||
ok = stop_apps([emqx_resource]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
ok = emqx_common_test_helpers:stop_apps([emqx_authn]).
|
ok.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Tests
|
%% Tests
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
-compile(nowarn_export_all).
|
-compile(nowarn_export_all).
|
||||||
-compile(export_all).
|
-compile(export_all).
|
||||||
|
|
||||||
-include_lib("emqx_connector/include/emqx_connector.hrl").
|
|
||||||
-include_lib("emqx_authn/include/emqx_authn.hrl").
|
-include_lib("emqx_authn/include/emqx_authn.hrl").
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
-include_lib("common_test/include/ct.hrl").
|
-include_lib("common_test/include/ct.hrl").
|
||||||
|
@ -36,7 +35,6 @@ groups() ->
|
||||||
[].
|
[].
|
||||||
|
|
||||||
init_per_testcase(_, Config) ->
|
init_per_testcase(_, Config) ->
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
|
||||||
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
|
@ -45,23 +43,23 @@ init_per_testcase(_, Config) ->
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
_ = application:load(emqx_conf),
|
|
||||||
case emqx_common_test_helpers:is_tcp_server_available(?REDIS_HOST, ?REDIS_TLS_PORT) of
|
case emqx_common_test_helpers:is_tcp_server_available(?REDIS_HOST, ?REDIS_TLS_PORT) of
|
||||||
true ->
|
true ->
|
||||||
ok = emqx_common_test_helpers:start_apps([emqx_authn]),
|
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn], #{
|
||||||
ok = start_apps([emqx_resource]),
|
work_dir => ?config(priv_dir, Config)
|
||||||
Config;
|
}),
|
||||||
|
[{apps, Apps} | Config];
|
||||||
false ->
|
false ->
|
||||||
{skip, no_redis}
|
{skip, no_redis}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(Config) ->
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
?GLOBAL
|
?GLOBAL
|
||||||
),
|
),
|
||||||
ok = stop_apps([emqx_resource]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
ok = emqx_common_test_helpers:stop_apps([emqx_authn]).
|
ok.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Tests
|
%% Tests
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
-compile(nowarn_export_all).
|
-compile(nowarn_export_all).
|
||||||
|
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
-include_lib("common_test/include/ct.hrl").
|
||||||
|
|
||||||
-include("emqx_authn.hrl").
|
-include("emqx_authn.hrl").
|
||||||
|
|
||||||
|
@ -11,16 +12,16 @@ all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
_ = application:load(emqx_conf),
|
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn], #{
|
||||||
emqx_common_test_helpers:start_apps([emqx_authn]),
|
work_dir => ?config(priv_dir, Config)
|
||||||
Config.
|
}),
|
||||||
|
[{apps, Apps} | Config].
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(Config) ->
|
||||||
emqx_common_test_helpers:stop_apps([emqx_authn]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
init_per_testcase(_Case, Config) ->
|
init_per_testcase(_Case, Config) ->
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
|
||||||
mria:clear_table(emqx_authn_mnesia),
|
mria:clear_table(emqx_authn_mnesia),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
|
|
|
@ -36,17 +36,18 @@ all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
_ = application:load(emqx_conf),
|
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn], #{
|
||||||
ok = emqx_common_test_helpers:start_apps([emqx_authn]),
|
work_dir => ?config(priv_dir, Config)
|
||||||
|
}),
|
||||||
IdleTimeout = emqx_config:get([mqtt, idle_timeout]),
|
IdleTimeout = emqx_config:get([mqtt, idle_timeout]),
|
||||||
[{idle_timeout, IdleTimeout} | Config].
|
[{apps, Apps}, {idle_timeout, IdleTimeout} | Config].
|
||||||
|
|
||||||
end_per_suite(Config) ->
|
end_per_suite(Config) ->
|
||||||
ok = emqx_config:put([mqtt, idle_timeout], ?config(idle_timeout, Config)),
|
ok = emqx_config:put([mqtt, idle_timeout], ?config(idle_timeout, Config)),
|
||||||
ok = emqx_common_test_helpers:stop_apps([emqx_authn]).
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
|
ok.
|
||||||
|
|
||||||
init_per_testcase(_Case, Config) ->
|
init_per_testcase(_Case, Config) ->
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
|
||||||
mria:clear_table(emqx_enhanced_authn_scram_mnesia),
|
mria:clear_table(emqx_enhanced_authn_scram_mnesia),
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
%% -*- mode: erlang -*-
|
%% -*- mode: erlang -*-
|
||||||
|
|
||||||
{erl_opts, [debug_info]}.
|
{erl_opts, [debug_info]}.
|
||||||
{deps, [{emqx, {path, "../emqx"}}]}.
|
{deps, [
|
||||||
|
{emqx, {path, "../emqx"}},
|
||||||
|
{emqx_authn, {path, "../emqx_authn"}}
|
||||||
|
]}.
|
||||||
|
|
||||||
{shell, [
|
{shell, [
|
||||||
% {config, "config/sys.config"},
|
% {config, "config/sys.config"},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{application, emqx_conf, [
|
{application, emqx_conf, [
|
||||||
{description, "EMQX configuration management"},
|
{description, "EMQX configuration management"},
|
||||||
{vsn, "0.1.26"},
|
{vsn, "0.1.27"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{mod, {emqx_conf_app, []}},
|
{mod, {emqx_conf_app, []}},
|
||||||
{applications, [kernel, stdlib, emqx_ctl]},
|
{applications, [kernel, stdlib, emqx_ctl]},
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
|
|
||||||
-module(emqx_conf_cli).
|
-module(emqx_conf_cli).
|
||||||
-include("emqx_conf.hrl").
|
-include("emqx_conf.hrl").
|
||||||
-include_lib("emqx/include/emqx_access_control.hrl").
|
-include_lib("emqx_authn/include/emqx_authentication.hrl").
|
||||||
-include_lib("emqx/include/emqx_authentication.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
load/0,
|
load/0,
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
-dialyzer(no_unused).
|
-dialyzer(no_unused).
|
||||||
-dialyzer(no_fail_call).
|
-dialyzer(no_fail_call).
|
||||||
|
|
||||||
|
-include_lib("emqx/include/emqx_access_control.hrl").
|
||||||
-include_lib("typerefl/include/types.hrl").
|
-include_lib("typerefl/include/types.hrl").
|
||||||
-include_lib("hocon/include/hoconsc.hrl").
|
-include_lib("hocon/include/hoconsc.hrl").
|
||||||
-include_lib("emqx/include/emqx_authentication.hrl").
|
|
||||||
|
|
||||||
-type log_level() :: debug | info | notice | warning | error | critical | alert | emergency | all.
|
-type log_level() :: debug | info | notice | warning | error | critical | alert | emergency | all.
|
||||||
-type file() :: string().
|
-type file() :: string().
|
||||||
|
@ -66,6 +66,10 @@
|
||||||
emqx_otel_schema,
|
emqx_otel_schema,
|
||||||
emqx_mgmt_api_key_schema
|
emqx_mgmt_api_key_schema
|
||||||
]).
|
]).
|
||||||
|
-define(INJECTING_CONFIGS, [
|
||||||
|
emqx_authn_schema
|
||||||
|
]).
|
||||||
|
|
||||||
%% 1 million default ports counter
|
%% 1 million default ports counter
|
||||||
-define(DEFAULT_MAX_PORTS, 1024 * 1024).
|
-define(DEFAULT_MAX_PORTS, 1024 * 1024).
|
||||||
|
|
||||||
|
@ -76,11 +80,7 @@ tags() ->
|
||||||
[<<"EMQX">>].
|
[<<"EMQX">>].
|
||||||
|
|
||||||
roots() ->
|
roots() ->
|
||||||
PtKey = ?EMQX_AUTHENTICATION_SCHEMA_MODULE_PT_KEY,
|
ok = emqx_schema_hooks:inject_from_modules(?INJECTING_CONFIGS),
|
||||||
case persistent_term:get(PtKey, undefined) of
|
|
||||||
undefined -> persistent_term:put(PtKey, emqx_authn_schema);
|
|
||||||
_ -> ok
|
|
||||||
end,
|
|
||||||
emqx_schema_high_prio_roots() ++
|
emqx_schema_high_prio_roots() ++
|
||||||
[
|
[
|
||||||
{"node",
|
{"node",
|
||||||
|
|
|
@ -2,5 +2,6 @@
|
||||||
{erl_opts, [debug_info]}.
|
{erl_opts, [debug_info]}.
|
||||||
{deps, [
|
{deps, [
|
||||||
{emqx, {path, "../emqx"}},
|
{emqx, {path, "../emqx"}},
|
||||||
{emqx_utils, {path, "../emqx_utils"}}
|
{emqx_utils, {path, "../emqx_utils"}},
|
||||||
|
{emqx_authn, {path, "../emqx_authn"}}
|
||||||
]}.
|
]}.
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
-include_lib("emqx/include/emqx_authentication.hrl").
|
-include_lib("emqx_authn/include/emqx_authentication.hrl").
|
||||||
-define(AUTHN_BIN, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY).
|
-define(AUTHN_BIN, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY).
|
||||||
|
|
||||||
-type atom_or_bin() :: atom() | binary().
|
-type atom_or_bin() :: atom() | binary().
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
-include("include/emqx_gateway.hrl").
|
-include("include/emqx_gateway.hrl").
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
-include_lib("emqx/include/emqx_authentication.hrl").
|
-include_lib("emqx_authn/include/emqx_authentication.hrl").
|
||||||
|
|
||||||
-define(AUTHN, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM).
|
-define(AUTHN, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM).
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,9 @@
|
||||||
-dialyzer(no_unused).
|
-dialyzer(no_unused).
|
||||||
-dialyzer(no_fail_call).
|
-dialyzer(no_fail_call).
|
||||||
|
|
||||||
-include_lib("emqx/include/emqx_authentication.hrl").
|
|
||||||
-include_lib("hocon/include/hoconsc.hrl").
|
-include_lib("hocon/include/hoconsc.hrl").
|
||||||
-include_lib("typerefl/include/types.hrl").
|
-include_lib("typerefl/include/types.hrl").
|
||||||
|
-include_lib("emqx_authn/include/emqx_authentication.hrl").
|
||||||
|
|
||||||
-type ip_port() :: tuple() | integer().
|
-type ip_port() :: tuple() | integer().
|
||||||
-type duration() :: non_neg_integer().
|
-type duration() :: non_neg_integer().
|
||||||
|
|
|
@ -17,15 +17,19 @@ all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
ok = emqx_common_test_helpers:start_apps([emqx_conf, emqx_authn, emqx_retainer, emqx_gcp_device]),
|
Apps = emqx_cth_suite:start(
|
||||||
Config.
|
[emqx, emqx_conf, emqx_authn, emqx_gcp_device, {emqx_retainer, "retainer {enable = true}"}],
|
||||||
|
#{
|
||||||
|
work_dir => ?config(priv_dir, Config)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
[{apps, Apps} | Config].
|
||||||
|
|
||||||
end_per_suite(Config) ->
|
end_per_suite(Config) ->
|
||||||
_ = emqx_common_test_helpers:stop_apps([emqx_authn, emqx_retainer, emqx_gcp_device]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
Config.
|
ok.
|
||||||
|
|
||||||
init_per_testcase(_TestCase, Config) ->
|
init_per_testcase(_TestCase, Config) ->
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
?GLOBAL
|
?GLOBAL
|
||||||
|
|
|
@ -14,32 +14,34 @@
|
||||||
-include_lib("emqx/include/emqx.hrl").
|
-include_lib("emqx/include/emqx.hrl").
|
||||||
|
|
||||||
-define(PATH, [authentication]).
|
-define(PATH, [authentication]).
|
||||||
-define(BASE_CONF, <<
|
|
||||||
""
|
|
||||||
"\n"
|
|
||||||
"retainer {\n"
|
|
||||||
" enable = true\n"
|
|
||||||
"}"
|
|
||||||
""
|
|
||||||
>>).
|
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
ok = emqx_config:init_load(emqx_retainer_schema, ?BASE_CONF),
|
Apps = emqx_cth_suite:start(
|
||||||
ok = emqx_common_test_helpers:start_apps([emqx_gcp_device, emqx_authn, emqx_conf, emqx_retainer]),
|
[
|
||||||
emqx_dashboard_api_test_helpers:set_default_config(),
|
emqx,
|
||||||
emqx_mgmt_api_test_util:init_suite(),
|
emqx_conf,
|
||||||
Config.
|
emqx_authn,
|
||||||
|
{emqx_retainer, "retainer {enable = true}"},
|
||||||
|
emqx_management,
|
||||||
|
{emqx_dashboard, "dashboard.listeners.http { enable = true, bind = 18083 }"},
|
||||||
|
emqx_gcp_device
|
||||||
|
],
|
||||||
|
#{
|
||||||
|
work_dir => ?config(priv_dir, Config)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
_ = emqx_common_test_http:create_default_app(),
|
||||||
|
[{apps, Apps} | Config].
|
||||||
|
|
||||||
end_per_suite(Config) ->
|
end_per_suite(Config) ->
|
||||||
emqx_mgmt_api_test_util:end_suite(),
|
_ = emqx_common_test_http:delete_default_app(),
|
||||||
_ = emqx_common_test_helpers:stop_apps([emqx_authn, emqx_retainer, emqx_gcp_device]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
init_per_testcase(_TestCase, Config) ->
|
init_per_testcase(_TestCase, Config) ->
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
?GLOBAL
|
?GLOBAL
|
||||||
|
|
|
@ -23,7 +23,9 @@ all() ->
|
||||||
|
|
||||||
init_per_suite(Config0) ->
|
init_per_suite(Config0) ->
|
||||||
ok = snabbkaffe:start_trace(),
|
ok = snabbkaffe:start_trace(),
|
||||||
emqx_common_test_helpers:start_apps([emqx_conf, emqx_authn, emqx_gcp_device]),
|
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn, emqx_gcp_device], #{
|
||||||
|
work_dir => ?config(priv_dir, Config0)
|
||||||
|
}),
|
||||||
ValidExpirationTime = erlang:system_time(second) + 3600,
|
ValidExpirationTime = erlang:system_time(second) + 3600,
|
||||||
ValidJWT = generate_jws(ValidExpirationTime),
|
ValidJWT = generate_jws(ValidExpirationTime),
|
||||||
ExpiredJWT = generate_jws(0),
|
ExpiredJWT = generate_jws(0),
|
||||||
|
@ -35,16 +37,16 @@ init_per_suite(Config0) ->
|
||||||
{valid_jwt, ValidJWT},
|
{valid_jwt, ValidJWT},
|
||||||
{expired_jwt, ExpiredJWT},
|
{expired_jwt, ExpiredJWT},
|
||||||
{valid_client, ValidClient},
|
{valid_client, ValidClient},
|
||||||
{expired_client, ExpiredClient}
|
{expired_client, ExpiredClient},
|
||||||
|
{apps, Apps}
|
||||||
| Config0
|
| Config0
|
||||||
].
|
].
|
||||||
|
|
||||||
end_per_suite(_) ->
|
end_per_suite(Config) ->
|
||||||
_ = emqx_common_test_helpers:stop_apps([emqx_authn, emqx_gcp_device]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
init_per_testcase(_, Config) ->
|
init_per_testcase(_, Config) ->
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_testcase(_Case, Config) ->
|
end_per_testcase(_Case, Config) ->
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
-include_lib("emqx_authn/include/emqx_authn.hrl").
|
-include_lib("emqx_authn/include/emqx_authn.hrl").
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
-include_lib("common_test/include/ct.hrl").
|
||||||
|
|
||||||
-define(LDAP_HOST, "ldap").
|
-define(LDAP_HOST, "ldap").
|
||||||
-define(LDAP_DEFAULT_PORT, 389).
|
-define(LDAP_DEFAULT_PORT, 389).
|
||||||
|
@ -20,7 +21,6 @@ all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
init_per_testcase(_, Config) ->
|
init_per_testcase(_, Config) ->
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
|
||||||
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
|
@ -32,8 +32,9 @@ init_per_suite(Config) ->
|
||||||
_ = application:load(emqx_conf),
|
_ = application:load(emqx_conf),
|
||||||
case emqx_common_test_helpers:is_tcp_server_available(?LDAP_HOST, ?LDAP_DEFAULT_PORT) of
|
case emqx_common_test_helpers:is_tcp_server_available(?LDAP_HOST, ?LDAP_DEFAULT_PORT) of
|
||||||
true ->
|
true ->
|
||||||
ok = emqx_common_test_helpers:start_apps([emqx_authn]),
|
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn], #{
|
||||||
ok = start_apps([emqx_resource]),
|
work_dir => ?config(priv_dir, Config)
|
||||||
|
}),
|
||||||
{ok, _} = emqx_resource:create_local(
|
{ok, _} = emqx_resource:create_local(
|
||||||
?LDAP_RESOURCE,
|
?LDAP_RESOURCE,
|
||||||
?RESOURCE_GROUP,
|
?RESOURCE_GROUP,
|
||||||
|
@ -41,19 +42,18 @@ init_per_suite(Config) ->
|
||||||
ldap_config(),
|
ldap_config(),
|
||||||
#{}
|
#{}
|
||||||
),
|
),
|
||||||
Config;
|
[{apps, Apps} | Config];
|
||||||
false ->
|
false ->
|
||||||
{skip, no_ldap}
|
{skip, no_ldap}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(Config) ->
|
||||||
emqx_authn_test_lib:delete_authenticators(
|
emqx_authn_test_lib:delete_authenticators(
|
||||||
[authentication],
|
[authentication],
|
||||||
?GLOBAL
|
?GLOBAL
|
||||||
),
|
),
|
||||||
ok = emqx_resource:remove_local(?LDAP_RESOURCE),
|
ok = emqx_resource:remove_local(?LDAP_RESOURCE),
|
||||||
ok = stop_apps([emqx_resource]),
|
ok = emqx_cth_suite:stop(?config(apps, Config)).
|
||||||
ok = emqx_common_test_helpers:stop_apps([emqx_authn]).
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Tests
|
%% Tests
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
{deps, [
|
{deps, [
|
||||||
{emqx, {path, "../emqx"}},
|
{emqx, {path, "../emqx"}},
|
||||||
{emqx_dashboard, {path, "../emqx_dashboard"}},
|
{emqx_dashboard, {path, "../emqx_dashboard"}},
|
||||||
|
{emqx_conf, {path, "../emqx_conf"}},
|
||||||
{emqx_utils, {path, "../emqx_utils"}}
|
{emqx_utils, {path, "../emqx_utils"}}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,32 @@
|
||||||
emqx_authn_schema {
|
emqx_authn_schema {
|
||||||
|
|
||||||
|
global_authentication.desc:
|
||||||
|
"""Default authentication configs for all MQTT listeners.
|
||||||
|
|
||||||
|
For per-listener overrides see <code>authentication</code> in listener configs
|
||||||
|
|
||||||
|
This option can be configured with:
|
||||||
|
<ul>
|
||||||
|
<li><code>[]</code>: The default value, it allows *ALL* logins</li>
|
||||||
|
<li>one: For example <code>{enable:true,backend:"built_in_database",mechanism="password_based"}</code></li>
|
||||||
|
<li>chain: An array of structs.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
When a chain is configured, the login credentials are checked against the backends per the configured order, until an 'allow' or 'deny' decision can be made.
|
||||||
|
|
||||||
|
If there is no decision after a full chain exhaustion, the login is rejected."""
|
||||||
|
|
||||||
|
global_authentication.label:
|
||||||
|
"""Global authentication"""
|
||||||
|
|
||||||
|
listener_authentication.desc:
|
||||||
|
"""Per-listener authentication override.
|
||||||
|
Authentication can be one single authenticator instance or a chain of authenticators as an array.
|
||||||
|
When authenticating a login (username, client ID, etc.) the authenticators are checked in the configured order."""
|
||||||
|
|
||||||
|
listener_authentication.label:
|
||||||
|
"""Per-listener authentication override"""
|
||||||
|
|
||||||
backend.desc:
|
backend.desc:
|
||||||
"""Backend type."""
|
"""Backend type."""
|
||||||
|
|
||||||
|
|
|
@ -532,22 +532,6 @@ mqtt_server_keepalive.desc:
|
||||||
mqtt_server_keepalive.label:
|
mqtt_server_keepalive.label:
|
||||||
"""Server Keep Alive"""
|
"""Server Keep Alive"""
|
||||||
|
|
||||||
global_authentication.desc:
|
|
||||||
"""Default authentication configs for all MQTT listeners.
|
|
||||||
|
|
||||||
For per-listener overrides see <code>authentication</code> in listener configs
|
|
||||||
|
|
||||||
This option can be configured with:
|
|
||||||
<ul>
|
|
||||||
<li><code>[]</code>: The default value, it allows *ALL* logins</li>
|
|
||||||
<li>one: For example <code>{enable:true,backend:"built_in_database",mechanism="password_based"}</code></li>
|
|
||||||
<li>chain: An array of structs.</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
When a chain is configured, the login credentials are checked against the backends per the configured order, until an 'allow' or 'deny' decision can be made.
|
|
||||||
|
|
||||||
If there is no decision after a full chain exhaustion, the login is rejected."""
|
|
||||||
|
|
||||||
fields_mqtt_quic_listener_load_balancing_mode.desc:
|
fields_mqtt_quic_listener_load_balancing_mode.desc:
|
||||||
"""0: Disabled, 1: SERVER_ID_IP, 2: SERVER_ID_FIXED. default: 0"""
|
"""0: Disabled, 1: SERVER_ID_IP, 2: SERVER_ID_FIXED. default: 0"""
|
||||||
|
|
||||||
|
@ -1103,14 +1087,6 @@ See: https://erlang.org/doc/man/inet.html#setopts-2"""
|
||||||
fields_tcp_opts_active_n.label:
|
fields_tcp_opts_active_n.label:
|
||||||
"""active_n"""
|
"""active_n"""
|
||||||
|
|
||||||
listener_authentication.desc:
|
|
||||||
"""Per-listener authentication override.
|
|
||||||
Authentication can be one single authenticator instance or a chain of authenticators as an array.
|
|
||||||
When authenticating a login (username, client ID, etc.) the authenticators are checked in the configured order."""
|
|
||||||
|
|
||||||
listener_authentication.label:
|
|
||||||
"""Per-listener authentication override"""
|
|
||||||
|
|
||||||
fields_trace_payload_encode.desc:
|
fields_trace_payload_encode.desc:
|
||||||
"""Determine the format of the payload format in the trace file.<br/>
|
"""Determine the format of the payload format in the trace file.<br/>
|
||||||
`text`: Text-based protocol or plain text protocol.
|
`text`: Text-based protocol or plain text protocol.
|
||||||
|
|
Loading…
Reference in New Issue