feat: support check_config callback for authenticator provider

This commit is contained in:
Zaiming Shi 2021-10-20 13:04:55 +02:00 committed by x1001100011
parent 494bac419d
commit 6d9b3ed341
3 changed files with 67 additions and 25 deletions

View File

@ -116,7 +116,9 @@
-type user_info() :: #{user_id := binary(),
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)
-> {ok, State}
@ -176,6 +178,7 @@
, update_user/3
, lookup_user/3
, list_users/1
, check_config/1
]).
%%------------------------------------------------------------------------------

View File

@ -27,6 +27,11 @@
, 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]).
-include("logger.hrl").
@ -151,17 +156,33 @@ do_check_conifg(Config, Providers) ->
providers => Providers}),
throw(unknown_authn_type);
Module ->
%% TODO: check if Module:check_config/1 is exported
%% so we do not force all providers to implement hocon schema
try hocon_schema:check_plain(Module, #{<<"config">> => Config},
#{atom_key => true}) of
#{config := Result} ->
Result
catch
C : E : S ->
?SLOG(warning, #{msg => "failed_to_check_config", config => Config}),
erlang:raise(C, E, S)
end
do_check_conifg(Type, Config, Module)
end.
do_check_conifg(Type, Config, Module) ->
F = case erlang:function_exported(Module, check_config, 1) of
true ->
fun Module:check_config/1;
false ->
fun(C) ->
#{config := R} =
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.
return_map([L]) -> L;

View File

@ -26,13 +26,13 @@
-include_lib("eunit/include/eunit.hrl").
-include_lib("typerefl/include/types.hrl").
-export([ fields/1 ]).
-export([ roots/0, fields/1 ]).
-export([ refs/0
, create/1
-export([ create/1
, update/2
, authenticate/2
, destroy/1
, check_config/1
]).
-define(AUTHN, emqx_authentication).
@ -42,6 +42,8 @@
%% Hocon Schema
%%------------------------------------------------------------------------------
roots() -> [{config, #{type => hoconsc:union([hoconsc:ref(type1), hoconsc:ref(type2)])}}].
fields(type1) ->
[ {mechanism, {enum, ['password-based']}}
, {backend, {enum, ['built-in-database']}}
@ -62,10 +64,11 @@ enable(_) -> undefined.
%% Callbacks
%%------------------------------------------------------------------------------
refs() ->
[ hoconsc:ref(?MODULE, type1)
, hoconsc:ref(?MODULE, type2)
].
check_config(C) ->
#{config := R} =
hocon_schema:check_plain(?MODULE, #{<<"config">> => C},
#{atom_key => true}),
R.
create(_Config) ->
{ok, #{mark => 1}}.
@ -268,14 +271,14 @@ t_convert_certs(Config) when is_list(Config) ->
, {<<"cacertfile">>, "cacert.pem"}
]),
CertsDir = ?AUTHN:certs_dir([Global, <<"password-based:built-in-database">>]),
#{<<"ssl">> := NCerts} = ?AUTHN:convert_certs(CertsDir, #{<<"ssl">> => Certs}),
CertsDir = certs_dir(Config, [Global, <<"password-based:built-in-database">>]),
#{<<"ssl">> := NCerts} = convert_certs(CertsDir, #{<<"ssl">> => Certs}),
?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts), maps:get(<<"keyfile">>, Certs))),
Certs2 = certs([ {<<"keyfile">>, "key.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(maps:get(<<"keyfile">>, NCerts), maps:get(<<"keyfile">>, 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"}
, {<<"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))),
?assertNotEqual(maps:get(<<"keyfile">>, NCerts2), maps:get(<<"keyfile">>, NCerts3)),
?assertNotEqual(maps:get(<<"certfile">>, NCerts2), maps:get(<<"certfile">>, 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))).
update_config(Path, ConfigRequest) ->
@ -305,7 +308,22 @@ certs(Certs) ->
diff_cert(CertFile, CertPem2) ->
{ok, CertPem1} = file:read_file(CertFile),
?AUTHN:diff_cert(CertPem1, CertPem2).
emqx_authentication_config:diff_cert(CertPem1, CertPem2).
register_provider(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).