feat: support check_config callback for authenticator provider
This commit is contained in:
parent
494bac419d
commit
6d9b3ed341
|
@ -116,7 +116,9 @@
|
||||||
-type user_info() :: #{user_id := binary(),
|
-type user_info() :: #{user_id := binary(),
|
||||||
atom() => term()}.
|
atom() => term()}.
|
||||||
|
|
||||||
-callback refs() -> [{ref, Module, Name}] when Module::module(), Name::atom().
|
%% @doc check_config takes raw config from config file,
|
||||||
|
%% parse and validate it, and reutrn parsed result.
|
||||||
|
-callback check_config(config()) -> config().
|
||||||
|
|
||||||
-callback create(Config)
|
-callback create(Config)
|
||||||
-> {ok, State}
|
-> {ok, State}
|
||||||
|
@ -176,6 +178,7 @@
|
||||||
, update_user/3
|
, update_user/3
|
||||||
, lookup_user/3
|
, lookup_user/3
|
||||||
, list_users/1
|
, list_users/1
|
||||||
|
, check_config/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
|
@ -27,6 +27,11 @@
|
||||||
, authn_type/1
|
, authn_type/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
%% TODO: certs handling should be moved out of emqx app
|
||||||
|
-ifdef(TEST).
|
||||||
|
-export([convert_certs/2, convert_certs/3, diff_cert/2, clear_certs/2]).
|
||||||
|
-endif.
|
||||||
|
|
||||||
-export_type([config/0]).
|
-export_type([config/0]).
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
@ -151,17 +156,33 @@ do_check_conifg(Config, Providers) ->
|
||||||
providers => Providers}),
|
providers => Providers}),
|
||||||
throw(unknown_authn_type);
|
throw(unknown_authn_type);
|
||||||
Module ->
|
Module ->
|
||||||
%% TODO: check if Module:check_config/1 is exported
|
do_check_conifg(Type, Config, Module)
|
||||||
%% so we do not force all providers to implement hocon schema
|
end.
|
||||||
try hocon_schema:check_plain(Module, #{<<"config">> => Config},
|
|
||||||
#{atom_key => true}) of
|
do_check_conifg(Type, Config, Module) ->
|
||||||
#{config := Result} ->
|
F = case erlang:function_exported(Module, check_config, 1) of
|
||||||
Result
|
true ->
|
||||||
catch
|
fun Module:check_config/1;
|
||||||
C : E : S ->
|
false ->
|
||||||
?SLOG(warning, #{msg => "failed_to_check_config", config => Config}),
|
fun(C) ->
|
||||||
erlang:raise(C, E, S)
|
#{config := R} =
|
||||||
end
|
hocon_schema:check_plain(Module, #{<<"config">> => C},
|
||||||
|
#{atom_key => true}),
|
||||||
|
R
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
try
|
||||||
|
F(Config)
|
||||||
|
catch
|
||||||
|
C : E : S ->
|
||||||
|
?SLOG(warning, #{msg => "failed_to_check_config",
|
||||||
|
config => Config,
|
||||||
|
type => Type,
|
||||||
|
exception => C,
|
||||||
|
reason => E,
|
||||||
|
stacktrace => S
|
||||||
|
}),
|
||||||
|
throw(bad_authenticator_config)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
return_map([L]) -> L;
|
return_map([L]) -> L;
|
||||||
|
|
|
@ -26,13 +26,13 @@
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
-include_lib("typerefl/include/types.hrl").
|
-include_lib("typerefl/include/types.hrl").
|
||||||
|
|
||||||
-export([ fields/1 ]).
|
-export([ roots/0, fields/1 ]).
|
||||||
|
|
||||||
-export([ refs/0
|
-export([ create/1
|
||||||
, create/1
|
|
||||||
, update/2
|
, update/2
|
||||||
, authenticate/2
|
, authenticate/2
|
||||||
, destroy/1
|
, destroy/1
|
||||||
|
, check_config/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-define(AUTHN, emqx_authentication).
|
-define(AUTHN, emqx_authentication).
|
||||||
|
@ -42,6 +42,8 @@
|
||||||
%% Hocon Schema
|
%% Hocon Schema
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
roots() -> [{config, #{type => hoconsc:union([hoconsc:ref(type1), hoconsc:ref(type2)])}}].
|
||||||
|
|
||||||
fields(type1) ->
|
fields(type1) ->
|
||||||
[ {mechanism, {enum, ['password-based']}}
|
[ {mechanism, {enum, ['password-based']}}
|
||||||
, {backend, {enum, ['built-in-database']}}
|
, {backend, {enum, ['built-in-database']}}
|
||||||
|
@ -62,10 +64,11 @@ enable(_) -> undefined.
|
||||||
%% Callbacks
|
%% Callbacks
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
refs() ->
|
check_config(C) ->
|
||||||
[ hoconsc:ref(?MODULE, type1)
|
#{config := R} =
|
||||||
, hoconsc:ref(?MODULE, type2)
|
hocon_schema:check_plain(?MODULE, #{<<"config">> => C},
|
||||||
].
|
#{atom_key => true}),
|
||||||
|
R.
|
||||||
|
|
||||||
create(_Config) ->
|
create(_Config) ->
|
||||||
{ok, #{mark => 1}}.
|
{ok, #{mark => 1}}.
|
||||||
|
@ -268,14 +271,14 @@ t_convert_certs(Config) when is_list(Config) ->
|
||||||
, {<<"cacertfile">>, "cacert.pem"}
|
, {<<"cacertfile">>, "cacert.pem"}
|
||||||
]),
|
]),
|
||||||
|
|
||||||
CertsDir = ?AUTHN:certs_dir([Global, <<"password-based:built-in-database">>]),
|
CertsDir = certs_dir(Config, [Global, <<"password-based:built-in-database">>]),
|
||||||
#{<<"ssl">> := NCerts} = ?AUTHN:convert_certs(CertsDir, #{<<"ssl">> => Certs}),
|
#{<<"ssl">> := NCerts} = convert_certs(CertsDir, #{<<"ssl">> => Certs}),
|
||||||
?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts), maps:get(<<"keyfile">>, Certs))),
|
?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts), maps:get(<<"keyfile">>, Certs))),
|
||||||
|
|
||||||
Certs2 = certs([ {<<"keyfile">>, "key.pem"}
|
Certs2 = certs([ {<<"keyfile">>, "key.pem"}
|
||||||
, {<<"certfile">>, "cert.pem"}
|
, {<<"certfile">>, "cert.pem"}
|
||||||
]),
|
]),
|
||||||
#{<<"ssl">> := NCerts2} = ?AUTHN:convert_certs(CertsDir, #{<<"ssl">> => Certs2}, #{<<"ssl">> => NCerts}),
|
#{<<"ssl">> := NCerts2} = convert_certs(CertsDir, #{<<"ssl">> => Certs2}, #{<<"ssl">> => NCerts}),
|
||||||
?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts2), maps:get(<<"keyfile">>, Certs2))),
|
?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts2), maps:get(<<"keyfile">>, Certs2))),
|
||||||
?assertEqual(maps:get(<<"keyfile">>, NCerts), maps:get(<<"keyfile">>, NCerts2)),
|
?assertEqual(maps:get(<<"keyfile">>, NCerts), maps:get(<<"keyfile">>, NCerts2)),
|
||||||
?assertEqual(maps:get(<<"certfile">>, NCerts), maps:get(<<"certfile">>, NCerts2)),
|
?assertEqual(maps:get(<<"certfile">>, NCerts), maps:get(<<"certfile">>, NCerts2)),
|
||||||
|
@ -284,13 +287,13 @@ t_convert_certs(Config) when is_list(Config) ->
|
||||||
, {<<"certfile">>, "client-cert.pem"}
|
, {<<"certfile">>, "client-cert.pem"}
|
||||||
, {<<"cacertfile">>, "cacert.pem"}
|
, {<<"cacertfile">>, "cacert.pem"}
|
||||||
]),
|
]),
|
||||||
#{<<"ssl">> := NCerts3} = ?AUTHN:convert_certs(CertsDir, #{<<"ssl">> => Certs3}, #{<<"ssl">> => NCerts2}),
|
#{<<"ssl">> := NCerts3} = convert_certs(CertsDir, #{<<"ssl">> => Certs3}, #{<<"ssl">> => NCerts2}),
|
||||||
?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts3), maps:get(<<"keyfile">>, Certs3))),
|
?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts3), maps:get(<<"keyfile">>, Certs3))),
|
||||||
?assertNotEqual(maps:get(<<"keyfile">>, NCerts2), maps:get(<<"keyfile">>, NCerts3)),
|
?assertNotEqual(maps:get(<<"keyfile">>, NCerts2), maps:get(<<"keyfile">>, NCerts3)),
|
||||||
?assertNotEqual(maps:get(<<"certfile">>, NCerts2), maps:get(<<"certfile">>, NCerts3)),
|
?assertNotEqual(maps:get(<<"certfile">>, NCerts2), maps:get(<<"certfile">>, NCerts3)),
|
||||||
|
|
||||||
?assertEqual(true, filelib:is_regular(maps:get(<<"keyfile">>, NCerts3))),
|
?assertEqual(true, filelib:is_regular(maps:get(<<"keyfile">>, NCerts3))),
|
||||||
?AUTHN:clear_certs(CertsDir, #{<<"ssl">> => NCerts3}),
|
clear_certs(CertsDir, #{<<"ssl">> => NCerts3}),
|
||||||
?assertEqual(false, filelib:is_regular(maps:get(<<"keyfile">>, NCerts3))).
|
?assertEqual(false, filelib:is_regular(maps:get(<<"keyfile">>, NCerts3))).
|
||||||
|
|
||||||
update_config(Path, ConfigRequest) ->
|
update_config(Path, ConfigRequest) ->
|
||||||
|
@ -305,7 +308,22 @@ certs(Certs) ->
|
||||||
|
|
||||||
diff_cert(CertFile, CertPem2) ->
|
diff_cert(CertFile, CertPem2) ->
|
||||||
{ok, CertPem1} = file:read_file(CertFile),
|
{ok, CertPem1} = file:read_file(CertFile),
|
||||||
?AUTHN:diff_cert(CertPem1, CertPem2).
|
emqx_authentication_config:diff_cert(CertPem1, CertPem2).
|
||||||
|
|
||||||
register_provider(Type, Module) ->
|
register_provider(Type, Module) ->
|
||||||
ok = ?AUTHN:register_providers([{Type, Module}]).
|
ok = ?AUTHN:register_providers([{Type, Module}]).
|
||||||
|
|
||||||
|
certs_dir(CtConfig, Path) ->
|
||||||
|
DataDir = proplists:get_value(data_dir, CtConfig),
|
||||||
|
Dir = filename:join([DataDir | Path]),
|
||||||
|
filelib:ensure_dir(Dir),
|
||||||
|
Dir.
|
||||||
|
|
||||||
|
convert_certs(CertsDir, SslConfig) ->
|
||||||
|
emqx_authentication_config:convert_certs(CertsDir, SslConfig).
|
||||||
|
|
||||||
|
convert_certs(CertsDir, New, Old) ->
|
||||||
|
emqx_authentication_config:convert_certs(CertsDir, New, Old).
|
||||||
|
|
||||||
|
clear_certs(CertsDir, SslConfig) ->
|
||||||
|
emqx_authentication_config:clear_certs(CertsDir, SslConfig).
|
||||||
|
|
Loading…
Reference in New Issue