fix(authn): throw exception on invalid config from the API

This commit is contained in:
Zaiming (Stone) Shi 2023-01-18 12:40:57 +01:00
parent a9c650da66
commit 83e66da2aa
6 changed files with 32 additions and 54 deletions

View File

@ -414,32 +414,10 @@ check_config(SchemaMod, RawConf, Opts0) ->
do_check_config(SchemaMod, RawConf, Opts0) do_check_config(SchemaMod, RawConf, Opts0)
catch catch
throw:Errors:Stacktrace -> throw:Errors:Stacktrace ->
throw(emqx_hocon:compact_errors(Errors, Stacktrace)) {error, Reason} = emqx_hocon:compact_errors(Errors, Stacktrace),
erlang:raise(throw, Reason, Stacktrace)
end. end.
%% HOCON tries to be very informative about all the detailed errors
%% it's maybe too much when reporting to the user
-spec compact_errors(any(), any()) -> no_return().
compact_errors(Schema, [Error0 | More]) when is_map(Error0) ->
Error1 =
case length(More) of
0 ->
Error0;
N ->
Error0#{unshown_errors => N}
end,
Error =
case is_atom(Schema) of
true ->
Error1#{schema_module => Schema};
false ->
Error1
end,
throw(Error);
compact_errors(Schema, Errors) ->
%% unexpected, we need the stacktrace reported, hence error
error({Schema, Errors}).
do_check_config(SchemaMod, RawConf, Opts0) -> do_check_config(SchemaMod, RawConf, Opts0) ->
Opts1 = #{ Opts1 = #{
return_plain => true, return_plain => true,

View File

@ -20,6 +20,7 @@
-export([ -export([
format_path/1, format_path/1,
check/2, check/2,
check/3,
compact_errors/2, compact_errors/2,
format_error/1, format_error/1,
format_error/2, format_error/2,
@ -37,20 +38,23 @@ format_path([Name | Rest]) -> [iol(Name), "." | format_path(Rest)].
%% Always return plain map with atom keys. %% Always return plain map with atom keys.
-spec check(module(), hocon:config() | iodata()) -> -spec check(module(), hocon:config() | iodata()) ->
{ok, hocon:config()} | {error, any()}. {ok, hocon:config()} | {error, any()}.
check(SchemaModule, Conf) when is_map(Conf) -> check(SchemaModule, Conf) ->
%% TODO: remove required %% TODO: remove required
%% fields should state required or not in their schema %% fields should state required or not in their schema
Opts = #{atom_key => true, required => false}, Opts = #{atom_key => true, required => false},
check(SchemaModule, Conf, Opts).
check(SchemaModule, Conf, Opts) when is_map(Conf) ->
try try
{ok, hocon_tconf:check_plain(SchemaModule, Conf, Opts)} {ok, hocon_tconf:check_plain(SchemaModule, Conf, Opts)}
catch catch
throw:Errors:Stacktrace -> throw:Errors:Stacktrace ->
compact_errors(Errors, Stacktrace) compact_errors(Errors, Stacktrace)
end; end;
check(SchemaModule, HoconText) -> check(SchemaModule, HoconText, Opts) ->
case hocon:binary(HoconText, #{format => map}) of case hocon:binary(HoconText, #{format => map}) of
{ok, MapConfig} -> {ok, MapConfig} ->
check(SchemaModule, MapConfig); check(SchemaModule, MapConfig, Opts);
{error, Reason} -> {error, Reason} ->
{error, Reason} {error, Reason}
end. end.

View File

@ -1,7 +1,7 @@
%% -*- mode: erlang -*- %% -*- mode: erlang -*-
{application, emqx_authn, [ {application, emqx_authn, [
{description, "EMQX Authentication"}, {description, "EMQX Authentication"},
{vsn, "0.1.12"}, {vsn, "0.1.13"},
{modules, []}, {modules, []},
{registered, [emqx_authn_sup, emqx_authn_registry]}, {registered, [emqx_authn_sup, emqx_authn_registry]},
{applications, [kernel, stdlib, emqx_resource, emqx_connector, ehttpc, epgsql, mysql, jose]}, {applications, [kernel, stdlib, emqx_resource, emqx_connector, ehttpc, epgsql, mysql, jose]},

View File

@ -69,11 +69,7 @@ do_check_config(#{<<"mechanism">> := Mec0} = Config, Opts) ->
false -> false ->
throw(#{error => unknown_authn_provider, which => Key}); throw(#{error => unknown_authn_provider, which => Key});
{_, ProviderModule} -> {_, ProviderModule} ->
emqx_hocon:check( do_check_config_maybe_throw(ProviderModule, Config, Opts)
ProviderModule,
#{?CONF_NS_BINARY => Config},
Opts#{atom_key => true}
)
end; end;
do_check_config(Config, Opts) when is_map(Config) -> do_check_config(Config, Opts) when is_map(Config) ->
throw(#{ throw(#{
@ -82,6 +78,15 @@ do_check_config(Config, Opts) when is_map(Config) ->
reason => "mechanism_field_required" reason => "mechanism_field_required"
}). }).
do_check_config_maybe_throw(ProviderModule, Config0, Opts) ->
Config = #{?CONF_NS_BINARY => Config0},
case emqx_hocon:check(ProviderModule, Config, Opts#{atom_key => true}) of
{ok, Checked} ->
Checked;
{error, Reason} ->
throw(Reason)
end.
%% The atoms have to be loaded already, %% The atoms have to be loaded already,
%% which might be an issue for plugins which are loaded after node boot %% which might be an issue for plugins which are loaded after node boot
%% but they should really manage their own configs in that case. %% but they should really manage their own configs in that case.

View File

@ -53,26 +53,17 @@ stop(_State) ->
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
initialize() -> initialize() ->
try ok = ?AUTHN:register_providers(emqx_authn:providers()),
ok = ?AUTHN:register_providers(emqx_authn:providers()), lists:foreach(
fun({ChainName, RawAuthConfigs}) ->
lists:foreach( AuthConfig = emqx_authn:check_configs(RawAuthConfigs),
fun({ChainName, RawAuthConfigs}) -> ?AUTHN:initialize_authentication(
AuthConfig = emqx_authn:check_configs(RawAuthConfigs), ChainName,
?AUTHN:initialize_authentication( AuthConfig
ChainName, )
AuthConfig end,
) chain_configs()
end, ).
chain_configs()
)
of
ok -> ok
catch
throw:Reason ->
?SLOG(error, #{msg => "failed_to_initialize_authentication", reason => Reason}),
{error, {failed_to_initialize_authentication, Reason}}
end.
deinitialize() -> deinitialize() ->
ok = ?AUTHN:deregister_providers(provider_types()), ok = ?AUTHN:deregister_providers(provider_types()),

View File

@ -1,6 +1,6 @@
{application, emqx_conf, [ {application, emqx_conf, [
{description, "EMQX configuration management"}, {description, "EMQX configuration management"},
{vsn, "0.1.11"}, {vsn, "0.1.12"},
{registered, []}, {registered, []},
{mod, {emqx_conf_app, []}}, {mod, {emqx_conf_app, []}},
{applications, [kernel, stdlib]}, {applications, [kernel, stdlib]},