Merge pull request #7628 from JimMoen/refine-authn-authz-api-fields
refine authn/authz api fields
This commit is contained in:
commit
cc220694fd
|
@ -0,0 +1,20 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2022 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.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
%% config root name all auth providers have to agree on.
|
||||||
|
-define(EMQX_AUTHORIZATION_CONFIG_ROOT_NAME, "authorization").
|
||||||
|
-define(EMQX_AUTHORIZATION_CONFIG_ROOT_NAME_ATOM, authorization).
|
||||||
|
-define(EMQX_AUTHORIZATION_CONFIG_ROOT_NAME_BINARY, <<"authorization">>).
|
|
@ -24,6 +24,7 @@
|
||||||
-elvis([{elvis_style, invalid_dynamic_call, disable}]).
|
-elvis([{elvis_style, invalid_dynamic_call, disable}]).
|
||||||
|
|
||||||
-include("emqx_authentication.hrl").
|
-include("emqx_authentication.hrl").
|
||||||
|
-include("emqx_access_control.hrl").
|
||||||
-include_lib("typerefl/include/types.hrl").
|
-include_lib("typerefl/include/types.hrl").
|
||||||
|
|
||||||
-type duration() :: integer().
|
-type duration() :: integer().
|
||||||
|
@ -159,9 +160,9 @@ roots(high) ->
|
||||||
)},
|
)},
|
||||||
%% NOTE: authorization schema here is only to keep emqx app prue
|
%% NOTE: authorization schema here is only to keep emqx app prue
|
||||||
%% the full schema for EMQX node is injected in emqx_conf_schema.
|
%% the full schema for EMQX node is injected in emqx_conf_schema.
|
||||||
{"authorization",
|
{?EMQX_AUTHORIZATION_CONFIG_ROOT_NAME,
|
||||||
sc(
|
sc(
|
||||||
ref("authorization"),
|
ref(?EMQX_AUTHORIZATION_CONFIG_ROOT_NAME),
|
||||||
#{}
|
#{}
|
||||||
)}
|
)}
|
||||||
];
|
];
|
||||||
|
@ -315,6 +316,7 @@ fields("authorization") ->
|
||||||
hoconsc:enum([allow, deny]),
|
hoconsc:enum([allow, deny]),
|
||||||
#{
|
#{
|
||||||
default => allow,
|
default => allow,
|
||||||
|
required => true,
|
||||||
%% TODO: make sources a reference link
|
%% TODO: make sources a reference link
|
||||||
desc =>
|
desc =>
|
||||||
"Default access control action if the user or client matches no ACL rules,\n"
|
"Default access control action if the user or client matches no ACL rules,\n"
|
||||||
|
@ -328,6 +330,7 @@ fields("authorization") ->
|
||||||
hoconsc:enum([ignore, disconnect]),
|
hoconsc:enum([ignore, disconnect]),
|
||||||
#{
|
#{
|
||||||
default => ignore,
|
default => ignore,
|
||||||
|
required => true,
|
||||||
desc => "The action when the authorization check rejects an operation."
|
desc => "The action when the authorization check rejects an operation."
|
||||||
}
|
}
|
||||||
)},
|
)},
|
||||||
|
|
|
@ -29,14 +29,14 @@
|
||||||
|
|
||||||
providers() ->
|
providers() ->
|
||||||
[
|
[
|
||||||
{{'password_based', 'built_in_database'}, emqx_authn_mnesia},
|
{{password_based, built_in_database}, emqx_authn_mnesia},
|
||||||
{{'password_based', mysql}, emqx_authn_mysql},
|
{{password_based, mysql}, emqx_authn_mysql},
|
||||||
{{'password_based', postgresql}, emqx_authn_pgsql},
|
{{password_based, postgresql}, emqx_authn_pgsql},
|
||||||
{{'password_based', mongodb}, emqx_authn_mongodb},
|
{{password_based, mongodb}, emqx_authn_mongodb},
|
||||||
{{'password_based', redis}, emqx_authn_redis},
|
{{password_based, redis}, emqx_authn_redis},
|
||||||
{{'password_based', 'http'}, emqx_authn_http},
|
{{password_based, http}, emqx_authn_http},
|
||||||
{jwt, emqx_authn_jwt},
|
{jwt, emqx_authn_jwt},
|
||||||
{{scram, 'built_in_database'}, emqx_enhanced_authn_scram_mnesia}
|
{{scram, built_in_database}, emqx_enhanced_authn_scram_mnesia}
|
||||||
].
|
].
|
||||||
|
|
||||||
check_configs(C) when is_map(C) ->
|
check_configs(C) when is_map(C) ->
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
-include_lib("emqx/include/emqx_authentication.hrl").
|
-include_lib("emqx/include/emqx_authentication.hrl").
|
||||||
|
|
||||||
-import(hoconsc, [mk/2, ref/1]).
|
-import(hoconsc, [mk/2, ref/1, ref/2]).
|
||||||
-import(emqx_dashboard_swagger, [error_codes/2]).
|
-import(emqx_dashboard_swagger, [error_codes/2]).
|
||||||
|
|
||||||
-define(BAD_REQUEST, 'BAD_REQUEST').
|
-define(BAD_REQUEST, 'BAD_REQUEST').
|
||||||
|
@ -128,31 +128,26 @@ roots() ->
|
||||||
|
|
||||||
fields(request_user_create) ->
|
fields(request_user_create) ->
|
||||||
[
|
[
|
||||||
{user_id, binary()}
|
{user_id, mk(binary(), #{required => true})}
|
||||||
| fields(request_user_update)
|
| fields(request_user_update)
|
||||||
];
|
];
|
||||||
fields(request_user_update) ->
|
fields(request_user_update) ->
|
||||||
[
|
[
|
||||||
{password, binary()},
|
{password, mk(binary(), #{required => true})},
|
||||||
{is_superuser, mk(boolean(), #{default => false, required => false})}
|
{is_superuser, mk(boolean(), #{default => false, required => false})}
|
||||||
];
|
];
|
||||||
fields(request_move) ->
|
fields(request_move) ->
|
||||||
[{position, binary()}];
|
[{position, mk(binary(), #{required => true})}];
|
||||||
fields(request_import_users) ->
|
fields(request_import_users) ->
|
||||||
[{filename, binary()}];
|
%% TODO: add file update
|
||||||
|
[{filename, mk(binary(), #{required => true})}];
|
||||||
fields(response_user) ->
|
fields(response_user) ->
|
||||||
[
|
[
|
||||||
{user_id, binary()},
|
{user_id, mk(binary(), #{required => true})},
|
||||||
{is_superuser, mk(boolean(), #{default => false, required => false})}
|
{is_superuser, mk(boolean(), #{default => false, required => false})}
|
||||||
];
|
];
|
||||||
fields(response_users) ->
|
fields(response_users) ->
|
||||||
paginated_list_type(ref(response_user));
|
paginated_list_type(ref(response_user)).
|
||||||
fields(pagination_meta) ->
|
|
||||||
[
|
|
||||||
{page, pos_integer()},
|
|
||||||
{limit, pos_integer()},
|
|
||||||
{count, non_neg_integer()}
|
|
||||||
].
|
|
||||||
|
|
||||||
schema("/authentication") ->
|
schema("/authentication") ->
|
||||||
#{
|
#{
|
||||||
|
@ -431,10 +426,8 @@ schema("/authentication/:id/users") ->
|
||||||
description => <<"List users in authenticator in global authentication chain">>,
|
description => <<"List users in authenticator in global authentication chain">>,
|
||||||
parameters => [
|
parameters => [
|
||||||
param_auth_id(),
|
param_auth_id(),
|
||||||
{page,
|
ref(emqx_dashboard_swagger, page),
|
||||||
mk(pos_integer(), #{in => query, desc => <<"Page Index">>, required => false})},
|
ref(emqx_dashboard_swagger, limit),
|
||||||
{limit,
|
|
||||||
mk(pos_integer(), #{in => query, desc => <<"Page Limit">>, required => false})},
|
|
||||||
{like_username,
|
{like_username,
|
||||||
mk(binary(), #{
|
mk(binary(), #{
|
||||||
in => query,
|
in => query,
|
||||||
|
@ -483,10 +476,8 @@ schema("/listeners/:listener_id/authentication/:id/users") ->
|
||||||
parameters => [
|
parameters => [
|
||||||
param_listener_id(),
|
param_listener_id(),
|
||||||
param_auth_id(),
|
param_auth_id(),
|
||||||
{page,
|
ref(emqx_dashboard_swagger, page),
|
||||||
mk(pos_integer(), #{in => query, desc => <<"Page Index">>, required => false})},
|
ref(emqx_dashboard_swagger, limit)
|
||||||
{limit,
|
|
||||||
mk(pos_integer(), #{in => query, desc => <<"Page Limit">>, required => false})}
|
|
||||||
],
|
],
|
||||||
responses => #{
|
responses => #{
|
||||||
200 => emqx_dashboard_swagger:schema_with_example(
|
200 => emqx_dashboard_swagger:schema_with_example(
|
||||||
|
@ -587,7 +578,8 @@ param_auth_id() ->
|
||||||
id,
|
id,
|
||||||
mk(binary(), #{
|
mk(binary(), #{
|
||||||
in => path,
|
in => path,
|
||||||
desc => <<"Authenticator ID">>
|
desc => <<"Authenticator ID">>,
|
||||||
|
required => true
|
||||||
})
|
})
|
||||||
}.
|
}.
|
||||||
|
|
||||||
|
@ -597,6 +589,7 @@ param_listener_id() ->
|
||||||
mk(binary(), #{
|
mk(binary(), #{
|
||||||
in => path,
|
in => path,
|
||||||
desc => <<"Listener ID">>,
|
desc => <<"Listener ID">>,
|
||||||
|
required => true,
|
||||||
example => emqx_listeners:id_example()
|
example => emqx_listeners:id_example()
|
||||||
})
|
})
|
||||||
}.
|
}.
|
||||||
|
@ -1182,7 +1175,7 @@ update_config(Path, ConfigRequest) ->
|
||||||
|
|
||||||
get_raw_config_with_defaults(ConfKeyPath) ->
|
get_raw_config_with_defaults(ConfKeyPath) ->
|
||||||
NConfKeyPath = [atom_to_binary(Key, utf8) || Key <- ConfKeyPath],
|
NConfKeyPath = [atom_to_binary(Key, utf8) || Key <- ConfKeyPath],
|
||||||
RawConfig = emqx_map_lib:deep_get(NConfKeyPath, emqx_config:get_raw([]), []),
|
RawConfig = emqx:get_raw_config(NConfKeyPath, []),
|
||||||
ensure_list(fill_defaults(RawConfig)).
|
ensure_list(fill_defaults(RawConfig)).
|
||||||
|
|
||||||
find_config(AuthenticatorID, AuthenticatorsConfig) ->
|
find_config(AuthenticatorID, AuthenticatorsConfig) ->
|
||||||
|
@ -1200,7 +1193,24 @@ find_config(AuthenticatorID, AuthenticatorsConfig) ->
|
||||||
fill_defaults(Configs) when is_list(Configs) ->
|
fill_defaults(Configs) when is_list(Configs) ->
|
||||||
lists:map(fun fill_defaults/1, Configs);
|
lists:map(fun fill_defaults/1, Configs);
|
||||||
fill_defaults(Config) ->
|
fill_defaults(Config) ->
|
||||||
emqx_authn:check_config(Config, #{only_fill_defaults => true}).
|
emqx_authn:check_config(merge_default_headers(Config), #{only_fill_defaults => true}).
|
||||||
|
|
||||||
|
merge_default_headers(Config) ->
|
||||||
|
case maps:find(<<"headers">>, Config) of
|
||||||
|
{ok, Headers} ->
|
||||||
|
NewHeaders =
|
||||||
|
case Config of
|
||||||
|
#{<<"method">> := <<"get">>} ->
|
||||||
|
(emqx_authn_http:headers_no_content_type(converter))(Headers);
|
||||||
|
#{<<"method">> := <<"post">>} ->
|
||||||
|
(emqx_authn_http:headers(converter))(Headers);
|
||||||
|
_ ->
|
||||||
|
Headers
|
||||||
|
end,
|
||||||
|
Config#{<<"headers">> => NewHeaders};
|
||||||
|
error ->
|
||||||
|
Config
|
||||||
|
end.
|
||||||
|
|
||||||
convert_certs(#{ssl := SSL} = Config) when SSL =/= undefined ->
|
convert_certs(#{ssl := SSL} = Config) when SSL =/= undefined ->
|
||||||
Config#{ssl := emqx_tls_lib:drop_invalid_certs(SSL)};
|
Config#{ssl := emqx_tls_lib:drop_invalid_certs(SSL)};
|
||||||
|
@ -1316,7 +1326,7 @@ binfmt(Fmt, Args) -> iolist_to_binary(io_lib:format(Fmt, Args)).
|
||||||
paginated_list_type(Type) ->
|
paginated_list_type(Type) ->
|
||||||
[
|
[
|
||||||
{data, hoconsc:array(Type)},
|
{data, hoconsc:array(Type)},
|
||||||
{meta, ref(pagination_meta)}
|
{meta, ref(emqx_dashboard_swagger, meta)}
|
||||||
].
|
].
|
||||||
|
|
||||||
authenticator_array_example() ->
|
authenticator_array_example() ->
|
||||||
|
|
|
@ -68,21 +68,31 @@ roots() -> [pbkdf2, bcrypt, bcrypt_rw, other_algorithms].
|
||||||
|
|
||||||
fields(bcrypt_rw) ->
|
fields(bcrypt_rw) ->
|
||||||
fields(bcrypt) ++
|
fields(bcrypt) ++
|
||||||
[{salt_rounds, fun salt_rounds/1}];
|
[
|
||||||
|
{salt_rounds,
|
||||||
|
sc(
|
||||||
|
integer(),
|
||||||
|
#{
|
||||||
|
default => 10,
|
||||||
|
example => 10,
|
||||||
|
desc => "Salt rounds for BCRYPT password generation."
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
];
|
||||||
fields(bcrypt) ->
|
fields(bcrypt) ->
|
||||||
[{name, sc(bcrypt, #{desc => "BCRYPT password hashing."})}];
|
[{name, sc(bcrypt, #{required => true, desc => "BCRYPT password hashing."})}];
|
||||||
fields(pbkdf2) ->
|
fields(pbkdf2) ->
|
||||||
[
|
[
|
||||||
{name, sc(pbkdf2, #{desc => "PBKDF2 password hashing."})},
|
{name, sc(pbkdf2, #{required => true, desc => "PBKDF2 password hashing."})},
|
||||||
{mac_fun,
|
{mac_fun,
|
||||||
sc(
|
sc(
|
||||||
hoconsc:enum([md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512]),
|
hoconsc:enum([md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512]),
|
||||||
#{desc => "Specifies mac_fun for PBKDF2 hashing algorithm."}
|
#{required => true, desc => "Specifies mac_fun for PBKDF2 hashing algorithm."}
|
||||||
)},
|
)},
|
||||||
{iterations,
|
{iterations,
|
||||||
sc(
|
sc(
|
||||||
integer(),
|
integer(),
|
||||||
#{desc => "Iteration count for PBKDF2 hashing algorithm."}
|
#{required => true, desc => "Iteration count for PBKDF2 hashing algorithm."}
|
||||||
)},
|
)},
|
||||||
{dk_length, fun dk_length/1}
|
{dk_length, fun dk_length/1}
|
||||||
];
|
];
|
||||||
|
@ -91,10 +101,7 @@ fields(other_algorithms) ->
|
||||||
{name,
|
{name,
|
||||||
sc(
|
sc(
|
||||||
hoconsc:enum([plain, md5, sha, sha256, sha512]),
|
hoconsc:enum([plain, md5, sha, sha256, sha512]),
|
||||||
#{
|
#{required => true, desc => "Simple password hashing algorithm."}
|
||||||
desc =>
|
|
||||||
"Simple password hashing algorithm."
|
|
||||||
}
|
|
||||||
)},
|
)},
|
||||||
{salt_position, fun salt_position/1}
|
{salt_position, fun salt_position/1}
|
||||||
].
|
].
|
||||||
|
@ -115,11 +122,6 @@ salt_position(default) -> prefix;
|
||||||
salt_position(desc) -> "Salt position for PLAIN, MD5, SHA, SHA256 and SHA512 algorithms.";
|
salt_position(desc) -> "Salt position for PLAIN, MD5, SHA, SHA256 and SHA512 algorithms.";
|
||||||
salt_position(_) -> undefined.
|
salt_position(_) -> undefined.
|
||||||
|
|
||||||
salt_rounds(type) -> integer();
|
|
||||||
salt_rounds(default) -> 10;
|
|
||||||
salt_rounds(desc) -> "Salt rounds for BCRYPT password generation.";
|
|
||||||
salt_rounds(_) -> undefined.
|
|
||||||
|
|
||||||
dk_length(type) ->
|
dk_length(type) ->
|
||||||
integer();
|
integer();
|
||||||
dk_length(required) ->
|
dk_length(required) ->
|
||||||
|
@ -130,6 +132,7 @@ dk_length(desc) ->
|
||||||
dk_length(_) ->
|
dk_length(_) ->
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
|
%% for simple_authn/emqx_authn_mnesia
|
||||||
type_rw(type) ->
|
type_rw(type) ->
|
||||||
hoconsc:union(rw_refs());
|
hoconsc:union(rw_refs());
|
||||||
type_rw(default) ->
|
type_rw(default) ->
|
||||||
|
@ -139,6 +142,7 @@ type_rw(desc) ->
|
||||||
type_rw(_) ->
|
type_rw(_) ->
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
|
%% for other authn resources
|
||||||
type_ro(type) ->
|
type_ro(type) ->
|
||||||
hoconsc:union(ro_refs());
|
hoconsc:union(ro_refs());
|
||||||
type_ro(default) ->
|
type_ro(default) ->
|
||||||
|
|
|
@ -32,6 +32,11 @@
|
||||||
validations/0
|
validations/0
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
-export([
|
||||||
|
headers_no_content_type/1,
|
||||||
|
headers/1
|
||||||
|
]).
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
refs/0,
|
refs/0,
|
||||||
create/2,
|
create/2,
|
||||||
|
@ -57,12 +62,12 @@ roots() ->
|
||||||
|
|
||||||
fields(get) ->
|
fields(get) ->
|
||||||
[
|
[
|
||||||
{method, #{type => get, default => post, desc => "HTTP method."}},
|
{method, #{type => get, required => true, default => post, desc => "HTTP method."}},
|
||||||
{headers, fun headers_no_content_type/1}
|
{headers, fun headers_no_content_type/1}
|
||||||
] ++ common_fields();
|
] ++ common_fields();
|
||||||
fields(post) ->
|
fields(post) ->
|
||||||
[
|
[
|
||||||
{method, #{type => post, default => post, desc => "HTTP method."}},
|
{method, #{type => post, required => true, default => post, desc => "HTTP method."}},
|
||||||
{headers, fun headers/1}
|
{headers, fun headers/1}
|
||||||
] ++ common_fields().
|
] ++ common_fields().
|
||||||
|
|
||||||
|
@ -75,7 +80,7 @@ desc(_) ->
|
||||||
|
|
||||||
common_fields() ->
|
common_fields() ->
|
||||||
[
|
[
|
||||||
{mechanism, emqx_authn_schema:mechanism('password_based')},
|
{mechanism, emqx_authn_schema:mechanism(password_based)},
|
||||||
{backend, emqx_authn_schema:backend(http)},
|
{backend, emqx_authn_schema:backend(http)},
|
||||||
{url, fun url/1},
|
{url, fun url/1},
|
||||||
{body,
|
{body,
|
||||||
|
|
|
@ -55,20 +55,22 @@ roots() ->
|
||||||
|
|
||||||
fields('hmac-based') ->
|
fields('hmac-based') ->
|
||||||
[
|
[
|
||||||
{use_jwks, sc(hoconsc:enum([false]), #{desc => ""})},
|
{use_jwks, sc(hoconsc:enum([false]), #{required => true, desc => ""})},
|
||||||
{algorithm, sc(hoconsc:enum(['hmac-based']), #{desc => "Signing algorithm."})},
|
{algorithm,
|
||||||
|
sc(hoconsc:enum(['hmac-based']), #{required => true, desc => "Signing algorithm."})},
|
||||||
{secret, fun secret/1},
|
{secret, fun secret/1},
|
||||||
{secret_base64_encoded, fun secret_base64_encoded/1}
|
{secret_base64_encoded, fun secret_base64_encoded/1}
|
||||||
] ++ common_fields();
|
] ++ common_fields();
|
||||||
fields('public-key') ->
|
fields('public-key') ->
|
||||||
[
|
[
|
||||||
{use_jwks, sc(hoconsc:enum([false]), #{desc => ""})},
|
{use_jwks, sc(hoconsc:enum([false]), #{required => true, desc => ""})},
|
||||||
{algorithm, sc(hoconsc:enum(['public-key']), #{desc => "Signing algorithm."})},
|
{algorithm,
|
||||||
|
sc(hoconsc:enum(['public-key']), #{required => true, desc => "Signing algorithm."})},
|
||||||
{certificate, fun certificate/1}
|
{certificate, fun certificate/1}
|
||||||
] ++ common_fields();
|
] ++ common_fields();
|
||||||
fields('jwks') ->
|
fields('jwks') ->
|
||||||
[
|
[
|
||||||
{use_jwks, sc(hoconsc:enum([true]), #{desc => ""})},
|
{use_jwks, sc(hoconsc:enum([true]), #{required => true, desc => ""})},
|
||||||
{endpoint, fun endpoint/1},
|
{endpoint, fun endpoint/1},
|
||||||
{pool_size, fun pool_size/1},
|
{pool_size, fun pool_size/1},
|
||||||
{refresh_interval, fun refresh_interval/1},
|
{refresh_interval, fun refresh_interval/1},
|
||||||
|
@ -78,7 +80,8 @@ fields('jwks') ->
|
||||||
hoconsc:ref(?MODULE, ssl_disable)
|
hoconsc:ref(?MODULE, ssl_disable)
|
||||||
]),
|
]),
|
||||||
desc => "Enable/disable SSL.",
|
desc => "Enable/disable SSL.",
|
||||||
default => #{<<"enable">> => false}
|
default => #{<<"enable">> => false},
|
||||||
|
required => false
|
||||||
}}
|
}}
|
||||||
] ++ common_fields();
|
] ++ common_fields();
|
||||||
fields(ssl_enable) ->
|
fields(ssl_enable) ->
|
||||||
|
@ -114,6 +117,7 @@ common_fields() ->
|
||||||
|
|
||||||
secret(type) -> binary();
|
secret(type) -> binary();
|
||||||
secret(desc) -> "The key to verify the JWT Token using HMAC algorithm.";
|
secret(desc) -> "The key to verify the JWT Token using HMAC algorithm.";
|
||||||
|
secret(required) -> true;
|
||||||
secret(_) -> undefined.
|
secret(_) -> undefined.
|
||||||
|
|
||||||
secret_base64_encoded(type) -> boolean();
|
secret_base64_encoded(type) -> boolean();
|
||||||
|
@ -123,10 +127,12 @@ secret_base64_encoded(_) -> undefined.
|
||||||
|
|
||||||
certificate(type) -> string();
|
certificate(type) -> string();
|
||||||
certificate(desc) -> "The certificate used for signing the token.";
|
certificate(desc) -> "The certificate used for signing the token.";
|
||||||
|
certificate(required) -> ture;
|
||||||
certificate(_) -> undefined.
|
certificate(_) -> undefined.
|
||||||
|
|
||||||
endpoint(type) -> string();
|
endpoint(type) -> string();
|
||||||
endpoint(desc) -> "JWKs endpoint.";
|
endpoint(desc) -> "JWKs endpoint.";
|
||||||
|
endpoint(required) -> true;
|
||||||
endpoint(_) -> undefined.
|
endpoint(_) -> undefined.
|
||||||
|
|
||||||
refresh_interval(type) -> integer();
|
refresh_interval(type) -> integer();
|
||||||
|
@ -168,6 +174,8 @@ verify_claims(converter) ->
|
||||||
fun(VerifyClaims) ->
|
fun(VerifyClaims) ->
|
||||||
[{to_binary(K), V} || {K, V} <- maps:to_list(VerifyClaims)]
|
[{to_binary(K), V} || {K, V} <- maps:to_list(VerifyClaims)]
|
||||||
end;
|
end;
|
||||||
|
verify_claims(required) ->
|
||||||
|
false;
|
||||||
verify_claims(_) ->
|
verify_claims(_) ->
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
|
|
|
@ -103,8 +103,8 @@ roots() -> [?CONF_NS].
|
||||||
|
|
||||||
fields(?CONF_NS) ->
|
fields(?CONF_NS) ->
|
||||||
[
|
[
|
||||||
{mechanism, emqx_authn_schema:mechanism('password_based')},
|
{mechanism, emqx_authn_schema:mechanism(password_based)},
|
||||||
{backend, emqx_authn_schema:backend('built_in_database')},
|
{backend, emqx_authn_schema:backend(built_in_database)},
|
||||||
{user_id_type, fun user_id_type/1},
|
{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().
|
] ++ emqx_authn_schema:common_fields().
|
||||||
|
@ -117,6 +117,7 @@ desc(_) ->
|
||||||
user_id_type(type) -> user_id_type();
|
user_id_type(type) -> user_id_type();
|
||||||
user_id_type(desc) -> "Authenticate by client ID or username.";
|
user_id_type(desc) -> "Authenticate by client ID or username.";
|
||||||
user_id_type(default) -> <<"username">>;
|
user_id_type(default) -> <<"username">>;
|
||||||
|
user_id_type(required) -> true;
|
||||||
user_id_type(_) -> undefined.
|
user_id_type(_) -> undefined.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
|
@ -83,6 +83,7 @@ common_fields() ->
|
||||||
|
|
||||||
collection(type) -> binary();
|
collection(type) -> binary();
|
||||||
collection(desc) -> "Collection used to store authentication data.";
|
collection(desc) -> "Collection used to store authentication data.";
|
||||||
|
collection(required) -> true;
|
||||||
collection(_) -> undefined.
|
collection(_) -> undefined.
|
||||||
|
|
||||||
selector(type) ->
|
selector(type) ->
|
||||||
|
@ -97,6 +98,7 @@ selector(_) ->
|
||||||
|
|
||||||
password_hash_field(type) -> binary();
|
password_hash_field(type) -> binary();
|
||||||
password_hash_field(desc) -> "Document field that contains password hash.";
|
password_hash_field(desc) -> "Document field that contains password hash.";
|
||||||
|
password_hash_field(required) -> false;
|
||||||
password_hash_field(_) -> undefined.
|
password_hash_field(_) -> undefined.
|
||||||
|
|
||||||
salt_field(type) -> binary();
|
salt_field(type) -> binary();
|
||||||
|
|
|
@ -48,7 +48,7 @@ roots() -> [?CONF_NS].
|
||||||
|
|
||||||
fields(?CONF_NS) ->
|
fields(?CONF_NS) ->
|
||||||
[
|
[
|
||||||
{mechanism, emqx_authn_schema:mechanism('password_based')},
|
{mechanism, emqx_authn_schema:mechanism(password_based)},
|
||||||
{backend, emqx_authn_schema:backend(mysql)},
|
{backend, emqx_authn_schema:backend(mysql)},
|
||||||
{password_hash_algorithm, fun emqx_authn_password_hashing:type_ro/1},
|
{password_hash_algorithm, fun emqx_authn_password_hashing:type_ro/1},
|
||||||
{query, fun query/1},
|
{query, fun query/1},
|
||||||
|
@ -63,6 +63,7 @@ desc(_) ->
|
||||||
|
|
||||||
query(type) -> string();
|
query(type) -> string();
|
||||||
query(desc) -> "SQL query used to lookup client data.";
|
query(desc) -> "SQL query used to lookup client data.";
|
||||||
|
query(required) -> true;
|
||||||
query(_) -> undefined.
|
query(_) -> undefined.
|
||||||
|
|
||||||
query_timeout(type) -> emqx_schema:duration_ms();
|
query_timeout(type) -> emqx_schema:duration_ms();
|
||||||
|
|
|
@ -54,7 +54,7 @@ roots() -> [?CONF_NS].
|
||||||
|
|
||||||
fields(?CONF_NS) ->
|
fields(?CONF_NS) ->
|
||||||
[
|
[
|
||||||
{mechanism, emqx_authn_schema:mechanism('password_based')},
|
{mechanism, emqx_authn_schema:mechanism(password_based)},
|
||||||
{backend, emqx_authn_schema:backend(postgresql)},
|
{backend, emqx_authn_schema:backend(postgresql)},
|
||||||
{password_hash_algorithm, fun emqx_authn_password_hashing:type_ro/1},
|
{password_hash_algorithm, fun emqx_authn_password_hashing:type_ro/1},
|
||||||
{query, fun query/1}
|
{query, fun query/1}
|
||||||
|
@ -69,6 +69,7 @@ desc(_) ->
|
||||||
|
|
||||||
query(type) -> string();
|
query(type) -> string();
|
||||||
query(desc) -> "`SQL` query for looking up authentication data.";
|
query(desc) -> "`SQL` query for looking up authentication data.";
|
||||||
|
query(required) -> true;
|
||||||
query(_) -> undefined.
|
query(_) -> undefined.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
|
@ -79,6 +79,7 @@ common_fields() ->
|
||||||
|
|
||||||
cmd(type) -> string();
|
cmd(type) -> string();
|
||||||
cmd(desc) -> "Redis query.";
|
cmd(desc) -> "Redis query.";
|
||||||
|
cmd(required) -> true;
|
||||||
cmd(_) -> undefined.
|
cmd(_) -> undefined.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
%% limitations under the License.
|
%% limitations under the License.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-include_lib("emqx/include/emqx_access_control.hrl").
|
||||||
|
|
||||||
-define(APP, emqx_authz).
|
-define(APP, emqx_authz).
|
||||||
|
|
||||||
-define(ALLOW_DENY(A),
|
-define(ALLOW_DENY(A),
|
||||||
|
@ -45,6 +47,11 @@
|
||||||
|
|
||||||
-define(RE_PLACEHOLDER, "\\$\\{[a-z0-9_]+\\}").
|
-define(RE_PLACEHOLDER, "\\$\\{[a-z0-9_]+\\}").
|
||||||
|
|
||||||
|
%% has to be the same as the root field name defined in emqx_schema
|
||||||
|
-define(CONF_NS, ?EMQX_AUTHORIZATION_CONFIG_ROOT_NAME).
|
||||||
|
-define(CONF_NS_ATOM, ?EMQX_AUTHORIZATION_CONFIG_ROOT_NAME_ATOM).
|
||||||
|
-define(CONF_NS_BINARY, ?EMQX_AUTHORIZATION_CONFIG_ROOT_NAME_BINARY).
|
||||||
|
|
||||||
%% API examples
|
%% API examples
|
||||||
-define(USERNAME_RULES_EXAMPLE, #{
|
-define(USERNAME_RULES_EXAMPLE, #{
|
||||||
username => user1,
|
username => user1,
|
||||||
|
|
|
@ -383,7 +383,7 @@ fields(rules_for_username) ->
|
||||||
fields(username_response_data) ->
|
fields(username_response_data) ->
|
||||||
[
|
[
|
||||||
{data, mk(array(ref(rules_for_username)), #{})},
|
{data, mk(array(ref(rules_for_username)), #{})},
|
||||||
{meta, ref(meta)}
|
{meta, ref(emqx_dashboard_swagger, meta)}
|
||||||
];
|
];
|
||||||
fields(rules_for_clientid) ->
|
fields(rules_for_clientid) ->
|
||||||
fields(rules) ++
|
fields(rules) ++
|
||||||
|
@ -391,14 +391,10 @@ fields(rules_for_clientid) ->
|
||||||
fields(clientid_response_data) ->
|
fields(clientid_response_data) ->
|
||||||
[
|
[
|
||||||
{data, mk(array(ref(rules_for_clientid)), #{})},
|
{data, mk(array(ref(rules_for_clientid)), #{})},
|
||||||
{meta, ref(meta)}
|
{meta, ref(emqx_dashboard_swagger, meta)}
|
||||||
];
|
];
|
||||||
fields(rules) ->
|
fields(rules) ->
|
||||||
[{rules, mk(array(ref(rule_item)))}];
|
[{rules, mk(array(ref(rule_item)))}].
|
||||||
fields(meta) ->
|
|
||||||
emqx_dashboard_swagger:fields(page) ++
|
|
||||||
emqx_dashboard_swagger:fields(limit) ++
|
|
||||||
[{count, mk(integer(), #{example => 1})}].
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% HTTP API
|
%% HTTP API
|
||||||
|
|
|
@ -16,37 +16,34 @@
|
||||||
|
|
||||||
-module(emqx_authz_api_schema).
|
-module(emqx_authz_api_schema).
|
||||||
|
|
||||||
|
-include("emqx_authz.hrl").
|
||||||
-include_lib("typerefl/include/types.hrl").
|
-include_lib("typerefl/include/types.hrl").
|
||||||
-include_lib("emqx_connector/include/emqx_connector.hrl").
|
-include_lib("emqx_connector/include/emqx_connector.hrl").
|
||||||
|
|
||||||
-import(hoconsc, [mk/2, enum/1]).
|
-import(hoconsc, [mk/2, enum/1]).
|
||||||
-import(emqx_schema, [mk_duration/2]).
|
-import(emqx_schema, [mk_duration/2]).
|
||||||
|
|
||||||
-export([fields/1, authz_sources_types/1]).
|
-export([
|
||||||
|
fields/1,
|
||||||
|
authz_sources_types/1
|
||||||
|
]).
|
||||||
|
|
||||||
fields(http) ->
|
%%------------------------------------------------------------------------------
|
||||||
authz_common_fields(http) ++
|
%% Hocon Schema
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
fields(http_get) ->
|
||||||
[
|
[
|
||||||
{url, fun url/1},
|
{method, #{type => get, default => get, required => true}},
|
||||||
{method, #{
|
{headers, fun headers_no_content_type/1}
|
||||||
type => enum([get, post]),
|
] ++ authz_http_common_fields();
|
||||||
default => get
|
fields(http_post) ->
|
||||||
}},
|
|
||||||
{headers, fun headers/1},
|
|
||||||
{body, map([{fuzzy, term(), binary()}])},
|
|
||||||
{request_timeout, mk_duration("Request timeout", #{default => "30s"})}
|
|
||||||
] ++
|
|
||||||
maps:to_list(
|
|
||||||
maps:without(
|
|
||||||
[
|
[
|
||||||
base_url,
|
{method, #{type => post, default => post, required => true}},
|
||||||
pool_type
|
{headers, fun headers/1}
|
||||||
],
|
] ++ authz_http_common_fields();
|
||||||
maps:from_list(emqx_connector_http:fields(config))
|
fields(built_in_database) ->
|
||||||
)
|
authz_common_fields(built_in_database);
|
||||||
);
|
|
||||||
fields('built_in_database') ->
|
|
||||||
authz_common_fields('built_in_database');
|
|
||||||
fields(mongo_single) ->
|
fields(mongo_single) ->
|
||||||
authz_mongo_common_fields() ++
|
authz_mongo_common_fields() ++
|
||||||
emqx_connector_mongo:fields(single);
|
emqx_connector_mongo:fields(single);
|
||||||
|
@ -58,11 +55,11 @@ fields(mongo_sharded) ->
|
||||||
emqx_connector_mongo:fields(sharded);
|
emqx_connector_mongo:fields(sharded);
|
||||||
fields(mysql) ->
|
fields(mysql) ->
|
||||||
authz_common_fields(mysql) ++
|
authz_common_fields(mysql) ++
|
||||||
[{query, #{type => binary()}}] ++
|
[{query, mk(binary(), #{required => true})}] ++
|
||||||
emqx_connector_mysql:fields(config);
|
emqx_connector_mysql:fields(config);
|
||||||
fields(postgresql) ->
|
fields(postgresql) ->
|
||||||
authz_common_fields(postgresql) ++
|
authz_common_fields(postgresql) ++
|
||||||
[{query, #{type => binary()}}] ++
|
[{query, mk(binary(), #{required => true})}] ++
|
||||||
proplists:delete(named_queries, emqx_connector_pgsql:fields(config));
|
proplists:delete(named_queries, emqx_connector_pgsql:fields(config));
|
||||||
fields(redis_single) ->
|
fields(redis_single) ->
|
||||||
authz_redis_common_fields() ++
|
authz_redis_common_fields() ++
|
||||||
|
@ -100,6 +97,23 @@ fields(position) ->
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% http type funcs
|
%% http type funcs
|
||||||
|
|
||||||
|
authz_http_common_fields() ->
|
||||||
|
authz_common_fields(http) ++
|
||||||
|
[
|
||||||
|
{url, fun url/1},
|
||||||
|
{body, map([{fuzzy, term(), binary()}])},
|
||||||
|
{request_timeout, mk_duration("Request timeout", #{default => "30s"})}
|
||||||
|
] ++
|
||||||
|
maps:to_list(
|
||||||
|
maps:without(
|
||||||
|
[
|
||||||
|
base_url,
|
||||||
|
pool_type
|
||||||
|
],
|
||||||
|
maps:from_list(emqx_connector_http:fields(config))
|
||||||
|
)
|
||||||
|
).
|
||||||
|
|
||||||
url(type) -> binary();
|
url(type) -> binary();
|
||||||
url(validator) -> [?NOT_EMPTY("the value of the field 'url' cannot be empty")];
|
url(validator) -> [?NOT_EMPTY("the value of the field 'url' cannot be empty")];
|
||||||
url(required) -> true;
|
url(required) -> true;
|
||||||
|
@ -107,6 +121,8 @@ url(_) -> undefined.
|
||||||
|
|
||||||
headers(type) ->
|
headers(type) ->
|
||||||
map();
|
map();
|
||||||
|
headers(desc) ->
|
||||||
|
"List of HTTP headers.";
|
||||||
headers(converter) ->
|
headers(converter) ->
|
||||||
fun(Headers) ->
|
fun(Headers) ->
|
||||||
maps:merge(default_headers(), transform_header_name(Headers))
|
maps:merge(default_headers(), transform_header_name(Headers))
|
||||||
|
@ -116,6 +132,19 @@ headers(default) ->
|
||||||
headers(_) ->
|
headers(_) ->
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
|
headers_no_content_type(type) ->
|
||||||
|
map();
|
||||||
|
headers_no_content_type(desc) ->
|
||||||
|
"List of HTTP headers.";
|
||||||
|
headers_no_content_type(converter) ->
|
||||||
|
fun(Headers) ->
|
||||||
|
maps:merge(default_headers_no_content_type(), transform_header_name(Headers))
|
||||||
|
end;
|
||||||
|
headers_no_content_type(default) ->
|
||||||
|
default_headers_no_content_type();
|
||||||
|
headers_no_content_type(_) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
%% headers
|
%% headers
|
||||||
default_headers() ->
|
default_headers() ->
|
||||||
maps:put(
|
maps:put(
|
||||||
|
@ -153,10 +182,19 @@ authz_mongo_common_fields() ->
|
||||||
].
|
].
|
||||||
|
|
||||||
collection(type) -> binary();
|
collection(type) -> binary();
|
||||||
|
collection(desc) -> "Collection used to store authentication data.";
|
||||||
|
collection(required) -> true;
|
||||||
collection(_) -> undefined.
|
collection(_) -> undefined.
|
||||||
|
|
||||||
selector(type) -> map();
|
selector(type) ->
|
||||||
selector(_) -> undefined.
|
map();
|
||||||
|
selector(desc) ->
|
||||||
|
"Statement that is executed during the authentication process. "
|
||||||
|
"Commands can support following wildcards:\n"
|
||||||
|
" - `${username}`: substituted with client's username\n"
|
||||||
|
" - `${clientid}`: substituted with the clientid";
|
||||||
|
selector(_) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Redis type funcs
|
%% Redis type funcs
|
||||||
|
@ -164,10 +202,11 @@ selector(_) -> undefined.
|
||||||
authz_redis_common_fields() ->
|
authz_redis_common_fields() ->
|
||||||
authz_common_fields(redis) ++
|
authz_common_fields(redis) ++
|
||||||
[
|
[
|
||||||
{cmd, #{
|
{cmd,
|
||||||
type => binary(),
|
mk(binary(), #{
|
||||||
|
required => true,
|
||||||
example => <<"HGETALL mqtt_authz">>
|
example => <<"HGETALL mqtt_authz">>
|
||||||
}}
|
})}
|
||||||
].
|
].
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
@ -179,6 +218,7 @@ authz_common_fields(Type) when is_atom(Type) ->
|
||||||
{type, #{
|
{type, #{
|
||||||
type => enum([Type]),
|
type => enum([Type]),
|
||||||
default => Type,
|
default => Type,
|
||||||
|
required => true,
|
||||||
in => body
|
in => body
|
||||||
}}
|
}}
|
||||||
].
|
].
|
||||||
|
@ -194,9 +234,11 @@ enable(_) -> undefined.
|
||||||
authz_sources_types(Type) ->
|
authz_sources_types(Type) ->
|
||||||
case Type of
|
case Type of
|
||||||
simple ->
|
simple ->
|
||||||
[mongodb, redis];
|
[http, mongodb, redis];
|
||||||
detailed ->
|
detailed ->
|
||||||
[
|
[
|
||||||
|
http_get,
|
||||||
|
http_post,
|
||||||
mongo_single,
|
mongo_single,
|
||||||
mongo_rs,
|
mongo_rs,
|
||||||
mongo_sharded,
|
mongo_sharded,
|
||||||
|
@ -206,8 +248,7 @@ authz_sources_types(Type) ->
|
||||||
]
|
]
|
||||||
end ++
|
end ++
|
||||||
[
|
[
|
||||||
http,
|
built_in_database,
|
||||||
'built_in_database',
|
|
||||||
mysql,
|
mysql,
|
||||||
postgresql,
|
postgresql,
|
||||||
file
|
file
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
-module(emqx_authz_schema).
|
-module(emqx_authz_schema).
|
||||||
|
|
||||||
|
-include("emqx_authz.hrl").
|
||||||
-include_lib("typerefl/include/types.hrl").
|
-include_lib("typerefl/include/types.hrl").
|
||||||
-include_lib("emqx_connector/include/emqx_connector.hrl").
|
-include_lib("emqx_connector/include/emqx_connector.hrl").
|
||||||
|
|
||||||
|
@ -40,9 +41,6 @@
|
||||||
headers/1
|
headers/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-import(emqx_schema, [mk_duration/2]).
|
|
||||||
-include_lib("hocon/include/hoconsc.hrl").
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Hocon Schema
|
%% Hocon Schema
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -96,7 +94,7 @@ fields("authorization") ->
|
||||||
];
|
];
|
||||||
fields(file) ->
|
fields(file) ->
|
||||||
[
|
[
|
||||||
{type, #{type => file, desc => "Backend type."}},
|
{type, #{type => file, required => true, desc => "Backend type."}},
|
||||||
{enable, #{
|
{enable, #{
|
||||||
type => boolean(),
|
type => boolean(),
|
||||||
default => true,
|
default => true,
|
||||||
|
@ -118,17 +116,17 @@ fields(file) ->
|
||||||
];
|
];
|
||||||
fields(http_get) ->
|
fields(http_get) ->
|
||||||
[
|
[
|
||||||
{method, #{type => get, default => get, desc => "HTTP method."}},
|
{method, #{type => get, default => get, required => true, desc => "HTTP method."}},
|
||||||
{headers, fun headers_no_content_type/1}
|
{headers, fun headers_no_content_type/1}
|
||||||
] ++ http_common_fields();
|
] ++ http_common_fields();
|
||||||
fields(http_post) ->
|
fields(http_post) ->
|
||||||
[
|
[
|
||||||
{method, #{type => post, default => post, desc => "HTTP method."}},
|
{method, #{type => post, default => post, required => true, desc => "HTTP method."}},
|
||||||
{headers, fun headers/1}
|
{headers, fun headers/1}
|
||||||
] ++ http_common_fields();
|
] ++ http_common_fields();
|
||||||
fields(mnesia) ->
|
fields(mnesia) ->
|
||||||
[
|
[
|
||||||
{type, #{type => 'built_in_database', desc => "Backend type."}},
|
{type, #{type => 'built_in_database', required => true, desc => "Backend type."}},
|
||||||
{enable, #{
|
{enable, #{
|
||||||
type => boolean(),
|
type => boolean(),
|
||||||
default => true,
|
default => true,
|
||||||
|
@ -147,7 +145,7 @@ fields(mysql) ->
|
||||||
fields(postgresql) ->
|
fields(postgresql) ->
|
||||||
[
|
[
|
||||||
{query, query()},
|
{query, query()},
|
||||||
{type, #{type => postgresql, desc => "Backend type."}},
|
{type, #{type => postgresql, required => true, desc => "Backend type."}},
|
||||||
{enable, #{
|
{enable, #{
|
||||||
type => boolean(),
|
type => boolean(),
|
||||||
desc => "Enable this backend.",
|
desc => "Enable this backend.",
|
||||||
|
@ -197,7 +195,9 @@ http_common_fields() ->
|
||||||
[
|
[
|
||||||
{url, fun url/1},
|
{url, fun url/1},
|
||||||
{request_timeout,
|
{request_timeout,
|
||||||
mk_duration("Request timeout", #{default => "30s", desc => "Request timeout."})},
|
emqx_schema:mk_duration("Request timeout", #{
|
||||||
|
default => "30s", desc => "Request timeout."
|
||||||
|
})},
|
||||||
{body, #{type => map(), required => false, desc => "HTTP request body."}}
|
{body, #{type => map(), required => false, desc => "HTTP request body."}}
|
||||||
] ++
|
] ++
|
||||||
maps:to_list(
|
maps:to_list(
|
||||||
|
@ -213,10 +213,16 @@ http_common_fields() ->
|
||||||
mongo_common_fields() ->
|
mongo_common_fields() ->
|
||||||
[
|
[
|
||||||
{collection, #{
|
{collection, #{
|
||||||
type => atom(), desc => "`MongoDB` collection containing the authorization data."
|
type => atom(),
|
||||||
|
required => true,
|
||||||
|
desc => "`MongoDB` collection containing the authorization data."
|
||||||
}},
|
}},
|
||||||
{selector, #{type => map(), desc => "MQL query used to select the authorization record."}},
|
{selector, #{
|
||||||
{type, #{type => mongodb, desc => "Database backend."}},
|
type => map(),
|
||||||
|
required => true,
|
||||||
|
desc => "MQL query used to select the authorization record."
|
||||||
|
}},
|
||||||
|
{type, #{type => mongodb, required => true, desc => "Database backend."}},
|
||||||
{enable, #{
|
{enable, #{
|
||||||
type => boolean(),
|
type => boolean(),
|
||||||
default => true,
|
default => true,
|
||||||
|
@ -226,8 +232,7 @@ mongo_common_fields() ->
|
||||||
|
|
||||||
validations() ->
|
validations() ->
|
||||||
[
|
[
|
||||||
{check_ssl_opts, fun check_ssl_opts/1},
|
{check_ssl_opts, fun check_ssl_opts/1}
|
||||||
{check_headers, fun check_headers/1}
|
|
||||||
].
|
].
|
||||||
|
|
||||||
headers(type) ->
|
headers(type) ->
|
||||||
|
@ -253,6 +258,13 @@ headers_no_content_type(converter) ->
|
||||||
end;
|
end;
|
||||||
headers_no_content_type(default) ->
|
headers_no_content_type(default) ->
|
||||||
default_headers_no_content_type();
|
default_headers_no_content_type();
|
||||||
|
headers_no_content_type(validator) ->
|
||||||
|
fun(Headers) ->
|
||||||
|
case lists:keyfind(<<"content-type">>, 1, Headers) of
|
||||||
|
false -> ok;
|
||||||
|
_ -> {error, do_not_include_content_type}
|
||||||
|
end
|
||||||
|
end;
|
||||||
headers_no_content_type(_) ->
|
headers_no_content_type(_) ->
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
|
@ -291,6 +303,7 @@ transform_header_name(Headers) ->
|
||||||
Headers
|
Headers
|
||||||
).
|
).
|
||||||
|
|
||||||
|
%% TODO: fix me, not work
|
||||||
check_ssl_opts(Conf) ->
|
check_ssl_opts(Conf) ->
|
||||||
case hocon_maps:get("config.url", Conf) of
|
case hocon_maps:get("config.url", Conf) of
|
||||||
undefined ->
|
undefined ->
|
||||||
|
@ -309,25 +322,6 @@ check_ssl_opts(Conf) ->
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
check_headers(Conf) ->
|
|
||||||
case hocon_maps:get("config.method", Conf) of
|
|
||||||
undefined ->
|
|
||||||
true;
|
|
||||||
Method0 ->
|
|
||||||
Method = to_bin(Method0),
|
|
||||||
Headers = hocon_maps:get("config.headers", Conf),
|
|
||||||
case Method of
|
|
||||||
<<"post">> ->
|
|
||||||
true;
|
|
||||||
_ when Headers =:= undefined -> true;
|
|
||||||
_ when is_list(Headers) ->
|
|
||||||
case lists:member(<<"content-type">>, Headers) of
|
|
||||||
false -> true;
|
|
||||||
true -> {Method0, do_not_include_content_type}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
union_array(Item) when is_list(Item) ->
|
union_array(Item) when is_list(Item) ->
|
||||||
hoconsc:array(hoconsc:union(Item)).
|
hoconsc:array(hoconsc:union(Item)).
|
||||||
|
|
||||||
|
@ -335,6 +329,7 @@ query() ->
|
||||||
#{
|
#{
|
||||||
type => binary(),
|
type => binary(),
|
||||||
desc => "Database query used to retrieve authorization data.",
|
desc => "Database query used to retrieve authorization data.",
|
||||||
|
required => true,
|
||||||
validator => fun(S) ->
|
validator => fun(S) ->
|
||||||
case size(S) > 0 of
|
case size(S) > 0 of
|
||||||
true -> ok;
|
true -> ok;
|
||||||
|
@ -369,10 +364,3 @@ to_list(A) when is_atom(A) ->
|
||||||
atom_to_list(A);
|
atom_to_list(A);
|
||||||
to_list(B) when is_binary(B) ->
|
to_list(B) when is_binary(B) ->
|
||||||
binary_to_list(B).
|
binary_to_list(B).
|
||||||
|
|
||||||
to_bin(A) when is_atom(A) ->
|
|
||||||
atom_to_binary(A);
|
|
||||||
to_bin(B) when is_binary(B) ->
|
|
||||||
B;
|
|
||||||
to_bin(L) when is_list(L) ->
|
|
||||||
list_to_binary(L).
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ roots() ->
|
||||||
fields(single) ->
|
fields(single) ->
|
||||||
[ {mongo_type, #{type => single,
|
[ {mongo_type, #{type => single,
|
||||||
default => single,
|
default => single,
|
||||||
|
required => true,
|
||||||
desc => ?DESC("single_mongo_type")}}
|
desc => ?DESC("single_mongo_type")}}
|
||||||
, {server, fun server/1}
|
, {server, fun server/1}
|
||||||
, {w_mode, fun w_mode/1}
|
, {w_mode, fun w_mode/1}
|
||||||
|
@ -63,6 +64,7 @@ fields(single) ->
|
||||||
fields(rs) ->
|
fields(rs) ->
|
||||||
[ {mongo_type, #{type => rs,
|
[ {mongo_type, #{type => rs,
|
||||||
default => rs,
|
default => rs,
|
||||||
|
required => true,
|
||||||
desc => ?DESC("rs_mongo_type")}}
|
desc => ?DESC("rs_mongo_type")}}
|
||||||
, {servers, fun servers/1}
|
, {servers, fun servers/1}
|
||||||
, {w_mode, fun w_mode/1}
|
, {w_mode, fun w_mode/1}
|
||||||
|
@ -72,6 +74,7 @@ fields(rs) ->
|
||||||
fields(sharded) ->
|
fields(sharded) ->
|
||||||
[ {mongo_type, #{type => sharded,
|
[ {mongo_type, #{type => sharded,
|
||||||
default => sharded,
|
default => sharded,
|
||||||
|
required => true,
|
||||||
desc => ?DESC("sharded_mongo_type")}}
|
desc => ?DESC("sharded_mongo_type")}}
|
||||||
, {servers, fun servers/1}
|
, {servers, fun servers/1}
|
||||||
, {w_mode, fun w_mode/1}
|
, {w_mode, fun w_mode/1}
|
||||||
|
@ -336,7 +339,7 @@ max_overflow(_) -> undefined.
|
||||||
|
|
||||||
replica_set_name(type) -> binary();
|
replica_set_name(type) -> binary();
|
||||||
replica_set_name(desc) -> ?DESC("replica_set_name");
|
replica_set_name(desc) -> ?DESC("replica_set_name");
|
||||||
replica_set_name(required) -> false;
|
replica_set_name(required) -> true;
|
||||||
replica_set_name(_) -> undefined.
|
replica_set_name(_) -> undefined.
|
||||||
|
|
||||||
srv_record(type) -> boolean();
|
srv_record(type) -> boolean();
|
||||||
|
|
|
@ -57,6 +57,7 @@ fields(single) ->
|
||||||
[ {server, fun server/1}
|
[ {server, fun server/1}
|
||||||
, {redis_type, #{type => hoconsc:enum([single]),
|
, {redis_type, #{type => hoconsc:enum([single]),
|
||||||
default => single,
|
default => single,
|
||||||
|
required => true,
|
||||||
desc => ?DESC("single")
|
desc => ?DESC("single")
|
||||||
}}
|
}}
|
||||||
] ++
|
] ++
|
||||||
|
@ -66,6 +67,7 @@ fields(cluster) ->
|
||||||
[ {servers, fun servers/1}
|
[ {servers, fun servers/1}
|
||||||
, {redis_type, #{type => hoconsc:enum([cluster]),
|
, {redis_type, #{type => hoconsc:enum([cluster]),
|
||||||
default => cluster,
|
default => cluster,
|
||||||
|
required => true,
|
||||||
desc => ?DESC("cluster")
|
desc => ?DESC("cluster")
|
||||||
}}
|
}}
|
||||||
] ++
|
] ++
|
||||||
|
@ -75,6 +77,7 @@ fields(sentinel) ->
|
||||||
[ {servers, fun servers/1}
|
[ {servers, fun servers/1}
|
||||||
, {redis_type, #{type => hoconsc:enum([sentinel]),
|
, {redis_type, #{type => hoconsc:enum([sentinel]),
|
||||||
default => sentinel,
|
default => sentinel,
|
||||||
|
required => true,
|
||||||
desc => ?DESC("sentinel")
|
desc => ?DESC("sentinel")
|
||||||
}}
|
}}
|
||||||
, {sentinel, #{type => string(), desc => ?DESC("sentinel_desc")
|
, {sentinel, #{type => string(), desc => ?DESC("sentinel_desc")
|
||||||
|
@ -210,6 +213,7 @@ redis_fields() ->
|
||||||
, {password, fun emqx_connector_schema_lib:password/1}
|
, {password, fun emqx_connector_schema_lib:password/1}
|
||||||
, {database, #{type => integer(),
|
, {database, #{type => integer(),
|
||||||
default => 0,
|
default => 0,
|
||||||
|
required => true,
|
||||||
desc => ?DESC("database")
|
desc => ?DESC("database")
|
||||||
}}
|
}}
|
||||||
, {auto_reconnect, fun emqx_connector_schema_lib:auto_reconnect/1}
|
, {auto_reconnect, fun emqx_connector_schema_lib:auto_reconnect/1}
|
||||||
|
|
|
@ -135,7 +135,12 @@ fields(limit) ->
|
||||||
<<")">>
|
<<")">>
|
||||||
]),
|
]),
|
||||||
Meta = #{in => query, desc => Desc, default => ?DEFAULT_ROW, example => 50},
|
Meta = #{in => query, desc => Desc, default => ?DEFAULT_ROW, example => 50},
|
||||||
[{limit, hoconsc:mk(range(1, ?MAX_ROW_LIMIT), Meta)}].
|
[{limit, hoconsc:mk(range(1, ?MAX_ROW_LIMIT), Meta)}];
|
||||||
|
fields(count) ->
|
||||||
|
Meta = #{desc => <<"Results count.">>, required => true},
|
||||||
|
[{count, hoconsc:mk(range(0, inf), Meta)}];
|
||||||
|
fields(meta) ->
|
||||||
|
fields(page) ++ fields(limit) ++ fields(count).
|
||||||
|
|
||||||
-spec schema_with_example(hocon_schema:type(), term()) -> hocon_schema:field_schema_map().
|
-spec schema_with_example(hocon_schema:type(), term()) -> hocon_schema:field_schema_map().
|
||||||
schema_with_example(Type, Example) ->
|
schema_with_example(Type, Example) ->
|
||||||
|
@ -574,7 +579,7 @@ hocon_schema_to_spec(Atom, _LocalModule) when is_atom(Atom) ->
|
||||||
typename_to_spec("user_id_type()", _Mod) ->
|
typename_to_spec("user_id_type()", _Mod) ->
|
||||||
#{type => string, enum => [clientid, username]};
|
#{type => string, enum => [clientid, username]};
|
||||||
typename_to_spec("term()", _Mod) ->
|
typename_to_spec("term()", _Mod) ->
|
||||||
#{type => string};
|
#{type => string, example => <<"any">>};
|
||||||
typename_to_spec("boolean()", _Mod) ->
|
typename_to_spec("boolean()", _Mod) ->
|
||||||
#{type => boolean};
|
#{type => boolean};
|
||||||
typename_to_spec("binary()", _Mod) ->
|
typename_to_spec("binary()", _Mod) ->
|
||||||
|
|
|
@ -53,7 +53,7 @@ schema("/alarms") ->
|
||||||
responses => #{
|
responses => #{
|
||||||
200 => [
|
200 => [
|
||||||
{data, hoconsc:mk(hoconsc:array(hoconsc:ref(?MODULE, alarm)), #{})},
|
{data, hoconsc:mk(hoconsc:array(hoconsc:ref(?MODULE, alarm)), #{})},
|
||||||
{meta, hoconsc:mk(hoconsc:ref(?MODULE, meta), #{})}
|
{meta, hoconsc:mk(hoconsc:ref(emqx_dashboard_swagger, meta), #{})}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -98,11 +98,8 @@ fields(alarm) ->
|
||||||
desc => ?DESC(deactivate_at),
|
desc => ?DESC(deactivate_at),
|
||||||
example => <<"2021-10-31T10:52:52.548+08:00">>
|
example => <<"2021-10-31T10:52:52.548+08:00">>
|
||||||
})}
|
})}
|
||||||
];
|
].
|
||||||
fields(meta) ->
|
|
||||||
emqx_dashboard_swagger:fields(page) ++
|
|
||||||
emqx_dashboard_swagger:fields(limit) ++
|
|
||||||
[{count, hoconsc:mk(integer(), #{example => 1})}].
|
|
||||||
%%%==============================================================================================
|
%%%==============================================================================================
|
||||||
%% parameters trans
|
%% parameters trans
|
||||||
alarms(get, #{query_string := QString}) ->
|
alarms(get, #{query_string := QString}) ->
|
||||||
|
|
|
@ -62,7 +62,7 @@ schema("/banned") ->
|
||||||
responses => #{
|
responses => #{
|
||||||
200 => [
|
200 => [
|
||||||
{data, hoconsc:mk(hoconsc:array(hoconsc:ref(ban)), #{})},
|
{data, hoconsc:mk(hoconsc:array(hoconsc:ref(ban)), #{})},
|
||||||
{meta, hoconsc:mk(hoconsc:ref(meta), #{})}
|
{meta, hoconsc:mk(hoconsc:ref(emqx_dashboard_swagger, meta), #{})}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -147,11 +147,7 @@ fields(ban) ->
|
||||||
required => false,
|
required => false,
|
||||||
example => <<"2021-10-25T21:53:47+08:00">>
|
example => <<"2021-10-25T21:53:47+08:00">>
|
||||||
})}
|
})}
|
||||||
];
|
].
|
||||||
fields(meta) ->
|
|
||||||
emqx_dashboard_swagger:fields(page) ++
|
|
||||||
emqx_dashboard_swagger:fields(limit) ++
|
|
||||||
[{count, hoconsc:mk(integer(), #{example => 1})}].
|
|
||||||
|
|
||||||
banned(get, #{query_string := Params}) ->
|
banned(get, #{query_string := Params}) ->
|
||||||
Response = emqx_mgmt_api:paginate(?TAB, Params, ?FORMAT_FUN),
|
Response = emqx_mgmt_api:paginate(?TAB, Params, ?FORMAT_FUN),
|
||||||
|
|
|
@ -203,7 +203,7 @@ schema("/clients") ->
|
||||||
responses => #{
|
responses => #{
|
||||||
200 => [
|
200 => [
|
||||||
{data, hoconsc:mk(hoconsc:array(hoconsc:ref(?MODULE, client)), #{})},
|
{data, hoconsc:mk(hoconsc:array(hoconsc:ref(?MODULE, client)), #{})},
|
||||||
{meta, hoconsc:mk(hoconsc:ref(?MODULE, meta), #{})}
|
{meta, hoconsc:mk(hoconsc:ref(emqx_dashboard_swagger, meta), #{})}
|
||||||
],
|
],
|
||||||
400 =>
|
400 =>
|
||||||
emqx_dashboard_swagger:error_codes(
|
emqx_dashboard_swagger:error_codes(
|
||||||
|
@ -518,11 +518,7 @@ fields(subscribe) ->
|
||||||
fields(unsubscribe) ->
|
fields(unsubscribe) ->
|
||||||
[
|
[
|
||||||
{topic, hoconsc:mk(binary(), #{desc => <<"Topic">>})}
|
{topic, hoconsc:mk(binary(), #{desc => <<"Topic">>})}
|
||||||
];
|
].
|
||||||
fields(meta) ->
|
|
||||||
emqx_dashboard_swagger:fields(page) ++
|
|
||||||
emqx_dashboard_swagger:fields(limit) ++
|
|
||||||
[{count, hoconsc:mk(integer(), #{example => 1})}].
|
|
||||||
|
|
||||||
%%%==============================================================================================
|
%%%==============================================================================================
|
||||||
%% parameters trans
|
%% parameters trans
|
||||||
|
|
|
@ -106,9 +106,11 @@ schema("/configs_reset/:rootname") ->
|
||||||
post => #{
|
post => #{
|
||||||
tags => [conf],
|
tags => [conf],
|
||||||
description =>
|
description =>
|
||||||
<<"Reset the config entry specified by the query string parameter `conf_path`.<br/>\n"
|
<<
|
||||||
|
"Reset the config entry specified by the query string parameter `conf_path`.<br/>\n"
|
||||||
"- For a config entry that has default value, this resets it to the default value;\n"
|
"- For a config entry that has default value, this resets it to the default value;\n"
|
||||||
"- For a config entry that has no default value, an error 400 will be returned">>,
|
"- For a config entry that has no default value, an error 400 will be returned"
|
||||||
|
>>,
|
||||||
%% We only return "200" rather than the new configs that has been changed, as
|
%% We only return "200" rather than the new configs that has been changed, as
|
||||||
%% the schema of the changed configs is depends on the request parameter
|
%% the schema of the changed configs is depends on the request parameter
|
||||||
%% `conf_path`, it cannot be defined here.
|
%% `conf_path`, it cannot be defined here.
|
||||||
|
|
|
@ -60,7 +60,7 @@ schema("/topics") ->
|
||||||
responses => #{
|
responses => #{
|
||||||
200 => [
|
200 => [
|
||||||
{data, hoconsc:mk(hoconsc:array(hoconsc:ref(topic)), #{})},
|
{data, hoconsc:mk(hoconsc:array(hoconsc:ref(topic)), #{})},
|
||||||
{meta, hoconsc:mk(hoconsc:ref(meta), #{})}
|
{meta, hoconsc:mk(hoconsc:ref(emqx_dashboard_swagger, meta), #{})}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,11 +91,7 @@ fields(topic) ->
|
||||||
desc => <<"Node">>,
|
desc => <<"Node">>,
|
||||||
required => true
|
required => true
|
||||||
})}
|
})}
|
||||||
];
|
].
|
||||||
fields(meta) ->
|
|
||||||
emqx_dashboard_swagger:fields(page) ++
|
|
||||||
emqx_dashboard_swagger:fields(limit) ++
|
|
||||||
[{count, hoconsc:mk(integer(), #{example => 1})}].
|
|
||||||
|
|
||||||
%%%==============================================================================================
|
%%%==============================================================================================
|
||||||
%% parameters trans
|
%% parameters trans
|
||||||
|
|
|
@ -23,5 +23,6 @@ acb3544d4b112121b5d9414237d2af7860ccc2a3
|
||||||
f1acfece6b79ed69b491da03783a7adaa7627b96
|
f1acfece6b79ed69b491da03783a7adaa7627b96
|
||||||
# reformat apps/emqx_management
|
# reformat apps/emqx_management
|
||||||
aa7807baebfa5d8678025e43f386bcd9b3259d6a
|
aa7807baebfa5d8678025e43f386bcd9b3259d6a
|
||||||
|
bf54f571fb8b27e76ada4ca75137d96ce4211d60
|
||||||
# reformat apps/emqx_slow_subs
|
# reformat apps/emqx_slow_subs
|
||||||
83511f8a4c1570a2c89d9c6c5b6f462520199ed8
|
83511f8a4c1570a2c89d9c6c5b6f462520199ed8
|
||||||
|
|
|
@ -13,6 +13,7 @@ APPS+=( 'apps/emqx_authn' 'apps/emqx_authz' )
|
||||||
APPS+=( 'lib-ee/emqx_enterprise_conf' 'lib-ee/emqx_license' )
|
APPS+=( 'lib-ee/emqx_enterprise_conf' 'lib-ee/emqx_license' )
|
||||||
APPS+=( 'apps/emqx_exhook')
|
APPS+=( 'apps/emqx_exhook')
|
||||||
APPS+=( 'apps/emqx_retainer' 'apps/emqx_slow_subs')
|
APPS+=( 'apps/emqx_retainer' 'apps/emqx_slow_subs')
|
||||||
|
APPS+=( 'apps/emqx_management')
|
||||||
|
|
||||||
for app in "${APPS[@]}"; do
|
for app in "${APPS[@]}"; do
|
||||||
echo "$app ..."
|
echo "$app ..."
|
||||||
|
|
Loading…
Reference in New Issue