refactor: for better test coverage
This commit is contained in:
parent
2ebc89e339
commit
a9c650da66
|
@ -413,8 +413,8 @@ check_config(SchemaMod, RawConf, Opts0) ->
|
||||||
try
|
try
|
||||||
do_check_config(SchemaMod, RawConf, Opts0)
|
do_check_config(SchemaMod, RawConf, Opts0)
|
||||||
catch
|
catch
|
||||||
throw:{Schema, Errors} ->
|
throw:Errors:Stacktrace ->
|
||||||
compact_errors(Schema, Errors)
|
throw(emqx_hocon:compact_errors(Errors, Stacktrace))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% HOCON tries to be very informative about all the detailed errors
|
%% HOCON tries to be very informative about all the detailed errors
|
||||||
|
@ -425,8 +425,8 @@ compact_errors(Schema, [Error0 | More]) when is_map(Error0) ->
|
||||||
case length(More) of
|
case length(More) of
|
||||||
0 ->
|
0 ->
|
||||||
Error0;
|
Error0;
|
||||||
_ ->
|
N ->
|
||||||
Error0#{unshown_errors => length(More)}
|
Error0#{unshown_errors => N}
|
||||||
end,
|
end,
|
||||||
Error =
|
Error =
|
||||||
case is_atom(Schema) of
|
case is_atom(Schema) of
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
-export([
|
-export([
|
||||||
format_path/1,
|
format_path/1,
|
||||||
check/2,
|
check/2,
|
||||||
|
compact_errors/2,
|
||||||
format_error/1,
|
format_error/1,
|
||||||
format_error/2,
|
format_error/2,
|
||||||
make_schema/1
|
make_schema/1
|
||||||
|
@ -43,8 +44,8 @@ check(SchemaModule, Conf) 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:Reason ->
|
throw:Errors:Stacktrace ->
|
||||||
{error, Reason}
|
compact_errors(Errors, Stacktrace)
|
||||||
end;
|
end;
|
||||||
check(SchemaModule, HoconText) ->
|
check(SchemaModule, HoconText) ->
|
||||||
case hocon:binary(HoconText, #{format => map}) of
|
case hocon:binary(HoconText, #{format => map}) of
|
||||||
|
@ -90,3 +91,34 @@ iol(L) when is_list(L) -> L.
|
||||||
|
|
||||||
no_stacktrace(Map) ->
|
no_stacktrace(Map) ->
|
||||||
maps:without([stacktrace], Map).
|
maps:without([stacktrace], Map).
|
||||||
|
|
||||||
|
%% @doc 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(), Stacktrace :: list()) -> {error, any()}.
|
||||||
|
compact_errors({SchemaModule, Errors}, Stacktrace) ->
|
||||||
|
compact_errors(SchemaModule, Errors, Stacktrace).
|
||||||
|
|
||||||
|
compact_errors(SchemaModule, [Error0 | More], _Stacktrace) when is_map(Error0) ->
|
||||||
|
Error1 =
|
||||||
|
case length(More) of
|
||||||
|
0 ->
|
||||||
|
Error0;
|
||||||
|
N ->
|
||||||
|
Error0#{unshown_errors_count => N}
|
||||||
|
end,
|
||||||
|
Error =
|
||||||
|
case is_atom(SchemaModule) of
|
||||||
|
true ->
|
||||||
|
Error1#{schema_module => SchemaModule};
|
||||||
|
false ->
|
||||||
|
Error1
|
||||||
|
end,
|
||||||
|
{error, Error};
|
||||||
|
compact_errors(SchemaModule, Error, Stacktrace) ->
|
||||||
|
%% unexpected, we need the stacktrace reported, hence error
|
||||||
|
%% if this happens i'ts a bug in hocon_tconf
|
||||||
|
{error, #{
|
||||||
|
schema_module => SchemaModule,
|
||||||
|
exception => Error,
|
||||||
|
stacktrace => Stacktrace
|
||||||
|
}}.
|
||||||
|
|
|
@ -69,7 +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} ->
|
||||||
hocon_tconf:check_plain(
|
emqx_hocon:check(
|
||||||
ProviderModule,
|
ProviderModule,
|
||||||
#{?CONF_NS_BINARY => Config},
|
#{?CONF_NS_BINARY => Config},
|
||||||
Opts#{atom_key => true}
|
Opts#{atom_key => true}
|
||||||
|
|
|
@ -105,13 +105,10 @@ select_union_member(Value, _Providers) ->
|
||||||
throw(#{reason => "not_a_struct", value => Value}).
|
throw(#{reason => "not_a_struct", value => Value}).
|
||||||
|
|
||||||
try_select_union_member(Module, Value) ->
|
try_select_union_member(Module, Value) ->
|
||||||
%% some modules have refs/1 exported to help selectin the sub-types
|
%% some modules have union_member_selector/1 exported to help selectin the sub-types
|
||||||
%% emqx_authn_http, emqx_authn_jwt, emqx_authn_mongodb and emqx_authn_redis
|
%% emqx_authn_http, emqx_authn_jwt, emqx_authn_mongodb and emqx_authn_redis
|
||||||
try Module:refs(Value) of
|
try
|
||||||
{ok, Type} ->
|
Module:union_member_selector({value, Value})
|
||||||
[Type];
|
|
||||||
{error, Reason} ->
|
|
||||||
throw(Reason)
|
|
||||||
catch
|
catch
|
||||||
error:undef ->
|
error:undef ->
|
||||||
%% otherwise expect only one member from this module
|
%% otherwise expect only one member from this module
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
refs/0,
|
refs/0,
|
||||||
refs/1,
|
union_member_selector/1,
|
||||||
create/2,
|
create/2,
|
||||||
update/2,
|
update/2,
|
||||||
authenticate/2,
|
authenticate/2,
|
||||||
|
@ -60,7 +60,7 @@ roots() ->
|
||||||
[
|
[
|
||||||
{?CONF_NS,
|
{?CONF_NS,
|
||||||
hoconsc:mk(
|
hoconsc:mk(
|
||||||
hoconsc:union(refs()),
|
hoconsc:union(fun union_member_selector/1),
|
||||||
#{}
|
#{}
|
||||||
)}
|
)}
|
||||||
].
|
].
|
||||||
|
@ -160,15 +160,20 @@ refs() ->
|
||||||
hoconsc:ref(?MODULE, post)
|
hoconsc:ref(?MODULE, post)
|
||||||
].
|
].
|
||||||
|
|
||||||
|
union_member_selector(all_union_members) ->
|
||||||
|
refs();
|
||||||
|
union_member_selector({value, Value}) ->
|
||||||
|
refs(Value).
|
||||||
|
|
||||||
refs(#{<<"method">> := <<"get">>}) ->
|
refs(#{<<"method">> := <<"get">>}) ->
|
||||||
{ok, hoconsc:ref(?MODULE, get)};
|
[hoconsc:ref(?MODULE, get)];
|
||||||
refs(#{<<"method">> := <<"post">>}) ->
|
refs(#{<<"method">> := <<"post">>}) ->
|
||||||
{ok, hoconsc:ref(?MODULE, post)};
|
[hoconsc:ref(?MODULE, post)];
|
||||||
refs(_) ->
|
refs(_) ->
|
||||||
{error, #{
|
throw(#{
|
||||||
field_name => method,
|
field_name => method,
|
||||||
expected => "get | post"
|
expected => "get | post"
|
||||||
}}.
|
}).
|
||||||
|
|
||||||
create(_AuthenticatorID, Config) ->
|
create(_AuthenticatorID, Config) ->
|
||||||
create(Config).
|
create(Config).
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
refs/0,
|
refs/0,
|
||||||
refs/1,
|
union_member_selector/1,
|
||||||
create/2,
|
create/2,
|
||||||
update/2,
|
update/2,
|
||||||
authenticate/2,
|
authenticate/2,
|
||||||
|
@ -52,7 +52,7 @@ roots() ->
|
||||||
[
|
[
|
||||||
{?CONF_NS,
|
{?CONF_NS,
|
||||||
hoconsc:mk(
|
hoconsc:mk(
|
||||||
hoconsc:union(refs()),
|
hoconsc:union(fun union_member_selector/1),
|
||||||
#{}
|
#{}
|
||||||
)}
|
)}
|
||||||
].
|
].
|
||||||
|
@ -169,7 +169,9 @@ refs() ->
|
||||||
hoconsc:ref(?MODULE, 'jwks')
|
hoconsc:ref(?MODULE, 'jwks')
|
||||||
].
|
].
|
||||||
|
|
||||||
refs(#{<<"mechanism">> := <<"jwt">>} = V) ->
|
union_member_selector(all_union_members) ->
|
||||||
|
refs();
|
||||||
|
union_member_selector({value, V}) ->
|
||||||
UseJWKS = maps:get(<<"use_jwks">>, V, undefined),
|
UseJWKS = maps:get(<<"use_jwks">>, V, undefined),
|
||||||
select_ref(boolean(UseJWKS), V).
|
select_ref(boolean(UseJWKS), V).
|
||||||
|
|
||||||
|
@ -181,16 +183,16 @@ boolean(<<"false">>) -> false;
|
||||||
boolean(Other) -> Other.
|
boolean(Other) -> Other.
|
||||||
|
|
||||||
select_ref(true, _) ->
|
select_ref(true, _) ->
|
||||||
{ok, hoconsc:ref(?MODULE, 'jwks')};
|
[hoconsc:ref(?MODULE, 'jwks')];
|
||||||
select_ref(false, #{<<"public_key">> := _}) ->
|
select_ref(false, #{<<"public_key">> := _}) ->
|
||||||
{ok, hoconsc:ref(?MODULE, 'public-key')};
|
[hoconsc:ref(?MODULE, 'public-key')];
|
||||||
select_ref(false, _) ->
|
select_ref(false, _) ->
|
||||||
{ok, hoconsc:ref(?MODULE, 'hmac-based')};
|
[hoconsc:ref(?MODULE, 'hmac-based')];
|
||||||
select_ref(_, _) ->
|
select_ref(_, _) ->
|
||||||
{error, #{
|
throw(#{
|
||||||
field_name => use_jwks,
|
field_name => use_jwks,
|
||||||
expected => "true | false"
|
expected => "true | false"
|
||||||
}}.
|
}).
|
||||||
|
|
||||||
create(_AuthenticatorID, Config) ->
|
create(_AuthenticatorID, Config) ->
|
||||||
create(Config).
|
create(Config).
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
refs/0,
|
refs/0,
|
||||||
refs/1,
|
union_member_selector/1,
|
||||||
create/2,
|
create/2,
|
||||||
update/2,
|
update/2,
|
||||||
authenticate/2,
|
authenticate/2,
|
||||||
|
@ -53,7 +53,7 @@ roots() ->
|
||||||
[
|
[
|
||||||
{?CONF_NS,
|
{?CONF_NS,
|
||||||
hoconsc:mk(
|
hoconsc:mk(
|
||||||
hoconsc:union(refs()),
|
hoconsc:union(fun union_member_selector/1),
|
||||||
#{}
|
#{}
|
||||||
)}
|
)}
|
||||||
].
|
].
|
||||||
|
@ -131,18 +131,6 @@ refs() ->
|
||||||
hoconsc:ref(?MODULE, 'sharded-cluster')
|
hoconsc:ref(?MODULE, 'sharded-cluster')
|
||||||
].
|
].
|
||||||
|
|
||||||
refs(#{<<"mongo_type">> := <<"single">>}) ->
|
|
||||||
{ok, hoconsc:ref(?MODULE, standalone)};
|
|
||||||
refs(#{<<"mongo_type">> := <<"rs">>}) ->
|
|
||||||
{ok, hoconsc:ref(?MODULE, 'replica-set')};
|
|
||||||
refs(#{<<"mongo_type">> := <<"sharded">>}) ->
|
|
||||||
{ok, hoconsc:ref(?MODULE, 'sharded-cluster')};
|
|
||||||
refs(_) ->
|
|
||||||
{error, #{
|
|
||||||
field_name => mongo_type,
|
|
||||||
expected => "single | rs | sharded"
|
|
||||||
}}.
|
|
||||||
|
|
||||||
create(_AuthenticatorID, Config) ->
|
create(_AuthenticatorID, Config) ->
|
||||||
create(Config).
|
create(Config).
|
||||||
|
|
||||||
|
@ -259,3 +247,20 @@ is_superuser(Doc, #{is_superuser_field := IsSuperuserField}) ->
|
||||||
emqx_authn_utils:is_superuser(#{<<"is_superuser">> => IsSuperuser});
|
emqx_authn_utils:is_superuser(#{<<"is_superuser">> => IsSuperuser});
|
||||||
is_superuser(_, _) ->
|
is_superuser(_, _) ->
|
||||||
emqx_authn_utils:is_superuser(#{<<"is_superuser">> => false}).
|
emqx_authn_utils:is_superuser(#{<<"is_superuser">> => false}).
|
||||||
|
|
||||||
|
union_member_selector(all_union_members) ->
|
||||||
|
refs();
|
||||||
|
union_member_selector({value, Value}) ->
|
||||||
|
refs(Value).
|
||||||
|
|
||||||
|
refs(#{<<"mongo_type">> := <<"single">>}) ->
|
||||||
|
[hoconsc:ref(?MODULE, standalone)];
|
||||||
|
refs(#{<<"mongo_type">> := <<"rs">>}) ->
|
||||||
|
[hoconsc:ref(?MODULE, 'replica-set')];
|
||||||
|
refs(#{<<"mongo_type">> := <<"sharded">>}) ->
|
||||||
|
[hoconsc:ref(?MODULE, 'sharded-cluster')];
|
||||||
|
refs(_) ->
|
||||||
|
throw(#{
|
||||||
|
field_name => mongo_type,
|
||||||
|
expected => "single | rs | sharded"
|
||||||
|
}).
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
refs/0,
|
refs/0,
|
||||||
refs/1,
|
union_member_selector/1,
|
||||||
create/2,
|
create/2,
|
||||||
update/2,
|
update/2,
|
||||||
authenticate/2,
|
authenticate/2,
|
||||||
|
@ -53,7 +53,7 @@ roots() ->
|
||||||
[
|
[
|
||||||
{?CONF_NS,
|
{?CONF_NS,
|
||||||
hoconsc:mk(
|
hoconsc:mk(
|
||||||
hoconsc:union(refs()),
|
hoconsc:union(fun union_member_selector/1),
|
||||||
#{}
|
#{}
|
||||||
)}
|
)}
|
||||||
].
|
].
|
||||||
|
@ -98,17 +98,22 @@ refs() ->
|
||||||
hoconsc:ref(?MODULE, sentinel)
|
hoconsc:ref(?MODULE, sentinel)
|
||||||
].
|
].
|
||||||
|
|
||||||
|
union_member_selector(all_union_members) ->
|
||||||
|
refs();
|
||||||
|
union_member_selector({value, Value}) ->
|
||||||
|
refs(Value).
|
||||||
|
|
||||||
refs(#{<<"redis_type">> := <<"single">>}) ->
|
refs(#{<<"redis_type">> := <<"single">>}) ->
|
||||||
{ok, hoconsc:ref(?MODULE, standalone)};
|
[hoconsc:ref(?MODULE, standalone)];
|
||||||
refs(#{<<"redis_type">> := <<"cluster">>}) ->
|
refs(#{<<"redis_type">> := <<"cluster">>}) ->
|
||||||
{ok, hoconsc:ref(?MODULE, cluster)};
|
[hoconsc:ref(?MODULE, cluster)];
|
||||||
refs(#{<<"redis_type">> := <<"sentinel">>}) ->
|
refs(#{<<"redis_type">> := <<"sentinel">>}) ->
|
||||||
{ok, hoconsc:ref(?MODULE, sentinel)};
|
[hoconsc:ref(?MODULE, sentinel)];
|
||||||
refs(_) ->
|
refs(_) ->
|
||||||
{error, #{
|
throw(#{
|
||||||
field_name => redis_type,
|
field_name => redis_type,
|
||||||
expected => "single | cluster | sentinel"
|
expected => "single | cluster | sentinel"
|
||||||
}}.
|
}).
|
||||||
|
|
||||||
create(_AuthenticatorID, Config) ->
|
create(_AuthenticatorID, Config) ->
|
||||||
create(Config).
|
create(Config).
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 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_schema_tests).
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
%% schema error
|
||||||
|
-define(ERR(Reason), {error, Reason}).
|
||||||
|
|
||||||
|
union_member_selector_mongo_test_() ->
|
||||||
|
Check = fun(Txt) -> check(emqx_authn_mongodb, Txt) end,
|
||||||
|
[
|
||||||
|
{"unknown", fun() ->
|
||||||
|
?assertMatch(
|
||||||
|
?ERR(#{field_name := mongo_type, expected := _}),
|
||||||
|
Check("{mongo_type: foobar}")
|
||||||
|
)
|
||||||
|
end},
|
||||||
|
{"single", fun() ->
|
||||||
|
?assertMatch(
|
||||||
|
?ERR(#{matched_type := "authn-mongodb:standalone"}),
|
||||||
|
Check("{mongo_type: single}")
|
||||||
|
)
|
||||||
|
end},
|
||||||
|
{"replica-set", fun() ->
|
||||||
|
?assertMatch(
|
||||||
|
?ERR(#{matched_type := "authn-mongodb:replica-set"}),
|
||||||
|
Check("{mongo_type: rs}")
|
||||||
|
)
|
||||||
|
end},
|
||||||
|
{"sharded", fun() ->
|
||||||
|
?assertMatch(
|
||||||
|
?ERR(#{matched_type := "authn-mongodb:sharded-cluster"}),
|
||||||
|
Check("{mongo_type: sharded}")
|
||||||
|
)
|
||||||
|
end}
|
||||||
|
].
|
||||||
|
|
||||||
|
union_member_selector_jwt_test_() ->
|
||||||
|
Check = fun(Txt) -> check(emqx_authn_jwt, Txt) end,
|
||||||
|
[
|
||||||
|
{"unknown", fun() ->
|
||||||
|
?assertMatch(
|
||||||
|
?ERR(#{field_name := use_jwks, expected := "true | false"}),
|
||||||
|
Check("{use_jwks = 1}")
|
||||||
|
)
|
||||||
|
end},
|
||||||
|
{"jwks", fun() ->
|
||||||
|
?assertMatch(
|
||||||
|
?ERR(#{matched_type := "authn-jwt:jwks"}),
|
||||||
|
Check("{use_jwks = true}")
|
||||||
|
)
|
||||||
|
end},
|
||||||
|
{"publick-key", fun() ->
|
||||||
|
?assertMatch(
|
||||||
|
?ERR(#{matched_type := "authn-jwt:public-key"}),
|
||||||
|
Check("{use_jwks = false, public_key = 1}")
|
||||||
|
)
|
||||||
|
end},
|
||||||
|
{"hmac-based", fun() ->
|
||||||
|
?assertMatch(
|
||||||
|
?ERR(#{matched_type := "authn-jwt:hmac-based"}),
|
||||||
|
Check("{use_jwks = false}")
|
||||||
|
)
|
||||||
|
end}
|
||||||
|
].
|
||||||
|
|
||||||
|
union_member_selector_redis_test_() ->
|
||||||
|
Check = fun(Txt) -> check(emqx_authn_redis, Txt) end,
|
||||||
|
[
|
||||||
|
{"unknown", fun() ->
|
||||||
|
?assertMatch(
|
||||||
|
?ERR(#{field_name := redis_type, expected := _}),
|
||||||
|
Check("{redis_type = 1}")
|
||||||
|
)
|
||||||
|
end},
|
||||||
|
{"single", fun() ->
|
||||||
|
?assertMatch(
|
||||||
|
?ERR(#{matched_type := "authn-redis:standalone"}),
|
||||||
|
Check("{redis_type = single}")
|
||||||
|
)
|
||||||
|
end},
|
||||||
|
{"cluster", fun() ->
|
||||||
|
?assertMatch(
|
||||||
|
?ERR(#{matched_type := "authn-redis:cluster"}),
|
||||||
|
Check("{redis_type = cluster}")
|
||||||
|
)
|
||||||
|
end},
|
||||||
|
{"sentinel", fun() ->
|
||||||
|
?assertMatch(
|
||||||
|
?ERR(#{matched_type := "authn-redis:sentinel"}),
|
||||||
|
Check("{redis_type = sentinel}")
|
||||||
|
)
|
||||||
|
end}
|
||||||
|
].
|
||||||
|
|
||||||
|
union_member_selector_http_test_() ->
|
||||||
|
Check = fun(Txt) -> check(emqx_authn_http, Txt) end,
|
||||||
|
[
|
||||||
|
{"unknown", fun() ->
|
||||||
|
?assertMatch(
|
||||||
|
?ERR(#{field_name := method, expected := _}),
|
||||||
|
Check("{method = 1}")
|
||||||
|
)
|
||||||
|
end},
|
||||||
|
{"get", fun() ->
|
||||||
|
?assertMatch(
|
||||||
|
?ERR(#{matched_type := "authn-http:get"}),
|
||||||
|
Check("{method = get}")
|
||||||
|
)
|
||||||
|
end},
|
||||||
|
{"post", fun() ->
|
||||||
|
?assertMatch(
|
||||||
|
?ERR(#{matched_type := "authn-http:post"}),
|
||||||
|
Check("{method = post}")
|
||||||
|
)
|
||||||
|
end}
|
||||||
|
].
|
||||||
|
|
||||||
|
check(Module, HoconConf) ->
|
||||||
|
emqx_hocon:check(Module, ["authentication= ", HoconConf]).
|
Loading…
Reference in New Issue