feat(authn): allow authn providers to define a separate schama for API
This commit is contained in:
parent
90a0c093bf
commit
6354f3b04f
|
@ -147,7 +147,7 @@ schema("/authentication") ->
|
||||||
description => ?DESC(authentication_get),
|
description => ?DESC(authentication_get),
|
||||||
responses => #{
|
responses => #{
|
||||||
200 => emqx_dashboard_swagger:schema_with_example(
|
200 => emqx_dashboard_swagger:schema_with_example(
|
||||||
hoconsc:array(emqx_authn_schema:authenticator_type()),
|
hoconsc:array(authenticator_type(config)),
|
||||||
authenticator_array_example()
|
authenticator_array_example()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -156,12 +156,12 @@ schema("/authentication") ->
|
||||||
tags => ?API_TAGS_GLOBAL,
|
tags => ?API_TAGS_GLOBAL,
|
||||||
description => ?DESC(authentication_post),
|
description => ?DESC(authentication_post),
|
||||||
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
emqx_authn_schema:authenticator_type(),
|
authenticator_type(api_write),
|
||||||
authenticator_examples()
|
authenticator_examples()
|
||||||
),
|
),
|
||||||
responses => #{
|
responses => #{
|
||||||
200 => emqx_dashboard_swagger:schema_with_examples(
|
200 => emqx_dashboard_swagger:schema_with_examples(
|
||||||
emqx_authn_schema:authenticator_type(),
|
authenticator_type(config),
|
||||||
authenticator_examples()
|
authenticator_examples()
|
||||||
),
|
),
|
||||||
400 => error_codes([?BAD_REQUEST], <<"Bad Request">>),
|
400 => error_codes([?BAD_REQUEST], <<"Bad Request">>),
|
||||||
|
@ -178,7 +178,7 @@ schema("/authentication/:id") ->
|
||||||
parameters => [param_auth_id()],
|
parameters => [param_auth_id()],
|
||||||
responses => #{
|
responses => #{
|
||||||
200 => emqx_dashboard_swagger:schema_with_examples(
|
200 => emqx_dashboard_swagger:schema_with_examples(
|
||||||
emqx_authn_schema:authenticator_type(),
|
authenticator_type(config),
|
||||||
authenticator_examples()
|
authenticator_examples()
|
||||||
),
|
),
|
||||||
404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
|
@ -189,7 +189,7 @@ schema("/authentication/:id") ->
|
||||||
description => ?DESC(authentication_id_put),
|
description => ?DESC(authentication_id_put),
|
||||||
parameters => [param_auth_id()],
|
parameters => [param_auth_id()],
|
||||||
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
emqx_authn_schema:authenticator_type(),
|
authenticator_type(api_write),
|
||||||
authenticator_examples()
|
authenticator_examples()
|
||||||
),
|
),
|
||||||
responses => #{
|
responses => #{
|
||||||
|
@ -236,7 +236,7 @@ schema("/listeners/:listener_id/authentication") ->
|
||||||
parameters => [param_listener_id()],
|
parameters => [param_listener_id()],
|
||||||
responses => #{
|
responses => #{
|
||||||
200 => emqx_dashboard_swagger:schema_with_example(
|
200 => emqx_dashboard_swagger:schema_with_example(
|
||||||
hoconsc:array(emqx_authn_schema:authenticator_type()),
|
hoconsc:array(authenticator_type(config)),
|
||||||
authenticator_array_example()
|
authenticator_array_example()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -247,12 +247,12 @@ schema("/listeners/:listener_id/authentication") ->
|
||||||
description => ?DESC(listeners_listener_id_authentication_post),
|
description => ?DESC(listeners_listener_id_authentication_post),
|
||||||
parameters => [param_listener_id()],
|
parameters => [param_listener_id()],
|
||||||
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
emqx_authn_schema:authenticator_type(),
|
authenticator_type(api_write),
|
||||||
authenticator_examples()
|
authenticator_examples()
|
||||||
),
|
),
|
||||||
responses => #{
|
responses => #{
|
||||||
200 => emqx_dashboard_swagger:schema_with_examples(
|
200 => emqx_dashboard_swagger:schema_with_examples(
|
||||||
emqx_authn_schema:authenticator_type(),
|
authenticator_type(config),
|
||||||
authenticator_examples()
|
authenticator_examples()
|
||||||
),
|
),
|
||||||
400 => error_codes([?BAD_REQUEST], <<"Bad Request">>),
|
400 => error_codes([?BAD_REQUEST], <<"Bad Request">>),
|
||||||
|
@ -270,7 +270,7 @@ schema("/listeners/:listener_id/authentication/:id") ->
|
||||||
parameters => [param_listener_id(), param_auth_id()],
|
parameters => [param_listener_id(), param_auth_id()],
|
||||||
responses => #{
|
responses => #{
|
||||||
200 => emqx_dashboard_swagger:schema_with_examples(
|
200 => emqx_dashboard_swagger:schema_with_examples(
|
||||||
emqx_authn_schema:authenticator_type(),
|
authenticator_type(config),
|
||||||
authenticator_examples()
|
authenticator_examples()
|
||||||
),
|
),
|
||||||
404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
|
@ -282,7 +282,7 @@ schema("/listeners/:listener_id/authentication/:id") ->
|
||||||
description => ?DESC(listeners_listener_id_authentication_id_put),
|
description => ?DESC(listeners_listener_id_authentication_id_put),
|
||||||
parameters => [param_listener_id(), param_auth_id()],
|
parameters => [param_listener_id(), param_auth_id()],
|
||||||
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
emqx_authn_schema:authenticator_type(),
|
authenticator_type(api_write),
|
||||||
authenticator_examples()
|
authenticator_examples()
|
||||||
),
|
),
|
||||||
responses => #{
|
responses => #{
|
||||||
|
@ -1278,6 +1278,9 @@ paginated_list_type(Type) ->
|
||||||
{meta, ref(emqx_dashboard_swagger, meta)}
|
{meta, ref(emqx_dashboard_swagger, meta)}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
authenticator_type(Kind) ->
|
||||||
|
emqx_authn_schema:authenticator_type(Kind).
|
||||||
|
|
||||||
authenticator_array_example() ->
|
authenticator_array_example() ->
|
||||||
[Config || #{value := Config} <- maps:values(authenticator_examples())].
|
[Config || #{value := Config} <- maps:values(authenticator_examples())].
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,8 @@
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
type_ro/1,
|
type_ro/1,
|
||||||
type_rw/1
|
type_rw/1,
|
||||||
|
type_rw_api/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
|
@ -67,21 +68,17 @@
|
||||||
-define(SALT_ROUNDS_MAX, 10).
|
-define(SALT_ROUNDS_MAX, 10).
|
||||||
|
|
||||||
namespace() -> "authn-hash".
|
namespace() -> "authn-hash".
|
||||||
roots() -> [pbkdf2, bcrypt, bcrypt_rw, simple].
|
roots() -> [pbkdf2, bcrypt, bcrypt_rw, bcrypt_rw_api, simple].
|
||||||
|
|
||||||
fields(bcrypt_rw) ->
|
fields(bcrypt_rw) ->
|
||||||
fields(bcrypt) ++
|
fields(bcrypt) ++
|
||||||
[
|
[
|
||||||
{salt_rounds,
|
{salt_rounds, fun bcrypt_salt_rounds/1}
|
||||||
sc(
|
];
|
||||||
range(?SALT_ROUNDS_MIN, ?SALT_ROUNDS_MAX),
|
fields(bcrypt_rw_api) ->
|
||||||
#{
|
fields(bcrypt) ++
|
||||||
default => ?SALT_ROUNDS_MAX,
|
[
|
||||||
example => ?SALT_ROUNDS_MAX,
|
{salt_rounds, fun bcrypt_salt_rounds_api/1}
|
||||||
desc => "Work factor for BCRYPT password generation.",
|
|
||||||
converter => fun salt_rounds_converter/2
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
];
|
];
|
||||||
fields(bcrypt) ->
|
fields(bcrypt) ->
|
||||||
[{name, sc(bcrypt, #{required => true, desc => "BCRYPT password hashing."})}];
|
[{name, sc(bcrypt, #{required => true, desc => "BCRYPT password hashing."})}];
|
||||||
|
@ -110,6 +107,15 @@ fields(simple) ->
|
||||||
{salt_position, fun salt_position/1}
|
{salt_position, fun salt_position/1}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
bcrypt_salt_rounds(converter) -> fun salt_rounds_converter/2;
|
||||||
|
bcrypt_salt_rounds(Option) -> bcrypt_salt_rounds_api(Option).
|
||||||
|
|
||||||
|
bcrypt_salt_rounds_api(type) -> range(?SALT_ROUNDS_MIN, ?SALT_ROUNDS_MAX);
|
||||||
|
bcrypt_salt_rounds_api(default) -> ?SALT_ROUNDS_MAX;
|
||||||
|
bcrypt_salt_rounds_api(example) -> ?SALT_ROUNDS_MAX;
|
||||||
|
bcrypt_salt_rounds_api(desc) -> "Work factor for BCRYPT password generation.";
|
||||||
|
bcrypt_salt_rounds_api(_) -> undefined.
|
||||||
|
|
||||||
salt_rounds_converter(undefined, _) ->
|
salt_rounds_converter(undefined, _) ->
|
||||||
undefined;
|
undefined;
|
||||||
salt_rounds_converter(I, _) when is_integer(I) ->
|
salt_rounds_converter(I, _) when is_integer(I) ->
|
||||||
|
@ -119,6 +125,8 @@ salt_rounds_converter(X, _) ->
|
||||||
|
|
||||||
desc(bcrypt_rw) ->
|
desc(bcrypt_rw) ->
|
||||||
"Settings for bcrypt password hashing algorithm (for DB backends with write capability).";
|
"Settings for bcrypt password hashing algorithm (for DB backends with write capability).";
|
||||||
|
desc(bcrypt_rw_api) ->
|
||||||
|
desc(bcrypt_rw);
|
||||||
desc(bcrypt) ->
|
desc(bcrypt) ->
|
||||||
"Settings for bcrypt password hashing algorithm.";
|
"Settings for bcrypt password hashing algorithm.";
|
||||||
desc(pbkdf2) ->
|
desc(pbkdf2) ->
|
||||||
|
@ -143,14 +151,20 @@ dk_length(desc) ->
|
||||||
dk_length(_) ->
|
dk_length(_) ->
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
%% for simple_authn/emqx_authn_mnesia
|
%% for emqx_authn_mnesia
|
||||||
type_rw(type) ->
|
type_rw(type) ->
|
||||||
hoconsc:union(rw_refs());
|
hoconsc:union(rw_refs());
|
||||||
type_rw(default) ->
|
|
||||||
#{<<"name">> => sha256, <<"salt_position">> => prefix};
|
|
||||||
type_rw(desc) ->
|
type_rw(desc) ->
|
||||||
"Options for password hash creation and verification.";
|
"Options for password hash creation and verification.";
|
||||||
type_rw(_) ->
|
type_rw(Option) ->
|
||||||
|
type_ro(Option).
|
||||||
|
|
||||||
|
%% for emqx_authn_mnesia API
|
||||||
|
type_rw_api(type) ->
|
||||||
|
hoconsc:union(api_refs());
|
||||||
|
type_rw_api(desc) ->
|
||||||
|
"Options for password hash creation and verification through API.";
|
||||||
|
type_rw_api(_) ->
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
%% for other authn resources
|
%% for other authn resources
|
||||||
|
@ -242,31 +256,41 @@ check_password(#{name := Other, salt_position := SaltPosition}, Salt, PasswordHa
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
rw_refs() ->
|
rw_refs() ->
|
||||||
All = [
|
union_selector(rw).
|
||||||
hoconsc:ref(?MODULE, bcrypt_rw),
|
|
||||||
hoconsc:ref(?MODULE, pbkdf2),
|
|
||||||
hoconsc:ref(?MODULE, simple)
|
|
||||||
],
|
|
||||||
fun
|
|
||||||
(all_union_members) -> All;
|
|
||||||
({value, #{<<"name">> := <<"bcrypt">>}}) -> [hoconsc:ref(?MODULE, bcrypt_rw)];
|
|
||||||
({value, #{<<"name">> := <<"pbkdf2">>}}) -> [hoconsc:ref(?MODULE, pbkdf2)];
|
|
||||||
({value, #{<<"name">> := _}}) -> [hoconsc:ref(?MODULE, simple)];
|
|
||||||
({value, _}) -> throw(#{reason => "algorithm_name_missing"})
|
|
||||||
end.
|
|
||||||
|
|
||||||
ro_refs() ->
|
ro_refs() ->
|
||||||
All = [
|
union_selector(ro).
|
||||||
hoconsc:ref(?MODULE, bcrypt),
|
|
||||||
hoconsc:ref(?MODULE, pbkdf2),
|
api_refs() ->
|
||||||
hoconsc:ref(?MODULE, simple)
|
union_selector(api).
|
||||||
],
|
|
||||||
|
sc(Type, Meta) -> hoconsc:mk(Type, Meta).
|
||||||
|
|
||||||
|
union_selector(Kind) ->
|
||||||
fun
|
fun
|
||||||
(all_union_members) -> All;
|
(all_union_members) -> refs(Kind);
|
||||||
({value, #{<<"name">> := <<"bcrypt">>}}) -> [hoconsc:ref(?MODULE, bcrypt)];
|
({value, #{<<"name">> := <<"bcrypt">>}}) -> [bcrypt_ref(Kind)];
|
||||||
({value, #{<<"name">> := <<"pbkdf2">>}}) -> [hoconsc:ref(?MODULE, pbkdf2)];
|
({value, #{<<"name">> := <<"pbkdf2">>}}) -> [pbkdf2_ref(Kind)];
|
||||||
({value, #{<<"name">> := _}}) -> [hoconsc:ref(?MODULE, simple)];
|
({value, #{<<"name">> := _}}) -> [simple_ref(Kind)];
|
||||||
({value, _}) -> throw(#{reason => "algorithm_name_missing"})
|
({value, _}) -> throw(#{reason => "algorithm_name_missing"})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
sc(Type, Meta) -> hoconsc:mk(Type, Meta).
|
refs(Kind) ->
|
||||||
|
[
|
||||||
|
bcrypt_ref(Kind),
|
||||||
|
pbkdf2_ref(Kind),
|
||||||
|
simple_ref(Kind)
|
||||||
|
].
|
||||||
|
|
||||||
|
pbkdf2_ref(_) ->
|
||||||
|
hoconsc:ref(?MODULE, pbkdf2).
|
||||||
|
|
||||||
|
bcrypt_ref(rw) ->
|
||||||
|
hoconsc:ref(?MODULE, bcrypt_rw);
|
||||||
|
bcrypt_ref(api) ->
|
||||||
|
hoconsc:ref(?MODULE, bcrypt_rw_api);
|
||||||
|
bcrypt_ref(_) ->
|
||||||
|
hoconsc:ref(?MODULE, bcrypt).
|
||||||
|
|
||||||
|
simple_ref(_) ->
|
||||||
|
hoconsc:ref(?MODULE, simple).
|
||||||
|
|
|
@ -34,7 +34,9 @@
|
||||||
tags/0,
|
tags/0,
|
||||||
fields/1,
|
fields/1,
|
||||||
authenticator_type/0,
|
authenticator_type/0,
|
||||||
|
authenticator_type/1,
|
||||||
authenticator_type_without/1,
|
authenticator_type_without/1,
|
||||||
|
authenticator_type_without/2,
|
||||||
mechanism/1,
|
mechanism/1,
|
||||||
backend/1
|
backend/1
|
||||||
]).
|
]).
|
||||||
|
@ -43,17 +45,35 @@
|
||||||
global_auth_fields/0
|
global_auth_fields/0
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
-export_type([shema_kind/0]).
|
||||||
|
|
||||||
-define(AUTHN_MODS_PT_KEY, {?MODULE, authn_schema_mods}).
|
-define(AUTHN_MODS_PT_KEY, {?MODULE, authn_schema_mods}).
|
||||||
|
-define(DEFAULT_SCHEMA_KIND, config).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Authn Source Schema Behaviour
|
%% Authn Source Schema Behaviour
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
-type schema_ref() :: ?R_REF(module(), hocon_schema:name()).
|
-type schema_ref() :: ?R_REF(module(), hocon_schema:name()).
|
||||||
|
-type shema_kind() ::
|
||||||
|
%% api_write: schema for mutating API request validation
|
||||||
|
api_write
|
||||||
|
%% config: schema for config validation
|
||||||
|
| config.
|
||||||
-callback refs() -> [schema_ref()].
|
-callback refs() -> [schema_ref()].
|
||||||
-callback select_union_member(emqx_config:raw_config()) -> schema_ref() | undefined | no_return().
|
-callback refs(shema_kind()) -> [schema_ref()].
|
||||||
|
-callback select_union_member(emqx_config:raw_config()) -> [schema_ref()] | undefined | no_return().
|
||||||
|
-callback select_union_member(shema_kind(), emqx_config:raw_config()) ->
|
||||||
|
[schema_ref()] | undefined | no_return().
|
||||||
-callback fields(hocon_schema:name()) -> [hocon_schema:field()].
|
-callback fields(hocon_schema:name()) -> [hocon_schema:field()].
|
||||||
|
|
||||||
|
-optional_callbacks([
|
||||||
|
select_union_member/1,
|
||||||
|
select_union_member/2,
|
||||||
|
refs/0,
|
||||||
|
refs/1
|
||||||
|
]).
|
||||||
|
|
||||||
roots() -> [].
|
roots() -> [].
|
||||||
|
|
||||||
injected_fields(AuthnSchemaMods) ->
|
injected_fields(AuthnSchemaMods) ->
|
||||||
|
@ -67,45 +87,63 @@ tags() ->
|
||||||
[<<"Authentication">>].
|
[<<"Authentication">>].
|
||||||
|
|
||||||
authenticator_type() ->
|
authenticator_type() ->
|
||||||
hoconsc:union(union_member_selector(provider_schema_mods())).
|
authenticator_type(?DEFAULT_SCHEMA_KIND).
|
||||||
|
|
||||||
|
authenticator_type(Kind) ->
|
||||||
|
hoconsc:union(union_member_selector(Kind, provider_schema_mods())).
|
||||||
|
|
||||||
authenticator_type_without(ProviderSchemaMods) ->
|
authenticator_type_without(ProviderSchemaMods) ->
|
||||||
|
authenticator_type_without(?DEFAULT_SCHEMA_KIND, ProviderSchemaMods).
|
||||||
|
|
||||||
|
authenticator_type_without(Kind, ProviderSchemaMods) ->
|
||||||
hoconsc:union(
|
hoconsc:union(
|
||||||
union_member_selector(provider_schema_mods() -- ProviderSchemaMods)
|
union_member_selector(Kind, provider_schema_mods() -- ProviderSchemaMods)
|
||||||
).
|
).
|
||||||
|
|
||||||
union_member_selector(Mods) ->
|
union_member_selector(Kind, Mods) ->
|
||||||
AllTypes = config_refs(Mods),
|
AllTypes = config_refs(Kind, Mods),
|
||||||
fun
|
fun
|
||||||
(all_union_members) -> AllTypes;
|
(all_union_members) -> AllTypes;
|
||||||
({value, Value}) -> select_union_member(Value, Mods)
|
({value, Value}) -> select_union_member(Kind, Value, Mods)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
select_union_member(#{<<"mechanism">> := Mechanism, <<"backend">> := Backend}, []) ->
|
select_union_member(_Kind, #{<<"mechanism">> := Mechanism, <<"backend">> := Backend}, []) ->
|
||||||
throw(#{
|
throw(#{
|
||||||
reason => "unsupported_mechanism",
|
reason => "unsupported_mechanism",
|
||||||
mechanism => Mechanism,
|
mechanism => Mechanism,
|
||||||
backend => Backend
|
backend => Backend
|
||||||
});
|
});
|
||||||
select_union_member(#{<<"mechanism">> := Mechanism}, []) ->
|
select_union_member(_Kind, #{<<"mechanism">> := Mechanism}, []) ->
|
||||||
throw(#{
|
throw(#{
|
||||||
reason => "unsupported_mechanism",
|
reason => "unsupported_mechanism",
|
||||||
mechanism => Mechanism
|
mechanism => Mechanism
|
||||||
});
|
});
|
||||||
select_union_member(#{<<"mechanism">> := _} = Value, [Mod | Mods]) ->
|
select_union_member(Kind, #{<<"mechanism">> := _} = Value, [Mod | Mods]) ->
|
||||||
case Mod:select_union_member(Value) of
|
case mod_select_union_member(Kind, Value, Mod) of
|
||||||
undefined ->
|
undefined ->
|
||||||
select_union_member(Value, Mods);
|
select_union_member(Kind, Value, Mods);
|
||||||
Member ->
|
Member ->
|
||||||
Member
|
Member
|
||||||
end;
|
end;
|
||||||
select_union_member(#{} = _Value, _Mods) ->
|
select_union_member(_Kind, #{} = _Value, _Mods) ->
|
||||||
throw(#{reason => "missing_mechanism_field"});
|
throw(#{reason => "missing_mechanism_field"});
|
||||||
select_union_member(Value, _Mods) ->
|
select_union_member(_Kind, Value, _Mods) ->
|
||||||
throw(#{reason => "not_a_struct", value => Value}).
|
throw(#{reason => "not_a_struct", value => Value}).
|
||||||
|
|
||||||
config_refs(Mods) ->
|
mod_select_union_member(Kind, Value, Mod) ->
|
||||||
lists:append([Mod:refs() || Mod <- Mods]).
|
emqx_utils:call_first_defined([
|
||||||
|
{Mod, select_union_member, [Kind, Value]},
|
||||||
|
{Mod, select_union_member, [Value]}
|
||||||
|
]).
|
||||||
|
|
||||||
|
config_refs(Kind, Mods) ->
|
||||||
|
lists:append([mod_refs(Kind, Mod) || Mod <- Mods]).
|
||||||
|
|
||||||
|
mod_refs(Kind, Mod) ->
|
||||||
|
emqx_utils:call_first_defined([
|
||||||
|
{Mod, refs, [Kind]},
|
||||||
|
{Mod, refs, []}
|
||||||
|
]).
|
||||||
|
|
||||||
root_type() ->
|
root_type() ->
|
||||||
hoconsc:array(authenticator_type()).
|
hoconsc:array(authenticator_type()).
|
||||||
|
|
|
@ -63,14 +63,16 @@ end_per_testcase(_, Config) ->
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
Apps = emqx_cth_suite:start(
|
Apps = emqx_cth_suite:start(
|
||||||
[
|
[
|
||||||
emqx,
|
|
||||||
emqx_conf,
|
emqx_conf,
|
||||||
|
emqx,
|
||||||
emqx_auth,
|
emqx_auth,
|
||||||
|
%% to load schema
|
||||||
|
{emqx_auth_mnesia, #{start => false}},
|
||||||
emqx_management,
|
emqx_management,
|
||||||
{emqx_dashboard, "dashboard.listeners.http { enable = true, bind = 18083 }"}
|
{emqx_dashboard, "dashboard.listeners.http { enable = true, bind = 18083 }"}
|
||||||
],
|
],
|
||||||
#{
|
#{
|
||||||
work_dir => ?config(priv_dir, Config)
|
work_dir => filename:join(?config(priv_dir, Config), ?MODULE)
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
_ = emqx_common_test_http:create_default_app(),
|
_ = emqx_common_test_http:create_default_app(),
|
||||||
|
@ -535,6 +537,36 @@ ignore_switch_to_global_chain(_) ->
|
||||||
),
|
),
|
||||||
ok = emqtt:disconnect(Client4).
|
ok = emqtt:disconnect(Client4).
|
||||||
|
|
||||||
|
t_bcrypt_validation(_Config) ->
|
||||||
|
BaseConf = #{
|
||||||
|
mechanism => <<"password_based">>,
|
||||||
|
backend => <<"built_in_database">>,
|
||||||
|
user_id_type => <<"username">>
|
||||||
|
},
|
||||||
|
BcryptValid = #{
|
||||||
|
name => <<"bcrypt">>,
|
||||||
|
salt_rounds => 10
|
||||||
|
},
|
||||||
|
BcryptInvalid = #{
|
||||||
|
name => <<"bcrypt">>,
|
||||||
|
salt_rounds => 15
|
||||||
|
},
|
||||||
|
|
||||||
|
ConfValid = BaseConf#{password_hash_algorithm => BcryptValid},
|
||||||
|
ConfInvalid = BaseConf#{password_hash_algorithm => BcryptInvalid},
|
||||||
|
|
||||||
|
{ok, 400, _} = request(
|
||||||
|
post,
|
||||||
|
uri([?CONF_NS]),
|
||||||
|
ConfInvalid
|
||||||
|
),
|
||||||
|
|
||||||
|
{ok, 200, _} = request(
|
||||||
|
post,
|
||||||
|
uri([?CONF_NS]),
|
||||||
|
ConfValid
|
||||||
|
).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Helpers
|
%% Helpers
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
|
@ -70,6 +70,7 @@ init_per_testcase(TestCase, Config) when
|
||||||
{ok, _} = emqx:update_config([authorization, deny_action], disconnect),
|
{ok, _} = emqx:update_config([authorization, deny_action], disconnect),
|
||||||
Config;
|
Config;
|
||||||
init_per_testcase(_TestCase, Config) ->
|
init_per_testcase(_TestCase, Config) ->
|
||||||
|
_ = file:delete(emqx_authz_file:acl_conf_file()),
|
||||||
{ok, _} = emqx_authz:update(?CMD_REPLACE, []),
|
{ok, _} = emqx_authz:update(?CMD_REPLACE, []),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
|
|
|
@ -24,27 +24,30 @@
|
||||||
-export([
|
-export([
|
||||||
fields/1,
|
fields/1,
|
||||||
desc/1,
|
desc/1,
|
||||||
refs/0,
|
refs/1,
|
||||||
select_union_member/1
|
select_union_member/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
refs() ->
|
refs(api_write) ->
|
||||||
|
[?R_REF(builtin_db_api)];
|
||||||
|
refs(_) ->
|
||||||
[?R_REF(builtin_db)].
|
[?R_REF(builtin_db)].
|
||||||
|
|
||||||
select_union_member(#{
|
select_union_member(Kind, #{
|
||||||
<<"mechanism">> := ?AUTHN_MECHANISM_SIMPLE_BIN, <<"backend">> := ?AUTHN_BACKEND_BIN
|
<<"mechanism">> := ?AUTHN_MECHANISM_SIMPLE_BIN, <<"backend">> := ?AUTHN_BACKEND_BIN
|
||||||
}) ->
|
}) ->
|
||||||
refs();
|
refs(Kind);
|
||||||
select_union_member(_) ->
|
select_union_member(_Kind, _Value) ->
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
fields(builtin_db) ->
|
fields(builtin_db) ->
|
||||||
[
|
[
|
||||||
{mechanism, emqx_authn_schema:mechanism(?AUTHN_MECHANISM_SIMPLE)},
|
|
||||||
{backend, emqx_authn_schema:backend(?AUTHN_BACKEND)},
|
|
||||||
{user_id_type, fun user_id_type/1},
|
|
||||||
{password_hash_algorithm, fun emqx_authn_password_hashing:type_rw/1}
|
{password_hash_algorithm, fun emqx_authn_password_hashing:type_rw/1}
|
||||||
] ++ emqx_authn_schema:common_fields().
|
] ++ common_fields();
|
||||||
|
fields(builtin_db_api) ->
|
||||||
|
[
|
||||||
|
{password_hash_algorithm, fun emqx_authn_password_hashing:type_rw_api/1}
|
||||||
|
] ++ common_fields().
|
||||||
|
|
||||||
desc(builtin_db) ->
|
desc(builtin_db) ->
|
||||||
?DESC(builtin_db);
|
?DESC(builtin_db);
|
||||||
|
@ -56,3 +59,10 @@ user_id_type(desc) -> ?DESC(?FUNCTION_NAME);
|
||||||
user_id_type(default) -> <<"username">>;
|
user_id_type(default) -> <<"username">>;
|
||||||
user_id_type(required) -> true;
|
user_id_type(required) -> true;
|
||||||
user_id_type(_) -> undefined.
|
user_id_type(_) -> undefined.
|
||||||
|
|
||||||
|
common_fields() ->
|
||||||
|
[
|
||||||
|
{mechanism, emqx_authn_schema:mechanism(?AUTHN_MECHANISM_SIMPLE)},
|
||||||
|
{backend, emqx_authn_schema:backend(?AUTHN_BACKEND)},
|
||||||
|
{user_id_type, fun user_id_type/1}
|
||||||
|
] ++ emqx_authn_schema:common_fields().
|
||||||
|
|
|
@ -62,7 +62,8 @@
|
||||||
merge_lists/3,
|
merge_lists/3,
|
||||||
tcp_keepalive_opts/4,
|
tcp_keepalive_opts/4,
|
||||||
format/1,
|
format/1,
|
||||||
format_mfal/1
|
format_mfal/1,
|
||||||
|
call_first_defined/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
|
@ -554,6 +555,22 @@ format_mfal(Data) ->
|
||||||
undefined
|
undefined
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec call_first_defined(list({module(), atom(), list()})) -> term() | no_return().
|
||||||
|
call_first_defined([{Module, Function, Args} | Rest]) ->
|
||||||
|
try
|
||||||
|
apply(Module, Function, Args)
|
||||||
|
catch
|
||||||
|
error:undef:Stacktrace ->
|
||||||
|
case Stacktrace of
|
||||||
|
[{Module, Function, _, _} | _] ->
|
||||||
|
call_first_defined(Rest);
|
||||||
|
_ ->
|
||||||
|
erlang:raise(error, undef, Stacktrace)
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
call_first_defined([]) ->
|
||||||
|
error(none_fun_is_defined).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Internal Functions
|
%% Internal Functions
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fixed validation of Bcrypt salt rounds in authentification management through the API/Dashboard.
|
Loading…
Reference in New Issue