refactor(authn): align authn config root name
authn configs are checked independently per-auth provider, this was to make authn providers more plugable. in order to make environment variable overrides work for authn, we need to have a unified view of the config layout, no matter from root level, or partially checking per-provider config independently, i.e. we try to use the same config envelop.
This commit is contained in:
parent
1070948a70
commit
5d3cb6ae1c
|
@ -0,0 +1,31 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2021 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.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-ifndef(EMQX_AUTHENTICATION_HRL).
|
||||||
|
-define(EMQX_AUTHENTICATION_HRL, true).
|
||||||
|
|
||||||
|
%% config root name all auth providers have to agree on.
|
||||||
|
-define(EMQX_AUTHENTICATION_CONFIG_ROOT_NAME, "authentication").
|
||||||
|
-define(EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM, 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).
|
||||||
|
|
||||||
|
-endif.
|
|
@ -24,9 +24,12 @@
|
||||||
|
|
||||||
-include("emqx.hrl").
|
-include("emqx.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
-include("emqx_authentication.hrl").
|
||||||
|
|
||||||
-include_lib("stdlib/include/ms_transform.hrl").
|
-include_lib("stdlib/include/ms_transform.hrl").
|
||||||
|
|
||||||
|
-define(CONF_ROOT, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM).
|
||||||
|
|
||||||
%% The authentication entrypoint.
|
%% The authentication entrypoint.
|
||||||
-export([ authenticate/2
|
-export([ authenticate/2
|
||||||
]).
|
]).
|
||||||
|
@ -383,8 +386,8 @@ list_users(ChainName, AuthenticatorID, Params) ->
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
init(_Opts) ->
|
init(_Opts) ->
|
||||||
ok = emqx_config_handler:add_handler([authentication], ?MODULE),
|
ok = emqx_config_handler:add_handler([?CONF_ROOT], ?MODULE),
|
||||||
ok = emqx_config_handler:add_handler([listeners, '?', '?', authentication], ?MODULE),
|
ok = emqx_config_handler:add_handler([listeners, '?', '?', ?CONF_ROOT], ?MODULE),
|
||||||
{ok, #{hooked => false, providers => #{}}}.
|
{ok, #{hooked => false, providers => #{}}}.
|
||||||
|
|
||||||
handle_call(get_providers, _From, #{providers := Providers} = State) ->
|
handle_call(get_providers, _From, #{providers := Providers} = State) ->
|
||||||
|
@ -496,8 +499,8 @@ terminate(Reason, _State) ->
|
||||||
Other -> ?SLOG(error, #{msg => "emqx_authentication_terminating",
|
Other -> ?SLOG(error, #{msg => "emqx_authentication_terminating",
|
||||||
reason => Other})
|
reason => Other})
|
||||||
end,
|
end,
|
||||||
emqx_config_handler:remove_handler([authentication]),
|
emqx_config_handler:remove_handler([?CONF_ROOT]),
|
||||||
emqx_config_handler:remove_handler([listeners, '?', '?', authentication]),
|
emqx_config_handler:remove_handler([listeners, '?', '?', ?CONF_ROOT]),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
-export_type([config/0]).
|
-export_type([config/0]).
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
-include("emqx_authentication.hrl").
|
||||||
|
|
||||||
-type parsed_config() :: #{mechanism := atom(),
|
-type parsed_config() :: #{mechanism := atom(),
|
||||||
backend => atom(),
|
backend => atom(),
|
||||||
|
@ -132,9 +133,9 @@ do_post_config_update({move_authenticator, ChainName, AuthenticatorID, Position}
|
||||||
|
|
||||||
check_configs(Configs) ->
|
check_configs(Configs) ->
|
||||||
Providers = emqx_authentication:get_providers(),
|
Providers = emqx_authentication:get_providers(),
|
||||||
lists:map(fun(C) -> do_check_conifg(C, Providers) end, Configs).
|
lists:map(fun(C) -> do_check_config(C, Providers) end, Configs).
|
||||||
|
|
||||||
do_check_conifg(Config, Providers) ->
|
do_check_config(Config, Providers) ->
|
||||||
Type = authn_type(Config),
|
Type = authn_type(Config),
|
||||||
case maps:get(Type, Providers, false) of
|
case maps:get(Type, Providers, false) of
|
||||||
false ->
|
false ->
|
||||||
|
@ -143,19 +144,20 @@ do_check_conifg(Config, Providers) ->
|
||||||
providers => Providers}),
|
providers => Providers}),
|
||||||
throw({unknown_authn_type, Type});
|
throw({unknown_authn_type, Type});
|
||||||
Module ->
|
Module ->
|
||||||
do_check_conifg(Type, Config, Module)
|
do_check_config(Type, Config, Module)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
do_check_conifg(Type, Config, Module) ->
|
do_check_config(Type, Config, Module) ->
|
||||||
F = case erlang:function_exported(Module, check_config, 1) of
|
F = case erlang:function_exported(Module, check_config, 1) of
|
||||||
true ->
|
true ->
|
||||||
fun Module:check_config/1;
|
fun Module:check_config/1;
|
||||||
false ->
|
false ->
|
||||||
fun(C) ->
|
fun(C) ->
|
||||||
#{config := R} =
|
Key = list_to_binary(?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME),
|
||||||
hocon_schema:check_plain(Module, #{<<"config">> => C},
|
AtomKey = list_to_atom(?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME),
|
||||||
|
R = hocon_schema:check_plain(Module, #{Key => C},
|
||||||
#{atom_key => true}),
|
#{atom_key => true}),
|
||||||
R
|
maps:get(AtomKey, R)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
try
|
try
|
||||||
|
@ -261,8 +263,8 @@ authn_type(#{mechanism := M}) -> atom(M);
|
||||||
authn_type(#{<<"mechanism">> := M, <<"backend">> := B}) -> {atom(M), atom(B)};
|
authn_type(#{<<"mechanism">> := M, <<"backend">> := B}) -> {atom(M), atom(B)};
|
||||||
authn_type(#{<<"mechanism">> := M}) -> atom(M).
|
authn_type(#{<<"mechanism">> := M}) -> atom(M).
|
||||||
|
|
||||||
atom(Bin) ->
|
atom(A) when is_atom(A) -> A;
|
||||||
binary_to_existing_atom(Bin, utf8).
|
atom(Bin) -> binary_to_existing_atom(Bin, utf8).
|
||||||
|
|
||||||
%% The relative dir for ssl files.
|
%% The relative dir for ssl files.
|
||||||
certs_dir(ChainName, ConfigOrID) ->
|
certs_dir(ChainName, ConfigOrID) ->
|
||||||
|
|
|
@ -268,12 +268,13 @@ init_load(SchemaMod, Conf) when is_list(Conf) orelse is_binary(Conf) ->
|
||||||
}),
|
}),
|
||||||
error(failed_to_load_hocon_conf)
|
error(failed_to_load_hocon_conf)
|
||||||
end;
|
end;
|
||||||
init_load(SchemaMod, RawConf0) when is_map(RawConf0) ->
|
init_load(SchemaMod, RawConf) when is_map(RawConf) ->
|
||||||
ok = save_schema_mod_and_names(SchemaMod),
|
ok = save_schema_mod_and_names(SchemaMod),
|
||||||
%% check and save configs
|
%% check and save configs
|
||||||
{_AppEnvs, CheckedConf} = check_config(SchemaMod, RawConf0),
|
{_AppEnvs, CheckedConf} = check_config(SchemaMod, RawConf),
|
||||||
|
RootNames = get_root_names(),
|
||||||
ok = save_to_config_map(maps:with(get_atom_root_names(), CheckedConf),
|
ok = save_to_config_map(maps:with(get_atom_root_names(), CheckedConf),
|
||||||
maps:with(get_root_names(), RawConf0)).
|
maps:with(RootNames, RawConf)).
|
||||||
|
|
||||||
include_dirs() ->
|
include_dirs() ->
|
||||||
[filename:join(emqx:data_dir(), "configs")].
|
[filename:join(emqx:data_dir(), "configs")].
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
-dialyzer(no_unused).
|
-dialyzer(no_unused).
|
||||||
-dialyzer(no_fail_call).
|
-dialyzer(no_fail_call).
|
||||||
|
|
||||||
|
-include("emqx_authentication.hrl").
|
||||||
-include_lib("typerefl/include/types.hrl").
|
-include_lib("typerefl/include/types.hrl").
|
||||||
|
|
||||||
-type duration() :: integer().
|
-type duration() :: integer().
|
||||||
|
@ -105,7 +106,7 @@ and can not be deleted."""
|
||||||
The configs here work as default values which can be overriden
|
The configs here work as default values which can be overriden
|
||||||
in <code>zone</code> configs"""
|
in <code>zone</code> configs"""
|
||||||
})}
|
})}
|
||||||
, {"authentication",
|
, {?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME,
|
||||||
authentication(
|
authentication(
|
||||||
"""Default authentication configs for all MQTT listeners.<br>
|
"""Default authentication configs for all MQTT listeners.<br>
|
||||||
For per-listener overrides see <code>authentication</code>
|
For per-listener overrides see <code>authentication</code>
|
||||||
|
@ -972,7 +973,7 @@ mqtt_listener() ->
|
||||||
sc(duration(),
|
sc(duration(),
|
||||||
#{})
|
#{})
|
||||||
}
|
}
|
||||||
, {"authentication",
|
, {?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME,
|
||||||
authentication("Per-listener authentication override")
|
authentication("Per-listener authentication override")
|
||||||
}
|
}
|
||||||
].
|
].
|
||||||
|
@ -1436,8 +1437,21 @@ str(S) when is_list(S) ->
|
||||||
S.
|
S.
|
||||||
|
|
||||||
authentication(Desc) ->
|
authentication(Desc) ->
|
||||||
#{ type => hoconsc:lazy(hoconsc:union([typerefl:map(), hoconsc:array(typerefl:map())]))
|
%% authentication schemais lazy to make it more 'plugable'
|
||||||
, desc => iolist_to_binary([Desc, "<br>", """
|
%% the type checks are done in emqx_auth application when it boots.
|
||||||
|
%% and in emqx_authentication_config module for rutime changes.
|
||||||
|
Default = hoconsc:lazy(hoconsc:union([typerefl:map(), hoconsc:array(typerefl:map())])),
|
||||||
|
%% as the type is lazy, the runtime module injection from EMQX_AUTHENTICATION_SCHEMA_MODULE_PT_KEY
|
||||||
|
%% is for now only affecting document generation.
|
||||||
|
%% maybe in the future, we can find a more straightforward way to support
|
||||||
|
%% * document generation (at compile time)
|
||||||
|
%% * type checks before boot (in bin/emqx config generation)
|
||||||
|
%% * type checks at runtime (when changing configs via management API)
|
||||||
|
#{ type => case persistent_term:get(?EMQX_AUTHENTICATION_SCHEMA_MODULE_PT_KEY, undefined) of
|
||||||
|
undefined -> Default;
|
||||||
|
Module -> hoconsc:lazy(Module:root_type())
|
||||||
|
end
|
||||||
|
, desc => iolist_to_binary([Desc, """
|
||||||
Authentication can be one single authenticator instance or a chain of authenticators as an array.
|
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
|
When authenticating a login (username, client ID, etc.) the authenticators are checked
|
||||||
in the configured order.<br>
|
in the configured order.<br>
|
||||||
|
|
|
@ -25,18 +25,11 @@
|
||||||
-include_lib("common_test/include/ct.hrl").
|
-include_lib("common_test/include/ct.hrl").
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
-include_lib("typerefl/include/types.hrl").
|
-include_lib("typerefl/include/types.hrl").
|
||||||
|
-include("emqx_authentication.hrl").
|
||||||
-export([ roots/0, fields/1 ]).
|
|
||||||
|
|
||||||
-export([ create/2
|
|
||||||
, update/2
|
|
||||||
, authenticate/2
|
|
||||||
, destroy/1
|
|
||||||
, check_config/1
|
|
||||||
]).
|
|
||||||
|
|
||||||
-define(AUTHN, emqx_authentication).
|
-define(AUTHN, emqx_authentication).
|
||||||
-define(config(KEY), (fun() -> {KEY, _V_} = lists:keyfind(KEY, 1, Config), _V_ end)()).
|
-define(config(KEY), (fun() -> {KEY, _V_} = lists:keyfind(KEY, 1, Config), _V_ end)()).
|
||||||
|
-define(CONF_ROOT, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Hocon Schema
|
%% Hocon Schema
|
||||||
|
@ -250,7 +243,7 @@ t_update_config({init, Config}) ->
|
||||||
{"auth2", AuthNType2} | Config];
|
{"auth2", AuthNType2} | Config];
|
||||||
|
|
||||||
t_update_config(Config) when is_list(Config) ->
|
t_update_config(Config) when is_list(Config) ->
|
||||||
emqx_config_handler:add_handler([authentication], emqx_authentication),
|
emqx_config_handler:add_handler([?CONF_ROOT], emqx_authentication),
|
||||||
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),
|
||||||
|
@ -267,7 +260,7 @@ t_update_config(Config) when is_list(Config) ->
|
||||||
|
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{ok, _},
|
{ok, _},
|
||||||
update_config([authentication], {create_authenticator, Global, AuthenticatorConfig1})),
|
update_config([?CONF_ROOT], {create_authenticator, Global, AuthenticatorConfig1})),
|
||||||
|
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{ok, #{id := ID1, state := #{mark := 1}}},
|
{ok, #{id := ID1, state := #{mark := 1}}},
|
||||||
|
@ -275,7 +268,7 @@ t_update_config(Config) when is_list(Config) ->
|
||||||
|
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{ok, _},
|
{ok, _},
|
||||||
update_config([authentication], {create_authenticator, Global, AuthenticatorConfig2})),
|
update_config([?CONF_ROOT], {create_authenticator, Global, AuthenticatorConfig2})),
|
||||||
|
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{ok, #{id := ID2, state := #{mark := 1}}},
|
{ok, #{id := ID2, state := #{mark := 1}}},
|
||||||
|
@ -283,7 +276,7 @@ t_update_config(Config) when is_list(Config) ->
|
||||||
|
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{ok, _},
|
{ok, _},
|
||||||
update_config([authentication],
|
update_config([?CONF_ROOT],
|
||||||
{update_authenticator,
|
{update_authenticator,
|
||||||
Global,
|
Global,
|
||||||
ID1,
|
ID1,
|
||||||
|
@ -296,25 +289,25 @@ t_update_config(Config) when is_list(Config) ->
|
||||||
|
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{ok, _},
|
{ok, _},
|
||||||
update_config([authentication], {move_authenticator, Global, ID2, top})),
|
update_config([?CONF_ROOT], {move_authenticator, Global, ID2, top})),
|
||||||
|
|
||||||
?assertMatch({ok, [#{id := ID2}, #{id := ID1}]}, ?AUTHN:list_authenticators(Global)),
|
?assertMatch({ok, [#{id := ID2}, #{id := ID1}]}, ?AUTHN:list_authenticators(Global)),
|
||||||
|
|
||||||
?assertMatch({ok, _}, update_config([authentication], {delete_authenticator, Global, ID1})),
|
?assertMatch({ok, _}, update_config([?CONF_ROOT], {delete_authenticator, Global, ID1})),
|
||||||
?assertEqual(
|
?assertEqual(
|
||||||
{error, {not_found, {authenticator, ID1}}},
|
{error, {not_found, {authenticator, ID1}}},
|
||||||
?AUTHN:lookup_authenticator(Global, ID1)),
|
?AUTHN:lookup_authenticator(Global, ID1)),
|
||||||
|
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{ok, _},
|
{ok, _},
|
||||||
update_config([authentication], {delete_authenticator, Global, ID2})),
|
update_config([?CONF_ROOT], {delete_authenticator, Global, ID2})),
|
||||||
|
|
||||||
?assertEqual(
|
?assertEqual(
|
||||||
{error, {not_found, {authenticator, ID2}}},
|
{error, {not_found, {authenticator, ID2}}},
|
||||||
?AUTHN:lookup_authenticator(Global, ID2)),
|
?AUTHN:lookup_authenticator(Global, ID2)),
|
||||||
|
|
||||||
ListenerID = 'tcp:default',
|
ListenerID = 'tcp:default',
|
||||||
ConfKeyPath = [listeners, tcp, default, authentication],
|
ConfKeyPath = [listeners, tcp, default, ?CONF_ROOT],
|
||||||
|
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{ok, _},
|
{ok, _},
|
||||||
|
|
|
@ -1,6 +1 @@
|
||||||
# authentication: {
|
authentication: []
|
||||||
# mechanism: password-based
|
|
||||||
# backend: built-in-database
|
|
||||||
# user_id_type: clientid
|
|
||||||
# }
|
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
-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").
|
||||||
|
|
||||||
-define(APP, emqx_authn).
|
-define(APP, emqx_authn).
|
||||||
|
|
||||||
-define(AUTHN, emqx_authentication).
|
-define(AUTHN, emqx_authentication).
|
||||||
|
@ -27,4 +29,9 @@
|
||||||
|
|
||||||
-define(AUTH_SHARD, emqx_authn_shard).
|
-define(AUTH_SHARD, emqx_authn_shard).
|
||||||
|
|
||||||
|
%% has to be the same as the root field name defined in emqx_schema
|
||||||
|
-define(CONF_NS, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME).
|
||||||
|
-define(CONF_NS_ATOM, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM).
|
||||||
|
-define(CONF_NS_BINARY, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY).
|
||||||
|
|
||||||
-endif.
|
-endif.
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
, check_configs/1
|
, check_configs/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
-include("emqx_authn.hrl").
|
||||||
|
|
||||||
providers() ->
|
providers() ->
|
||||||
[ {{'password-based', 'built-in-database'}, emqx_authn_mnesia}
|
[ {{'password-based', 'built-in-database'}, emqx_authn_mnesia}
|
||||||
, {{'password-based', mysql}, emqx_authn_mysql}
|
, {{'password-based', mysql}, emqx_authn_mysql}
|
||||||
|
@ -44,8 +46,8 @@ check_config(Config) ->
|
||||||
|
|
||||||
check_config(Config, Opts) ->
|
check_config(Config, Opts) ->
|
||||||
case do_check_config(Config, Opts) of
|
case do_check_config(Config, Opts) of
|
||||||
#{config := Checked} -> Checked;
|
#{?CONF_NS_ATOM := Checked} -> Checked;
|
||||||
#{<<"config">> := WithDefaults} -> WithDefaults
|
#{?CONF_NS_BINARY := WithDefaults} -> WithDefaults
|
||||||
end.
|
end.
|
||||||
|
|
||||||
do_check_config(#{<<"mechanism">> := Mec} = Config, Opts) ->
|
do_check_config(#{<<"mechanism">> := Mec} = Config, Opts) ->
|
||||||
|
@ -56,10 +58,15 @@ do_check_config(#{<<"mechanism">> := Mec} = Config, Opts) ->
|
||||||
case lists:keyfind(Key, 1, providers()) of
|
case lists:keyfind(Key, 1, providers()) of
|
||||||
false ->
|
false ->
|
||||||
throw({unknown_handler, Key});
|
throw({unknown_handler, Key});
|
||||||
{_, Provider} ->
|
{_, ProviderModule} ->
|
||||||
hocon_schema:check_plain(Provider, #{<<"config">> => Config},
|
hocon_schema:check_plain(ProviderModule, #{?CONF_NS_BINARY => Config},
|
||||||
Opts#{atom_key => true})
|
Opts#{atom_key => true})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
atom(Bin) ->
|
atom(Bin) ->
|
||||||
binary_to_existing_atom(Bin, utf8).
|
try
|
||||||
|
binary_to_existing_atom(Bin, utf8)
|
||||||
|
catch
|
||||||
|
_ : _ ->
|
||||||
|
throw({unknown_auth_provider, Bin})
|
||||||
|
end.
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
-include("emqx_authn.hrl").
|
-include("emqx_authn.hrl").
|
||||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
-include_lib("emqx/include/emqx_authentication.hrl").
|
||||||
|
|
||||||
-import(hoconsc, [mk/2, ref/1]).
|
-import(hoconsc, [mk/2, ref/1]).
|
||||||
-import(emqx_dashboard_swagger, [error_codes/2]).
|
-import(emqx_dashboard_swagger, [error_codes/2]).
|
||||||
|
@ -32,8 +33,10 @@
|
||||||
|
|
||||||
% Swagger
|
% Swagger
|
||||||
|
|
||||||
-define(API_TAGS_GLOBAL, [<<"authentication">>, <<"authentication config(global)">>]).
|
-define(API_TAGS_GLOBAL, [?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY,
|
||||||
-define(API_TAGS_SINGLE, [<<"authentication">>, <<"authentication config(single listener)">>]).
|
<<"authentication config(global)">>]).
|
||||||
|
-define(API_TAGS_SINGLE, [?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY,
|
||||||
|
<<"authentication config(single listener)">>]).
|
||||||
|
|
||||||
-export([ api_spec/0
|
-export([ api_spec/0
|
||||||
, paths/0
|
, paths/0
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
, stop/1
|
, stop/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
-include_lib("emqx/include/emqx_authentication.hrl").
|
||||||
|
|
||||||
-dialyzer({nowarn_function, [start/2]}).
|
-dialyzer({nowarn_function, [start/2]}).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
@ -65,7 +67,7 @@ chain_configs() ->
|
||||||
[global_chain_config() | listener_chain_configs()].
|
[global_chain_config() | listener_chain_configs()].
|
||||||
|
|
||||||
global_chain_config() ->
|
global_chain_config() ->
|
||||||
{?GLOBAL, emqx:get_raw_config([<<"authentication">>], [])}.
|
{?GLOBAL, emqx:get_raw_config([?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY], [])}.
|
||||||
|
|
||||||
listener_chain_configs() ->
|
listener_chain_configs() ->
|
||||||
lists:map(
|
lists:map(
|
||||||
|
@ -77,7 +79,7 @@ listener_chain_configs() ->
|
||||||
auth_config_path(ListenerID) ->
|
auth_config_path(ListenerID) ->
|
||||||
[<<"listeners">>]
|
[<<"listeners">>]
|
||||||
++ binary:split(atom_to_binary(ListenerID), <<":">>)
|
++ binary:split(atom_to_binary(ListenerID), <<":">>)
|
||||||
++ [<<"authentication">>].
|
++ [?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY].
|
||||||
|
|
||||||
provider_types() ->
|
provider_types() ->
|
||||||
lists:map(fun({Type, _Module}) -> Type end, emqx_authn:providers()).
|
lists:map(fun({Type, _Module}) -> Type end, emqx_authn:providers()).
|
||||||
|
|
|
@ -22,10 +22,12 @@
|
||||||
, roots/0
|
, roots/0
|
||||||
, fields/1
|
, fields/1
|
||||||
, authenticator_type/0
|
, authenticator_type/0
|
||||||
|
, root_type/0
|
||||||
|
, mechanism/1
|
||||||
|
, backend/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% only for doc generation
|
roots() -> [].
|
||||||
roots() -> [{authenticator_config, hoconsc:mk(authenticator_type())}].
|
|
||||||
|
|
||||||
fields(_) -> [].
|
fields(_) -> [].
|
||||||
|
|
||||||
|
@ -35,6 +37,7 @@ common_fields() ->
|
||||||
|
|
||||||
enable(type) -> boolean();
|
enable(type) -> boolean();
|
||||||
enable(default) -> true;
|
enable(default) -> true;
|
||||||
|
enable(desc) -> "Set to <code>false</code> to disable this auth provider";
|
||||||
enable(_) -> undefined.
|
enable(_) -> undefined.
|
||||||
|
|
||||||
authenticator_type() ->
|
authenticator_type() ->
|
||||||
|
@ -42,3 +45,18 @@ authenticator_type() ->
|
||||||
|
|
||||||
config_refs(Modules) ->
|
config_refs(Modules) ->
|
||||||
lists:append([Module:refs() || Module <- Modules]).
|
lists:append([Module:refs() || Module <- Modules]).
|
||||||
|
|
||||||
|
%% authn is a core functionality however implemented outside fo emqx app
|
||||||
|
%% in emqx_schema, 'authentication' is a map() type which is to allow
|
||||||
|
%% EMQ X more plugable.
|
||||||
|
root_type() ->
|
||||||
|
T = authenticator_type(),
|
||||||
|
hoconsc:union([T, hoconsc:array(T)]).
|
||||||
|
|
||||||
|
mechanism(Name) ->
|
||||||
|
hoconsc:mk(hoconsc:enum([Name]),
|
||||||
|
#{nullable => false}).
|
||||||
|
|
||||||
|
backend(Name) ->
|
||||||
|
hoconsc:mk(hoconsc:enum([Name]),
|
||||||
|
#{nullable => false}).
|
||||||
|
|
|
@ -83,11 +83,11 @@ mnesia(boot) ->
|
||||||
|
|
||||||
namespace() -> "authn-scram-builtin_db".
|
namespace() -> "authn-scram-builtin_db".
|
||||||
|
|
||||||
roots() -> [config].
|
roots() -> [?CONF_NS].
|
||||||
|
|
||||||
fields(config) ->
|
fields(?CONF_NS) ->
|
||||||
[ {mechanism, {enum, [scram]}}
|
[ {mechanism, emqx_authn_schema:mechanism('scram')}
|
||||||
, {backend, {enum, ['built-in-database']}}
|
, {backend, emqx_authn_schema:backend('built-in-database')}
|
||||||
, {algorithm, fun algorithm/1}
|
, {algorithm, fun algorithm/1}
|
||||||
, {iteration_count, fun iteration_count/1}
|
, {iteration_count, fun iteration_count/1}
|
||||||
] ++ emqx_authn_schema:common_fields().
|
] ++ emqx_authn_schema:common_fields().
|
||||||
|
@ -105,7 +105,7 @@ iteration_count(_) -> undefined.
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
refs() ->
|
refs() ->
|
||||||
[hoconsc:ref(?MODULE, config)].
|
[hoconsc:ref(?MODULE, ?CONF_NS)].
|
||||||
|
|
||||||
create(AuthenticatorID,
|
create(AuthenticatorID,
|
||||||
#{algorithm := Algorithm,
|
#{algorithm := Algorithm,
|
||||||
|
|
|
@ -43,7 +43,8 @@
|
||||||
namespace() -> "authn-http".
|
namespace() -> "authn-http".
|
||||||
|
|
||||||
roots() ->
|
roots() ->
|
||||||
[ {config, hoconsc:mk(hoconsc:union(refs()),
|
[ {?CONF_NS,
|
||||||
|
hoconsc:mk(hoconsc:union(refs()),
|
||||||
#{})}
|
#{})}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
@ -60,8 +61,8 @@ fields(post) ->
|
||||||
] ++ common_fields().
|
] ++ common_fields().
|
||||||
|
|
||||||
common_fields() ->
|
common_fields() ->
|
||||||
[ {mechanism, hoconsc:enum(['password-based'])}
|
[ {mechanism, emqx_authn_schema:mechanism('password-based')}
|
||||||
, {backend, hoconsc:enum(['http'])}
|
, {backend, emqx_authn_schema:backend(http)}
|
||||||
, {url, fun url/1}
|
, {url, fun url/1}
|
||||||
, {body, fun body/1}
|
, {body, fun body/1}
|
||||||
, {request_timeout, fun request_timeout/1}
|
, {request_timeout, fun request_timeout/1}
|
||||||
|
@ -233,9 +234,9 @@ transform_header_name(Headers) ->
|
||||||
end, #{}, Headers).
|
end, #{}, Headers).
|
||||||
|
|
||||||
check_ssl_opts(Conf) ->
|
check_ssl_opts(Conf) ->
|
||||||
case parse_url(hocon_schema:get_value("config.url", Conf)) of
|
case parse_url(get_conf_val("url", Conf)) of
|
||||||
#{scheme := https} ->
|
#{scheme := https} ->
|
||||||
case hocon_schema:get_value("config.ssl.enable", Conf) of
|
case get_conf_val("ssl.enable", Conf) of
|
||||||
true -> ok;
|
true -> ok;
|
||||||
false -> false
|
false -> false
|
||||||
end;
|
end;
|
||||||
|
@ -244,8 +245,8 @@ check_ssl_opts(Conf) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
check_headers(Conf) ->
|
check_headers(Conf) ->
|
||||||
Method = to_bin(hocon_schema:get_value("config.method", Conf)),
|
Method = to_bin(get_conf_val("method", Conf)),
|
||||||
Headers = hocon_schema:get_value("config.headers", Conf),
|
Headers = get_conf_val("headers", Conf),
|
||||||
Method =:= <<"post">> orelse (not maps:is_key(<<"content-type">>, Headers)).
|
Method =:= <<"post">> orelse (not maps:is_key(<<"content-type">>, Headers)).
|
||||||
|
|
||||||
parse_url(URL) ->
|
parse_url(URL) ->
|
||||||
|
@ -340,3 +341,6 @@ to_bin(B) when is_binary(B) ->
|
||||||
B;
|
B;
|
||||||
to_bin(L) when is_list(L) ->
|
to_bin(L) when is_list(L) ->
|
||||||
list_to_binary(L).
|
list_to_binary(L).
|
||||||
|
|
||||||
|
get_conf_val(Name, Conf) ->
|
||||||
|
hocon_schema:get_value(?CONF_NS ++ "." ++ Name, Conf).
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
-module(emqx_authn_jwt).
|
-module(emqx_authn_jwt).
|
||||||
|
|
||||||
|
-include("emqx_authn.hrl").
|
||||||
-include_lib("typerefl/include/types.hrl").
|
-include_lib("typerefl/include/types.hrl").
|
||||||
|
|
||||||
-behaviour(hocon_schema).
|
-behaviour(hocon_schema).
|
||||||
|
@ -40,9 +41,9 @@
|
||||||
namespace() -> "authn-jwt".
|
namespace() -> "authn-jwt".
|
||||||
|
|
||||||
roots() ->
|
roots() ->
|
||||||
[ {config, hoconsc:mk(hoconsc:union(refs()),
|
[ {?CONF_NS,
|
||||||
#{}
|
hoconsc:mk(hoconsc:union(refs()),
|
||||||
)}
|
#{})}
|
||||||
].
|
].
|
||||||
|
|
||||||
fields('hmac-based') ->
|
fields('hmac-based') ->
|
||||||
|
@ -82,7 +83,7 @@ fields(ssl_disable) ->
|
||||||
[ {enable, #{type => false}} ].
|
[ {enable, #{type => false}} ].
|
||||||
|
|
||||||
common_fields() ->
|
common_fields() ->
|
||||||
[ {mechanism, {enum, [jwt]}}
|
[ {mechanism, emqx_authn_schema:mechanism('jwt')}
|
||||||
, {verify_claims, fun verify_claims/1}
|
, {verify_claims, fun verify_claims/1}
|
||||||
] ++ emqx_authn_schema:common_fields().
|
] ++ emqx_authn_schema:common_fields().
|
||||||
|
|
||||||
|
|
|
@ -85,11 +85,11 @@ mnesia(boot) ->
|
||||||
|
|
||||||
namespace() -> "authn-builtin_db".
|
namespace() -> "authn-builtin_db".
|
||||||
|
|
||||||
roots() -> [config].
|
roots() -> [?CONF_NS].
|
||||||
|
|
||||||
fields(config) ->
|
fields(?CONF_NS) ->
|
||||||
[ {mechanism, {enum, ['password-based']}}
|
[ {mechanism, emqx_authn_schema:mechanism('password-based')}
|
||||||
, {backend, {enum, ['built-in-database']}}
|
, {backend, emqx_authn_schema:backend('built-in-database')}
|
||||||
, {user_id_type, fun user_id_type/1}
|
, {user_id_type, fun user_id_type/1}
|
||||||
, {password_hash_algorithm, fun password_hash_algorithm/1}
|
, {password_hash_algorithm, fun password_hash_algorithm/1}
|
||||||
] ++ emqx_authn_schema:common_fields();
|
] ++ emqx_authn_schema:common_fields();
|
||||||
|
@ -104,7 +104,7 @@ fields(other_algorithms) ->
|
||||||
].
|
].
|
||||||
|
|
||||||
user_id_type(type) -> user_id_type();
|
user_id_type(type) -> user_id_type();
|
||||||
user_id_type(default) -> username;
|
user_id_type(default) -> <<"username">>;
|
||||||
user_id_type(_) -> undefined.
|
user_id_type(_) -> undefined.
|
||||||
|
|
||||||
password_hash_algorithm(type) -> hoconsc:union([hoconsc:ref(?MODULE, bcrypt),
|
password_hash_algorithm(type) -> hoconsc:union([hoconsc:ref(?MODULE, bcrypt),
|
||||||
|
@ -121,7 +121,7 @@ salt_rounds(_) -> undefined.
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
refs() ->
|
refs() ->
|
||||||
[hoconsc:ref(?MODULE, config)].
|
[hoconsc:ref(?MODULE, ?CONF_NS)].
|
||||||
|
|
||||||
create(AuthenticatorID,
|
create(AuthenticatorID,
|
||||||
#{user_id_type := Type,
|
#{user_id_type := Type,
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
namespace() -> "authn-mongodb".
|
namespace() -> "authn-mongodb".
|
||||||
|
|
||||||
roots() ->
|
roots() ->
|
||||||
[ {config, hoconsc:mk(hoconsc:union(refs()),
|
[ {?CONF_NS, hoconsc:mk(hoconsc:union(refs()),
|
||||||
#{})}
|
#{})}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
@ -56,8 +56,8 @@ fields('sharded-cluster') ->
|
||||||
common_fields() ++ emqx_connector_mongo:fields(sharded).
|
common_fields() ++ emqx_connector_mongo:fields(sharded).
|
||||||
|
|
||||||
common_fields() ->
|
common_fields() ->
|
||||||
[ {mechanism, {enum, ['password-based']}}
|
[ {mechanism, emqx_authn_schema:mechanism('password-based')}
|
||||||
, {backend, {enum, [mongodb]}}
|
, {backend, emqx_authn_schema:backend(mongodb)}
|
||||||
, {collection, fun collection/1}
|
, {collection, fun collection/1}
|
||||||
, {selector, fun selector/1}
|
, {selector, fun selector/1}
|
||||||
, {password_hash_field, fun password_hash_field/1}
|
, {password_hash_field, fun password_hash_field/1}
|
||||||
|
|
|
@ -41,11 +41,11 @@
|
||||||
|
|
||||||
namespace() -> "authn-mysql".
|
namespace() -> "authn-mysql".
|
||||||
|
|
||||||
roots() -> [config].
|
roots() -> [?CONF_NS].
|
||||||
|
|
||||||
fields(config) ->
|
fields(?CONF_NS) ->
|
||||||
[ {mechanism, {enum, ['password-based']}}
|
[ {mechanism, emqx_authn_schema:mechanism('password-based')}
|
||||||
, {backend, {enum, [mysql]}}
|
, {backend, emqx_authn_schema:backend(mysql)}
|
||||||
, {password_hash_algorithm, fun password_hash_algorithm/1}
|
, {password_hash_algorithm, fun password_hash_algorithm/1}
|
||||||
, {salt_position, fun salt_position/1}
|
, {salt_position, fun salt_position/1}
|
||||||
, {query, fun query/1}
|
, {query, fun query/1}
|
||||||
|
@ -74,7 +74,7 @@ query_timeout(_) -> undefined.
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
refs() ->
|
refs() ->
|
||||||
[hoconsc:ref(?MODULE, config)].
|
[hoconsc:ref(?MODULE, ?CONF_NS)].
|
||||||
|
|
||||||
create(_AuthenticatorID, Config) ->
|
create(_AuthenticatorID, Config) ->
|
||||||
create(Config).
|
create(Config).
|
||||||
|
|
|
@ -47,11 +47,11 @@
|
||||||
|
|
||||||
namespace() -> "authn-postgresql".
|
namespace() -> "authn-postgresql".
|
||||||
|
|
||||||
roots() -> [config].
|
roots() -> [?CONF_NS].
|
||||||
|
|
||||||
fields(config) ->
|
fields(?CONF_NS) ->
|
||||||
[ {mechanism, {enum, ['password-based']}}
|
[ {mechanism, emqx_authn_schema:mechanism('password-based')}
|
||||||
, {backend, {enum, [postgresql]}}
|
, {backend, emqx_authn_schema:backend(postgresql)}
|
||||||
, {password_hash_algorithm, fun password_hash_algorithm/1}
|
, {password_hash_algorithm, fun password_hash_algorithm/1}
|
||||||
, {salt_position, fun salt_position/1}
|
, {salt_position, fun salt_position/1}
|
||||||
, {query, fun query/1}
|
, {query, fun query/1}
|
||||||
|
@ -75,7 +75,7 @@ query(_) -> undefined.
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
refs() ->
|
refs() ->
|
||||||
[hoconsc:ref(?MODULE, config)].
|
[hoconsc:ref(?MODULE, ?CONF_NS)].
|
||||||
|
|
||||||
create(_AuthenticatorID, Config) ->
|
create(_AuthenticatorID, Config) ->
|
||||||
create(Config).
|
create(Config).
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
namespace() -> "authn-redis".
|
namespace() -> "authn-redis".
|
||||||
|
|
||||||
roots() ->
|
roots() ->
|
||||||
[ {config, hoconsc:mk(hoconsc:union(refs()),
|
[ {?CONF_NS, hoconsc:mk(hoconsc:union(refs()),
|
||||||
#{})}
|
#{})}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
@ -56,11 +56,11 @@ fields(sentinel) ->
|
||||||
common_fields() ++ emqx_connector_redis:fields(sentinel).
|
common_fields() ++ emqx_connector_redis:fields(sentinel).
|
||||||
|
|
||||||
common_fields() ->
|
common_fields() ->
|
||||||
[{mechanism, {enum, ['password-based']}},
|
[ {mechanism, emqx_authn_schema:mechanism('password-based')}
|
||||||
{backend, {enum, [redis]}},
|
, {backend, emqx_authn_schema:backend(redis)}
|
||||||
{cmd, fun cmd/1},
|
, {cmd, fun cmd/1}
|
||||||
{password_hash_algorithm, fun password_hash_algorithm/1},
|
, {password_hash_algorithm, fun password_hash_algorithm/1}
|
||||||
{salt_position, fun salt_position/1}
|
, {salt_position, fun salt_position/1}
|
||||||
] ++ emqx_authn_schema:common_fields().
|
] ++ emqx_authn_schema:common_fields().
|
||||||
|
|
||||||
cmd(type) -> string();
|
cmd(type) -> string();
|
||||||
|
|
|
@ -47,6 +47,8 @@ end_per_testcase(_Case, Config) ->
|
||||||
%% Tests
|
%% Tests
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-define(CONF(Conf), #{?CONF_NS_BINARY => Conf}).
|
||||||
|
|
||||||
t_check_schema(_Config) ->
|
t_check_schema(_Config) ->
|
||||||
ConfigOk = #{
|
ConfigOk = #{
|
||||||
<<"mechanism">> => <<"password-based">>,
|
<<"mechanism">> => <<"password-based">>,
|
||||||
|
@ -58,7 +60,7 @@ t_check_schema(_Config) ->
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
hocon_schema:check_plain(emqx_authn_mnesia, #{<<"config">> => ConfigOk}),
|
hocon_schema:check_plain(emqx_authn_mnesia, ?CONF(ConfigOk)),
|
||||||
|
|
||||||
ConfigNotOk = #{
|
ConfigNotOk = #{
|
||||||
<<"mechanism">> => <<"password-based">>,
|
<<"mechanism">> => <<"password-based">>,
|
||||||
|
@ -72,7 +74,7 @@ t_check_schema(_Config) ->
|
||||||
?assertException(
|
?assertException(
|
||||||
throw,
|
throw,
|
||||||
{emqx_authn_mnesia, _},
|
{emqx_authn_mnesia, _},
|
||||||
hocon_schema:check_plain(emqx_authn_mnesia, #{<<"config">> => ConfigNotOk})).
|
hocon_schema:check_plain(emqx_authn_mnesia, ?CONF(ConfigNotOk))).
|
||||||
|
|
||||||
t_create(_) ->
|
t_create(_) ->
|
||||||
Config0 = config(),
|
Config0 = config(),
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
-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().
|
||||||
|
@ -62,6 +63,11 @@
|
||||||
namespace() -> undefined.
|
namespace() -> undefined.
|
||||||
|
|
||||||
roots() ->
|
roots() ->
|
||||||
|
PtKey = ?EMQX_AUTHENTICATION_SCHEMA_MODULE_PT_KEY,
|
||||||
|
case persistent_term:get(PtKey, undefined) of
|
||||||
|
undefined -> persistent_term:put(PtKey, emqx_authn_schema);
|
||||||
|
_ -> ok
|
||||||
|
end,
|
||||||
%% authorization configs are merged in THIS schema's "authorization" fields
|
%% authorization configs are merged in THIS schema's "authorization" fields
|
||||||
lists:keydelete("authorization", 1, emqx_schema:roots(high)) ++
|
lists:keydelete("authorization", 1, emqx_schema:roots(high)) ++
|
||||||
[ {"node",
|
[ {"node",
|
||||||
|
|
|
@ -18,9 +18,6 @@
|
||||||
|
|
||||||
-behaviour(emqx_gateway_channel).
|
-behaviour(emqx_gateway_channel).
|
||||||
|
|
||||||
-include_lib("emqx/include/logger.hrl").
|
|
||||||
-include_lib("emqx_gateway/src/coap/include/emqx_coap.hrl").
|
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([ info/1
|
-export([ info/1
|
||||||
, info/2
|
, info/2
|
||||||
|
@ -44,6 +41,12 @@
|
||||||
|
|
||||||
-export_type([channel/0]).
|
-export_type([channel/0]).
|
||||||
|
|
||||||
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
-include_lib("emqx_gateway/src/coap/include/emqx_coap.hrl").
|
||||||
|
-include_lib("emqx/include/emqx_authentication.hrl").
|
||||||
|
|
||||||
|
-define(AUTHN, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM).
|
||||||
|
|
||||||
-record(channel, {
|
-record(channel, {
|
||||||
%% Context
|
%% Context
|
||||||
ctx :: emqx_gateway_ctx:context(),
|
ctx :: emqx_gateway_ctx:context(),
|
||||||
|
@ -283,7 +286,7 @@ try_takeover(idle, DesireId, Msg, Channel) ->
|
||||||
%% udp connection baseon the clientid
|
%% udp connection baseon the clientid
|
||||||
call_session(handle_request, Msg, Channel);
|
call_session(handle_request, Msg, Channel);
|
||||||
_ ->
|
_ ->
|
||||||
case emqx_conf:get([gateway, coap, authentication], undefined) of
|
case emqx_conf:get([gateway, coap, ?AUTHN], undefined) of
|
||||||
undefined ->
|
undefined ->
|
||||||
call_session(handle_request, Msg, Channel);
|
call_session(handle_request, Msg, Channel);
|
||||||
_ ->
|
_ ->
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
-module(emqx_gateway_api).
|
-module(emqx_gateway_api).
|
||||||
|
|
||||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||||
|
-include_lib("emqx/include/emqx_authentication.hrl").
|
||||||
|
|
||||||
-behaviour(minirest_api).
|
-behaviour(minirest_api).
|
||||||
|
|
||||||
|
@ -243,7 +244,7 @@ schema_gateway_overview_list() ->
|
||||||
%%
|
%%
|
||||||
%% NOTE: It is a temporary measure to generate swagger-schema
|
%% NOTE: It is a temporary measure to generate swagger-schema
|
||||||
-define(COAP_GATEWAY_CONFS,
|
-define(COAP_GATEWAY_CONFS,
|
||||||
#{<<"authentication">> =>
|
#{?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY =>
|
||||||
#{<<"mechanism">> => <<"password-based">>,
|
#{<<"mechanism">> => <<"password-based">>,
|
||||||
<<"name">> => <<"authenticator1">>,
|
<<"name">> => <<"authenticator1">>,
|
||||||
<<"server_type">> => <<"built-in-database">>,
|
<<"server_type">> => <<"built-in-database">>,
|
||||||
|
@ -331,7 +332,7 @@ schema_gateway_overview_list() ->
|
||||||
).
|
).
|
||||||
|
|
||||||
-define(STOMP_GATEWAY_CONFS,
|
-define(STOMP_GATEWAY_CONFS,
|
||||||
#{<<"authentication">> =>
|
#{?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY =>
|
||||||
#{<<"mechanism">> => <<"password-based">>,
|
#{<<"mechanism">> => <<"password-based">>,
|
||||||
<<"name">> => <<"authenticator1">>,
|
<<"name">> => <<"authenticator1">>,
|
||||||
<<"server_type">> => <<"built-in-database">>,
|
<<"server_type">> => <<"built-in-database">>,
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
%% @doc The gateway configuration management module
|
%% @doc The gateway configuration management module
|
||||||
-module(emqx_gateway_conf).
|
-module(emqx_gateway_conf).
|
||||||
|
|
||||||
-include_lib("emqx/include/logger.hrl").
|
|
||||||
|
|
||||||
%% Load/Unload
|
%% Load/Unload
|
||||||
-export([ load/0
|
-export([ load/0
|
||||||
, unload/0
|
, unload/0
|
||||||
|
@ -56,6 +54,10 @@
|
||||||
, post_config_update/5
|
, post_config_update/5
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
-include_lib("emqx/include/emqx_authentication.hrl").
|
||||||
|
-define(AUTHN_BIN, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY).
|
||||||
|
|
||||||
-type atom_or_bin() :: atom() | binary().
|
-type atom_or_bin() :: atom() | binary().
|
||||||
-type ok_or_err() :: ok_or_err().
|
-type ok_or_err() :: ok_or_err().
|
||||||
-type listener_ref() :: {ListenerType :: atom_or_bin(),
|
-type listener_ref() :: {ListenerType :: atom_or_bin(),
|
||||||
|
@ -106,8 +108,9 @@ maps_key_take([K | Ks], M, Acc) ->
|
||||||
|
|
||||||
-spec update_gateway(atom_or_bin(), map()) -> ok_or_err().
|
-spec update_gateway(atom_or_bin(), map()) -> ok_or_err().
|
||||||
update_gateway(GwName, Conf0) ->
|
update_gateway(GwName, Conf0) ->
|
||||||
Conf = maps:without([listeners, authentication,
|
Exclude0 = [listeners, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM],
|
||||||
<<"listeners">>, <<"authentication">>], Conf0),
|
Exclude1 = [atom_to_binary(K, utf8) || K <- Exclude0],
|
||||||
|
Conf = maps:without(Exclude0 ++ Exclude1, Conf0),
|
||||||
update({?FUNCTION_NAME, bin(GwName), Conf}).
|
update({?FUNCTION_NAME, bin(GwName), Conf}).
|
||||||
|
|
||||||
%% FIXME: delete cert files ??
|
%% FIXME: delete cert files ??
|
||||||
|
@ -263,8 +266,7 @@ pre_config_update(_, {update_gateway, GwName, Conf}, RawConf) ->
|
||||||
undefined ->
|
undefined ->
|
||||||
{error, not_found};
|
{error, not_found};
|
||||||
_ ->
|
_ ->
|
||||||
NConf = maps:without([<<"listeners">>,
|
NConf = maps:without([<<"listeners">>, ?AUTHN_BIN], Conf),
|
||||||
<<"authentication">>], Conf),
|
|
||||||
{ok, emqx_map_lib:deep_merge(RawConf, #{GwName => NConf})}
|
{ok, emqx_map_lib:deep_merge(RawConf, #{GwName => NConf})}
|
||||||
end;
|
end;
|
||||||
pre_config_update(_, {unload_gateway, GwName}, RawConf) ->
|
pre_config_update(_, {unload_gateway, GwName}, RawConf) ->
|
||||||
|
@ -311,11 +313,11 @@ pre_config_update(_, {remove_listener, GwName, {LType, LName}}, RawConf) ->
|
||||||
|
|
||||||
pre_config_update(_, {add_authn, GwName, Conf}, RawConf) ->
|
pre_config_update(_, {add_authn, GwName, Conf}, RawConf) ->
|
||||||
case emqx_map_lib:deep_get(
|
case emqx_map_lib:deep_get(
|
||||||
[GwName, <<"authentication">>], RawConf, undefined) of
|
[GwName, ?AUTHN_BIN], RawConf, undefined) of
|
||||||
undefined ->
|
undefined ->
|
||||||
{ok, emqx_map_lib:deep_merge(
|
{ok, emqx_map_lib:deep_merge(
|
||||||
RawConf,
|
RawConf,
|
||||||
#{GwName => #{<<"authentication">> => Conf}})};
|
#{GwName => #{?AUTHN_BIN => Conf}})};
|
||||||
_ ->
|
_ ->
|
||||||
{error, already_exist}
|
{error, already_exist}
|
||||||
end;
|
end;
|
||||||
|
@ -326,9 +328,9 @@ pre_config_update(_, {add_authn, GwName, {LType, LName}, Conf}, RawConf) ->
|
||||||
undefined ->
|
undefined ->
|
||||||
{error, not_found};
|
{error, not_found};
|
||||||
Listener ->
|
Listener ->
|
||||||
case maps:get(<<"authentication">>, Listener, undefined) of
|
case maps:get(?AUTHN_BIN, Listener, undefined) of
|
||||||
undefined ->
|
undefined ->
|
||||||
NListener = maps:put(<<"authentication">>, Conf, Listener),
|
NListener = maps:put(?AUTHN_BIN, Conf, Listener),
|
||||||
NGateway = #{GwName =>
|
NGateway = #{GwName =>
|
||||||
#{<<"listeners">> =>
|
#{<<"listeners">> =>
|
||||||
#{LType => #{LName => NListener}}}},
|
#{LType => #{LName => NListener}}}},
|
||||||
|
@ -339,13 +341,13 @@ pre_config_update(_, {add_authn, GwName, {LType, LName}, Conf}, RawConf) ->
|
||||||
end;
|
end;
|
||||||
pre_config_update(_, {update_authn, GwName, Conf}, RawConf) ->
|
pre_config_update(_, {update_authn, GwName, Conf}, RawConf) ->
|
||||||
case emqx_map_lib:deep_get(
|
case emqx_map_lib:deep_get(
|
||||||
[GwName, <<"authentication">>], RawConf, undefined) of
|
[GwName, ?AUTHN_BIN], RawConf, undefined) of
|
||||||
undefined ->
|
undefined ->
|
||||||
{error, not_found};
|
{error, not_found};
|
||||||
_ ->
|
_ ->
|
||||||
{ok, emqx_map_lib:deep_merge(
|
{ok, emqx_map_lib:deep_merge(
|
||||||
RawConf,
|
RawConf,
|
||||||
#{GwName => #{<<"authentication">> => Conf}})}
|
#{GwName => #{?AUTHN_BIN => Conf}})}
|
||||||
end;
|
end;
|
||||||
pre_config_update(_, {update_authn, GwName, {LType, LName}, Conf}, RawConf) ->
|
pre_config_update(_, {update_authn, GwName, {LType, LName}, Conf}, RawConf) ->
|
||||||
case emqx_map_lib:deep_get(
|
case emqx_map_lib:deep_get(
|
||||||
|
@ -354,12 +356,12 @@ pre_config_update(_, {update_authn, GwName, {LType, LName}, Conf}, RawConf) ->
|
||||||
undefined ->
|
undefined ->
|
||||||
{error, not_found};
|
{error, not_found};
|
||||||
Listener ->
|
Listener ->
|
||||||
case maps:get(<<"authentication">>, Listener, undefined) of
|
case maps:get(?AUTHN_BIN, Listener, undefined) of
|
||||||
undefined ->
|
undefined ->
|
||||||
{error, not_found};
|
{error, not_found};
|
||||||
Auth ->
|
Auth ->
|
||||||
NListener = maps:put(
|
NListener = maps:put(
|
||||||
<<"authentication">>,
|
?AUTHN_BIN,
|
||||||
emqx_map_lib:deep_merge(Auth, Conf),
|
emqx_map_lib:deep_merge(Auth, Conf),
|
||||||
Listener
|
Listener
|
||||||
),
|
),
|
||||||
|
@ -371,9 +373,9 @@ pre_config_update(_, {update_authn, GwName, {LType, LName}, Conf}, RawConf) ->
|
||||||
end;
|
end;
|
||||||
pre_config_update(_, {remove_authn, GwName}, RawConf) ->
|
pre_config_update(_, {remove_authn, GwName}, RawConf) ->
|
||||||
{ok, emqx_map_lib:deep_remove(
|
{ok, emqx_map_lib:deep_remove(
|
||||||
[GwName, <<"authentication">>], RawConf)};
|
[GwName, ?AUTHN_BIN], RawConf)};
|
||||||
pre_config_update(_, {remove_authn, GwName, {LType, LName}}, RawConf) ->
|
pre_config_update(_, {remove_authn, GwName, {LType, LName}}, RawConf) ->
|
||||||
Path = [GwName, <<"listeners">>, LType, LName, <<"authentication">>],
|
Path = [GwName, <<"listeners">>, LType, LName, ?AUTHN_BIN],
|
||||||
{ok, emqx_map_lib:deep_remove(Path, RawConf)};
|
{ok, emqx_map_lib:deep_remove(Path, RawConf)};
|
||||||
|
|
||||||
pre_config_update(_, UnknownReq, _RawConf) ->
|
pre_config_update(_, UnknownReq, _RawConf) ->
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
|
|
||||||
-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").
|
||||||
|
|
||||||
|
-define(AUTHN, ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM).
|
||||||
|
|
||||||
%% Mgmt APIs - gateway
|
%% Mgmt APIs - gateway
|
||||||
-export([ gateways/1
|
-export([ gateways/1
|
||||||
|
@ -166,7 +169,7 @@ remove_listener(ListenerId) ->
|
||||||
-spec authn(gateway_name()) -> map().
|
-spec authn(gateway_name()) -> map().
|
||||||
authn(GwName) ->
|
authn(GwName) ->
|
||||||
%% XXX: Need append chain-nanme, authenticator-id?
|
%% XXX: Need append chain-nanme, authenticator-id?
|
||||||
Path = [gateway, GwName, authentication],
|
Path = [gateway, GwName, ?AUTHN],
|
||||||
ChainName = emqx_gateway_utils:global_chain(GwName),
|
ChainName = emqx_gateway_utils:global_chain(GwName),
|
||||||
wrap_chain_name(
|
wrap_chain_name(
|
||||||
ChainName,
|
ChainName,
|
||||||
|
@ -176,7 +179,7 @@ authn(GwName) ->
|
||||||
-spec authn(gateway_name(), binary()) -> map().
|
-spec authn(gateway_name(), binary()) -> map().
|
||||||
authn(GwName, ListenerId) ->
|
authn(GwName, ListenerId) ->
|
||||||
{_, Type, Name} = emqx_gateway_utils:parse_listener_id(ListenerId),
|
{_, Type, Name} = emqx_gateway_utils:parse_listener_id(ListenerId),
|
||||||
Path = [gateway, GwName, listeners, Type, Name, authentication],
|
Path = [gateway, GwName, listeners, Type, Name, ?AUTHN],
|
||||||
ChainName = emqx_gateway_utils:listener_chain(GwName, Type, Name),
|
ChainName = emqx_gateway_utils:listener_chain(GwName, Type, Name),
|
||||||
wrap_chain_name(
|
wrap_chain_name(
|
||||||
ChainName,
|
ChainName,
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
-dialyzer(no_unused).
|
-dialyzer(no_unused).
|
||||||
-dialyzer(no_fail_call).
|
-dialyzer(no_fail_call).
|
||||||
|
|
||||||
|
-include_lib("emqx/include/emqx_authentication.hrl").
|
||||||
-include_lib("typerefl/include/types.hrl").
|
-include_lib("typerefl/include/types.hrl").
|
||||||
|
|
||||||
-type ip_port() :: tuple().
|
-type ip_port() :: tuple().
|
||||||
|
@ -144,7 +145,7 @@ The client just sends its PUBLISH messages to a GW"
|
||||||
, desc =>
|
, desc =>
|
||||||
"The Pre-defined topic ids and topic names.<br>
|
"The Pre-defined topic ids and topic names.<br>
|
||||||
A 'pre-defined' topic id is a topic id whose mapping to a topic name
|
A 'pre-defined' topic id is a topic id whose mapping to a topic name
|
||||||
is known in advance by both the client’s application and the gateway"
|
is known in advance by both the client's application and the gateway"
|
||||||
})}
|
})}
|
||||||
, {listeners, sc(ref(udp_listeners))}
|
, {listeners, sc(ref(udp_listeners))}
|
||||||
] ++ gateway_common_options();
|
] ++ gateway_common_options();
|
||||||
|
@ -407,24 +408,8 @@ fields(dtls_opts) ->
|
||||||
, ciphers => dtls_all_available
|
, ciphers => dtls_all_available
|
||||||
}, false).
|
}, false).
|
||||||
|
|
||||||
authentication() ->
|
authentication_schema() ->
|
||||||
sc(hoconsc:union(
|
sc(emqx_authn_schema:authenticator_type(),
|
||||||
[ hoconsc:ref(emqx_authn_mnesia, config)
|
|
||||||
, hoconsc:ref(emqx_authn_mysql, config)
|
|
||||||
, hoconsc:ref(emqx_authn_pgsql, config)
|
|
||||||
, hoconsc:ref(emqx_authn_mongodb, standalone)
|
|
||||||
, hoconsc:ref(emqx_authn_mongodb, 'replica-set')
|
|
||||||
, hoconsc:ref(emqx_authn_mongodb, 'sharded-cluster')
|
|
||||||
, hoconsc:ref(emqx_authn_redis, standalone)
|
|
||||||
, hoconsc:ref(emqx_authn_redis, cluster)
|
|
||||||
, hoconsc:ref(emqx_authn_redis, sentinel)
|
|
||||||
, hoconsc:ref(emqx_authn_http, get)
|
|
||||||
, hoconsc:ref(emqx_authn_http, post)
|
|
||||||
, hoconsc:ref(emqx_authn_jwt, 'hmac-based')
|
|
||||||
, hoconsc:ref(emqx_authn_jwt, 'public-key')
|
|
||||||
, hoconsc:ref(emqx_authn_jwt, 'jwks')
|
|
||||||
, hoconsc:ref(emqx_enhanced_authn_scram_mnesia, config)
|
|
||||||
]),
|
|
||||||
#{ nullable => {true, recursively}
|
#{ nullable => {true, recursively}
|
||||||
, desc =>
|
, desc =>
|
||||||
"""Default authentication configs for all of the gateway listeners.<br>
|
"""Default authentication configs for all of the gateway listeners.<br>
|
||||||
|
@ -464,7 +449,7 @@ it has two purposes:
|
||||||
sc(ref(clientinfo_override),
|
sc(ref(clientinfo_override),
|
||||||
#{ desc => ""
|
#{ desc => ""
|
||||||
})}
|
})}
|
||||||
, {authentication, authentication()}
|
, {?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME, authentication_schema()}
|
||||||
].
|
].
|
||||||
|
|
||||||
common_listener_opts() ->
|
common_listener_opts() ->
|
||||||
|
@ -483,7 +468,7 @@ common_listener_opts() ->
|
||||||
sc(integer(),
|
sc(integer(),
|
||||||
#{ default => 1000
|
#{ default => 1000
|
||||||
})}
|
})}
|
||||||
, {authentication, authentication()}
|
, {?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME, authentication_schema()}
|
||||||
, {mountpoint,
|
, {mountpoint,
|
||||||
sc(binary(),
|
sc(binary(),
|
||||||
#{ default => undefined
|
#{ default => undefined
|
||||||
|
|
Loading…
Reference in New Issue