diff --git a/apps/emqx_authz/include/emqx_authz.hrl b/apps/emqx_authz/include/emqx_authz.hrl
index 13c100ba8..a3fe2d1f9 100644
--- a/apps/emqx_authz/include/emqx_authz.hrl
+++ b/apps/emqx_authz/include/emqx_authz.hrl
@@ -16,13 +16,15 @@
-define(APP, emqx_authz).
--define(ALLOW_DENY(A), ((A =:= allow) orelse (A =:= <<"allow">>) orelse
- (A =:= deny) orelse (A =:= <<"deny">>)
- )).
--define(PUBSUB(A), ((A =:= subscribe) orelse (A =:= <<"subscribe">>) orelse
- (A =:= publish) orelse (A =:= <<"publish">>) orelse
- (A =:= all) orelse (A =:= <<"all">>)
- )).
+-define(ALLOW_DENY(A),
+ ((A =:= allow) orelse (A =:= <<"allow">>) orelse
+ (A =:= deny) orelse (A =:= <<"deny">>))
+).
+-define(PUBSUB(A),
+ ((A =:= subscribe) orelse (A =:= <<"subscribe">>) orelse
+ (A =:= publish) orelse (A =:= <<"publish">>) orelse
+ (A =:= all) orelse (A =:= <<"all">>))
+).
%% authz_mnesia
-define(ACL_TABLE, emqx_acl).
@@ -44,53 +46,69 @@
-define(RE_PLACEHOLDER, "\\$\\{[a-z0-9_]+\\}").
%% API examples
--define(USERNAME_RULES_EXAMPLE, #{username => user1,
- rules => [ #{topic => <<"test/toopic/1">>,
- permission => <<"allow">>,
- action => <<"publish">>
- }
- , #{topic => <<"test/toopic/2">>,
- permission => <<"allow">>,
- action => <<"subscribe">>
- }
- , #{topic => <<"eq test/#">>,
- permission => <<"deny">>,
- action => <<"all">>
- }
- ]
- }).
--define(CLIENTID_RULES_EXAMPLE, #{clientid => client1,
- rules => [ #{topic => <<"test/toopic/1">>,
- permission => <<"allow">>,
- action => <<"publish">>
- }
- , #{topic => <<"test/toopic/2">>,
- permission => <<"allow">>,
- action => <<"subscribe">>
- }
- , #{topic => <<"eq test/#">>,
- permission => <<"deny">>,
- action => <<"all">>
- }
- ]
- }).
--define(ALL_RULES_EXAMPLE, #{rules => [ #{topic => <<"test/toopic/1">>,
- permission => <<"allow">>,
- action => <<"publish">>
- }
- , #{topic => <<"test/toopic/2">>,
- permission => <<"allow">>,
- action => <<"subscribe">>
- }
- , #{topic => <<"eq test/#">>,
- permission => <<"deny">>,
- action => <<"all">>
- }
- ]
- }).
--define(META_EXAMPLE, #{ page => 1
- , limit => 100
- , count => 1
- }).
+-define(USERNAME_RULES_EXAMPLE, #{
+ username => user1,
+ rules => [
+ #{
+ topic => <<"test/toopic/1">>,
+ permission => <<"allow">>,
+ action => <<"publish">>
+ },
+ #{
+ topic => <<"test/toopic/2">>,
+ permission => <<"allow">>,
+ action => <<"subscribe">>
+ },
+ #{
+ topic => <<"eq test/#">>,
+ permission => <<"deny">>,
+ action => <<"all">>
+ }
+ ]
+}).
+-define(CLIENTID_RULES_EXAMPLE, #{
+ clientid => client1,
+ rules => [
+ #{
+ topic => <<"test/toopic/1">>,
+ permission => <<"allow">>,
+ action => <<"publish">>
+ },
+ #{
+ topic => <<"test/toopic/2">>,
+ permission => <<"allow">>,
+ action => <<"subscribe">>
+ },
+ #{
+ topic => <<"eq test/#">>,
+ permission => <<"deny">>,
+ action => <<"all">>
+ }
+ ]
+}).
+-define(ALL_RULES_EXAMPLE, #{
+ rules => [
+ #{
+ topic => <<"test/toopic/1">>,
+ permission => <<"allow">>,
+ action => <<"publish">>
+ },
+ #{
+ topic => <<"test/toopic/2">>,
+ permission => <<"allow">>,
+ action => <<"subscribe">>
+ },
+ #{
+ topic => <<"eq test/#">>,
+ permission => <<"deny">>,
+ action => <<"all">>
+ }
+ ]
+}).
+-define(META_EXAMPLE, #{
+ page => 1,
+ limit => 100,
+ count => 1
+}).
-define(RESOURCE_GROUP, <<"emqx_authz">>).
diff --git a/apps/emqx_authz/rebar.config b/apps/emqx_authz/rebar.config
index 2e33f41d5..da2fa7807 100644
--- a/apps/emqx_authz/rebar.config
+++ b/apps/emqx_authz/rebar.config
@@ -1,11 +1,14 @@
%% -*- mode: erlang -*-
{erl_opts, [debug_info, nowarn_unused_import]}.
-{deps, [ {emqx, {path, "../emqx"}}
- , {emqx_connector, {path, "../emqx_connector"}}
- ]}.
+{deps, [
+ {emqx, {path, "../emqx"}},
+ {emqx_connector, {path, "../emqx_connector"}}
+]}.
{shell, [
- % {config, "config/sys.config"},
+ % {config, "config/sys.config"},
{apps, [emqx_authz]}
]}.
+
+{project_plugins, [erlfmt]}.
diff --git a/apps/emqx_authz/src/emqx_authz.app.src b/apps/emqx_authz/src/emqx_authz.app.src
index ff43b3536..a8e781df9 100644
--- a/apps/emqx_authz/src/emqx_authz.app.src
+++ b/apps/emqx_authz/src/emqx_authz.app.src
@@ -1,18 +1,18 @@
%% -*- mode: erlang -*-
-{application, emqx_authz,
- [{description, "An OTP application"},
- {vsn, "0.1.1"},
- {registered, []},
- {mod, {emqx_authz_app, []}},
- {applications,
- [kernel,
- stdlib,
- crypto,
- emqx_connector
- ]},
- {env,[]},
- {modules, []},
+{application, emqx_authz, [
+ {description, "An OTP application"},
+ {vsn, "0.1.1"},
+ {registered, []},
+ {mod, {emqx_authz_app, []}},
+ {applications, [
+ kernel,
+ stdlib,
+ crypto,
+ emqx_connector
+ ]},
+ {env, []},
+ {modules, []},
- {licenses, ["Apache 2.0"]},
- {links, []}
- ]}.
+ {licenses, ["Apache 2.0"]},
+ {links, []}
+]}.
diff --git a/apps/emqx_authz/src/emqx_authz.erl b/apps/emqx_authz/src/emqx_authz.erl
index 3e5a53649..40c987e1f 100644
--- a/apps/emqx_authz/src/emqx_authz.erl
+++ b/apps/emqx_authz/src/emqx_authz.erl
@@ -25,29 +25,30 @@
-compile(nowarn_export_all).
-endif.
--export([ register_metrics/0
- , init/0
- , deinit/0
- , lookup/0
- , lookup/1
- , move/2
- , update/2
- , authorize/5
- ]).
+-export([
+ register_metrics/0,
+ init/0,
+ deinit/0,
+ lookup/0,
+ lookup/1,
+ move/2,
+ update/2,
+ authorize/5
+]).
-export([post_config_update/5, pre_config_update/3]).
-export([acl_conf_file/0]).
--type(source() :: map()).
+-type source() :: map().
--type(match_result() :: {matched, allow} | {matched, deny} | nomatch).
+-type match_result() :: {matched, allow} | {matched, deny} | nomatch.
--type(default_result() :: allow | deny).
+-type default_result() :: allow | deny.
--type(authz_result() :: {stop, allow} | {ok, deny}).
+-type authz_result() :: {stop, allow} | {ok, deny}.
--type(sources() :: [source()]).
+-type sources() :: [source()].
-define(METRIC_ALLOW, 'client.authorize.allow').
-define(METRIC_DENY, 'client.authorize.deny').
@@ -60,24 +61,25 @@
%% Initialize authz backend.
%% Populate the passed configuration map with necessary data,
%% like `ResourceID`s
--callback(init(source()) -> source()).
+-callback init(source()) -> source().
%% Get authz text description.
--callback(description() -> string()).
+-callback description() -> string().
%% Destroy authz backend.
%% Make cleanup of all allocated data.
%% An authz backend will not be used after `destroy`.
--callback(destroy(source()) -> ok).
+-callback destroy(source()) -> ok.
%% Authorize client action.
--callback(authorize(
- emqx_types:clientinfo(),
- emqx_types:pubsub(),
- emqx_types:topic(),
- source()) -> match_result()).
+-callback authorize(
+ emqx_types:clientinfo(),
+ emqx_types:pubsub(),
+ emqx_types:topic(),
+ source()
+) -> match_result().
--spec(register_metrics() -> ok).
+-spec register_metrics() -> ok.
register_metrics() ->
lists:foreach(fun emqx_metrics:ensure/1, ?METRICS).
@@ -104,13 +106,16 @@ lookup(Type) ->
move(Type, ?CMD_MOVE_BEFORE(Before)) ->
emqx_authz_utils:update_config(
- ?CONF_KEY_PATH, {?CMD_MOVE, type(Type), ?CMD_MOVE_BEFORE(type(Before))});
+ ?CONF_KEY_PATH, {?CMD_MOVE, type(Type), ?CMD_MOVE_BEFORE(type(Before))}
+ );
move(Type, ?CMD_MOVE_AFTER(After)) ->
emqx_authz_utils:update_config(
- ?CONF_KEY_PATH, {?CMD_MOVE, type(Type), ?CMD_MOVE_AFTER(type(After))});
+ ?CONF_KEY_PATH, {?CMD_MOVE, type(Type), ?CMD_MOVE_AFTER(type(After))}
+ );
move(Type, Position) ->
emqx_authz_utils:update_config(
- ?CONF_KEY_PATH, {?CMD_MOVE, type(Type), Position}).
+ ?CONF_KEY_PATH, {?CMD_MOVE, type(Type), Position}
+ ).
update({?CMD_REPLACE, Type}, Sources) ->
emqx_authz_utils:update_config(?CONF_KEY_PATH, {{?CMD_REPLACE, type(Type)}, Sources});
@@ -205,7 +210,8 @@ do_move({?CMD_MOVE, Type, ?CMD_MOVE_AFTER(After)}, Sources) ->
{S2, Front2, Rear2} = take(After, Front1 ++ Rear1),
Front2 ++ [S2, S1] ++ Rear2.
-ensure_resource_deleted(#{enable := false}) -> ok;
+ensure_resource_deleted(#{enable := false}) ->
+ ok;
ensure_resource_deleted(#{type := Type} = Source) ->
Module = authz_module(Type),
Module:destroy(Source).
@@ -213,17 +219,19 @@ ensure_resource_deleted(#{type := Type} = Source) ->
check_dup_types(Sources) ->
check_dup_types(Sources, []).
-check_dup_types([], _Checked) -> ok;
+check_dup_types([], _Checked) ->
+ ok;
check_dup_types([Source | Sources], Checked) ->
%% the input might be raw or type-checked result, so lookup both 'type' and <<"type">>
%% TODO: check: really?
- Type = case maps:get(<<"type">>, Source, maps:get(type, Source, undefined)) of
- undefined ->
- %% this should never happen if the value is type checked by honcon schema
- throw({bad_source_input, Source});
- Type0 ->
- type(Type0)
- end,
+ Type =
+ case maps:get(<<"type">>, Source, maps:get(type, Source, undefined)) of
+ undefined ->
+ %% this should never happen if the value is type checked by honcon schema
+ throw({bad_source_input, Source});
+ Type0 ->
+ type(Type0)
+ end,
case lists:member(Type, Checked) of
true ->
%% we have made it clear not to support more than one authz instance for each type
@@ -240,7 +248,8 @@ init_sources(Sources) ->
end,
lists:map(fun init_source/1, Sources).
-init_source(#{enable := false} = Source) -> Source;
+init_source(#{enable := false} = Source) ->
+ Source;
init_source(#{type := Type} = Source) ->
Module = authz_module(Type),
Module:init(Source).
@@ -250,42 +259,63 @@ init_source(#{type := Type} = Source) ->
%%--------------------------------------------------------------------
%% @doc Check AuthZ
--spec(authorize( emqx_types:clientinfo()
- , emqx_types:pubsub()
- , emqx_types:topic()
- , default_result()
- , sources())
- -> authz_result()).
-authorize(#{username := Username,
- peerhost := IpAddress
- } = Client, PubSub, Topic, DefaultResult, Sources) ->
+-spec authorize(
+ emqx_types:clientinfo(),
+ emqx_types:pubsub(),
+ emqx_types:topic(),
+ default_result(),
+ sources()
+) ->
+ authz_result().
+authorize(
+ #{
+ username := Username,
+ peerhost := IpAddress
+ } = Client,
+ PubSub,
+ Topic,
+ DefaultResult,
+ Sources
+) ->
case do_authorize(Client, PubSub, Topic, Sources) of
- {{matched, allow}, AuthzSource}->
- emqx:run_hook('client.check_authz_complete',
- [Client, PubSub, Topic, allow, AuthzSource]),
- ?SLOG(info, #{msg => "authorization_permission_allowed",
- username => Username,
- ipaddr => IpAddress,
- topic => Topic}),
+ {{matched, allow}, AuthzSource} ->
+ emqx:run_hook(
+ 'client.check_authz_complete',
+ [Client, PubSub, Topic, allow, AuthzSource]
+ ),
+ ?SLOG(info, #{
+ msg => "authorization_permission_allowed",
+ username => Username,
+ ipaddr => IpAddress,
+ topic => Topic
+ }),
emqx_metrics:inc(?METRIC_ALLOW),
{stop, allow};
- {{matched, deny}, AuthzSource}->
- emqx:run_hook('client.check_authz_complete',
- [Client, PubSub, Topic, deny, AuthzSource]),
- ?SLOG(info, #{msg => "authorization_permission_denied",
- username => Username,
- ipaddr => IpAddress,
- topic => Topic}),
+ {{matched, deny}, AuthzSource} ->
+ emqx:run_hook(
+ 'client.check_authz_complete',
+ [Client, PubSub, Topic, deny, AuthzSource]
+ ),
+ ?SLOG(info, #{
+ msg => "authorization_permission_denied",
+ username => Username,
+ ipaddr => IpAddress,
+ topic => Topic
+ }),
emqx_metrics:inc(?METRIC_DENY),
{stop, deny};
nomatch ->
- emqx:run_hook('client.check_authz_complete',
- [Client, PubSub, Topic, DefaultResult, default]),
- ?SLOG(info, #{msg => "authorization_failed_nomatch",
- username => Username,
- ipaddr => IpAddress,
- topic => Topic,
- reason => "no-match rule"}),
+ emqx:run_hook(
+ 'client.check_authz_complete',
+ [Client, PubSub, Topic, DefaultResult, default]
+ ),
+ ?SLOG(info, #{
+ msg => "authorization_failed_nomatch",
+ username => Username,
+ ipaddr => IpAddress,
+ topic => Topic,
+ reason => "no-match rule"
+ }),
emqx_metrics:inc(?METRIC_NOMATCH),
{stop, DefaultResult}
end.
@@ -294,8 +324,12 @@ do_authorize(_Client, _PubSub, _Topic, []) ->
nomatch;
do_authorize(Client, PubSub, Topic, [#{enable := false} | Rest]) ->
do_authorize(Client, PubSub, Topic, Rest);
-do_authorize(Client, PubSub, Topic,
- [Connector = #{type := Type} | Tail] ) ->
+do_authorize(
+ Client,
+ PubSub,
+ Topic,
+ [Connector = #{type := Type} | Tail]
+) ->
Module = authz_module(Type),
case Module:authorize(Client, PubSub, Topic, Connector) of
nomatch -> do_authorize(Client, PubSub, Topic, Tail);
@@ -311,7 +345,7 @@ take(Type) -> take(Type, lookup()).
%% Take the source of give type, the sources list is split into two parts
%% front part and rear part.
take(Type, Sources) ->
- {Front, Rear} = lists:splitwith(fun(T) -> type(T) =/= type(Type) end, Sources),
+ {Front, Rear} = lists:splitwith(fun(T) -> type(T) =/= type(Type) end, Sources),
case Rear =:= [] of
true ->
throw({not_found_source, Type});
@@ -321,7 +355,7 @@ take(Type, Sources) ->
find_action_in_hooks() ->
Callbacks = emqx_hooks:lookup('client.authorize'),
- [Action] = [Action || {callback,{?MODULE, authorize, _} = Action, _, _} <- Callbacks ],
+ [Action] = [Action || {callback, {?MODULE, authorize, _} = Action, _, _} <- Callbacks],
Action.
authz_module('built_in_database') ->
@@ -364,8 +398,11 @@ acl_conf_file() ->
filename:join([emqx:data_dir(), "authz", "acl.conf"]).
maybe_write_certs(#{<<"type">> := Type} = Source) ->
- case emqx_tls_lib:ensure_ssl_files(
- ssl_file_path(Type), maps:get(<<"ssl">>, Source, undefined)) of
+ case
+ emqx_tls_lib:ensure_ssl_files(
+ ssl_file_path(Type), maps:get(<<"ssl">>, Source, undefined)
+ )
+ of
{ok, SSL} ->
new_ssl_source(Source, SSL);
{error, Reason} ->
@@ -380,7 +417,8 @@ clear_certs(OldSource) ->
write_file(Filename, Bytes) ->
ok = filelib:ensure_dir(Filename),
case file:write_file(Filename, Bytes) of
- ok -> {ok, iolist_to_binary(Filename)};
+ ok ->
+ {ok, iolist_to_binary(Filename)};
{error, Reason} ->
?SLOG(error, #{filename => Filename, msg => "write_file_error", reason => Reason}),
throw(Reason)
diff --git a/apps/emqx_authz/src/emqx_authz_api_mnesia.erl b/apps/emqx_authz/src/emqx_authz_api_mnesia.erl
index 12459d684..2f06ccdbc 100644
--- a/apps/emqx_authz/src/emqx_authz_api_mnesia.erl
+++ b/apps/emqx_authz/src/emqx_authz_api_mnesia.erl
@@ -30,25 +30,28 @@
-define(ACL_USERNAME_QSCHEMA, [{<<"like_username">>, binary}]).
-define(ACL_CLIENTID_QSCHEMA, [{<<"like_clientid">>, binary}]).
-
--export([ api_spec/0
- , paths/0
- , schema/1
- , fields/1
- ]).
+-export([
+ api_spec/0,
+ paths/0,
+ schema/1,
+ fields/1
+]).
%% operation funs
--export([ users/2
- , clients/2
- , user/2
- , client/2
- , all/2
- , purge/2
- ]).
+-export([
+ users/2,
+ clients/2,
+ user/2,
+ client/2,
+ all/2,
+ purge/2
+]).
%% query funs
--export([ query_username/4
- , query_clientid/4]).
+-export([
+ query_username/4,
+ query_clientid/4
+]).
-export([format_result/1]).
@@ -65,279 +68,399 @@ api_spec() ->
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}).
paths() ->
- [ "/authorization/sources/built_in_database/username"
- , "/authorization/sources/built_in_database/clientid"
- , "/authorization/sources/built_in_database/username/:username"
- , "/authorization/sources/built_in_database/clientid/:clientid"
- , "/authorization/sources/built_in_database/all"
- , "/authorization/sources/built_in_database/purge-all"].
+ [
+ "/authorization/sources/built_in_database/username",
+ "/authorization/sources/built_in_database/clientid",
+ "/authorization/sources/built_in_database/username/:username",
+ "/authorization/sources/built_in_database/clientid/:clientid",
+ "/authorization/sources/built_in_database/all",
+ "/authorization/sources/built_in_database/purge-all"
+ ].
%%--------------------------------------------------------------------
%% Schema for each URI
%%--------------------------------------------------------------------
schema("/authorization/sources/built_in_database/username") ->
- #{ 'operationId' => users
- , get =>
- #{ tags => [<<"authorization">>]
- , description => <<"Show the list of record for username">>
- , parameters =>
- [ ref(emqx_dashboard_swagger, page)
- , ref(emqx_dashboard_swagger, limit)
- , { like_username
- , mk( binary(), #{ in => query
- , required => false
- , desc => <<"Fuzzy search `username` as substring">>})}
- ]
- , responses =>
- #{ 200 => swagger_with_example( {username_response_data, ?TYPE_REF}
- , {username, ?PAGE_QUERY_EXAMPLE})
+ #{
+ 'operationId' => users,
+ get =>
+ #{
+ tags => [<<"authorization">>],
+ description => <<"Show the list of record for username">>,
+ parameters =>
+ [
+ ref(emqx_dashboard_swagger, page),
+ ref(emqx_dashboard_swagger, limit),
+ {like_username,
+ mk(binary(), #{
+ in => query,
+ required => false,
+ desc => <<"Fuzzy search `username` as substring">>
+ })}
+ ],
+ responses =>
+ #{
+ 200 => swagger_with_example(
+ {username_response_data, ?TYPE_REF},
+ {username, ?PAGE_QUERY_EXAMPLE}
+ )
+ }
+ },
+ post =>
+ #{
+ tags => [<<"authorization">>],
+ description => <<"Add new records for username">>,
+ 'requestBody' => swagger_with_example(
+ {rules_for_username, ?TYPE_ARRAY},
+ {username, ?POST_ARRAY_EXAMPLE}
+ ),
+ responses =>
+ #{
+ 204 => <<"Created">>,
+ 400 => emqx_dashboard_swagger:error_codes(
+ [?BAD_REQUEST], <<"Bad username or bad rule schema">>
+ )
+ }
}
- }
- , post =>
- #{ tags => [<<"authorization">>]
- , description => <<"Add new records for username">>
- , 'requestBody' => swagger_with_example( {rules_for_username, ?TYPE_ARRAY}
- , {username, ?POST_ARRAY_EXAMPLE})
- , responses =>
- #{ 204 => <<"Created">>
- , 400 => emqx_dashboard_swagger:error_codes(
- [?BAD_REQUEST], <<"Bad username or bad rule schema">>)
- }
- }
};
schema("/authorization/sources/built_in_database/clientid") ->
- #{ 'operationId' => clients
- , get =>
- #{ tags => [<<"authorization">>]
- , description => <<"Show the list of record for clientid">>
- , parameters =>
- [ ref(emqx_dashboard_swagger, page)
- , ref(emqx_dashboard_swagger, limit)
- , { like_clientid
- , mk( binary()
- , #{ in => query
- , required => false
- , desc => <<"Fuzzy search `clientid` as substring">>})
+ #{
+ 'operationId' => clients,
+ get =>
+ #{
+ tags => [<<"authorization">>],
+ description => <<"Show the list of record for clientid">>,
+ parameters =>
+ [
+ ref(emqx_dashboard_swagger, page),
+ ref(emqx_dashboard_swagger, limit),
+ {like_clientid,
+ mk(
+ binary(),
+ #{
+ in => query,
+ required => false,
+ desc => <<"Fuzzy search `clientid` as substring">>
+ }
+ )}
+ ],
+ responses =>
+ #{
+ 200 => swagger_with_example(
+ {clientid_response_data, ?TYPE_REF},
+ {clientid, ?PAGE_QUERY_EXAMPLE}
+ )
}
- ]
- , responses =>
- #{ 200 => swagger_with_example( {clientid_response_data, ?TYPE_REF}
- , {clientid, ?PAGE_QUERY_EXAMPLE})
- }
- }
- , post =>
- #{ tags => [<<"authorization">>]
- , description => <<"Add new records for clientid">>
- , 'requestBody' => swagger_with_example( {rules_for_clientid, ?TYPE_ARRAY}
- , {clientid, ?POST_ARRAY_EXAMPLE})
- , responses =>
- #{ 204 => <<"Created">>
- , 400 => emqx_dashboard_swagger:error_codes(
- [?BAD_REQUEST], <<"Bad clientid or bad rule schema">>)
- }
- }
- };
-schema("/authorization/sources/built_in_database/username/:username") ->
- #{ 'operationId' => user
- , get =>
- #{ tags => [<<"authorization">>]
- , description => <<"Get record info for username">>
- , parameters => [ref(username)]
- , responses =>
- #{ 200 => swagger_with_example( {rules_for_username, ?TYPE_REF}
- , {username, ?PUT_MAP_EXAMPLE})
- , 404 => emqx_dashboard_swagger:error_codes(
- [?NOT_FOUND], <<"Not Found">>)
- }
- }
- , put =>
- #{ tags => [<<"authorization">>]
- , description => <<"Set record for username">>
- , parameters => [ref(username)]
- , 'requestBody' => swagger_with_example( {rules_for_username, ?TYPE_REF}
- , {username, ?PUT_MAP_EXAMPLE})
- , responses =>
- #{ 204 => <<"Updated">>
- , 400 => emqx_dashboard_swagger:error_codes(
- [?BAD_REQUEST], <<"Bad username or bad rule schema">>)
- }
- }
- , delete =>
- #{ tags => [<<"authorization">>]
- , description => <<"Delete one record for username">>
- , parameters => [ref(username)]
- , responses =>
- #{ 204 => <<"Deleted">>
- , 400 => emqx_dashboard_swagger:error_codes(
- [?BAD_REQUEST], <<"Bad username">>)
- , 404 => emqx_dashboard_swagger:error_codes(
- [?NOT_FOUND], <<"Username Not Found">>)
- }
- }
- };
-schema("/authorization/sources/built_in_database/clientid/:clientid") ->
- #{ 'operationId' => client
- , get =>
- #{ tags => [<<"authorization">>]
- , description => <<"Get record info for clientid">>
- , parameters => [ref(clientid)]
- , responses =>
- #{ 200 => swagger_with_example( {rules_for_clientid, ?TYPE_REF}
- , {clientid, ?PUT_MAP_EXAMPLE})
- , 404 => emqx_dashboard_swagger:error_codes(
- [?NOT_FOUND], <<"Not Found">>)
- }
},
- put =>
- #{ tags => [<<"authorization">>]
- , description => <<"Set record for clientid">>
- , parameters => [ref(clientid)]
- , 'requestBody' => swagger_with_example( {rules_for_clientid, ?TYPE_REF}
- , {clientid, ?PUT_MAP_EXAMPLE})
- , responses =>
- #{ 204 => <<"Updated">>
- , 400 => emqx_dashboard_swagger:error_codes(
- [?BAD_REQUEST], <<"Bad clientid or bad rule schema">>)
- }
+ post =>
+ #{
+ tags => [<<"authorization">>],
+ description => <<"Add new records for clientid">>,
+ 'requestBody' => swagger_with_example(
+ {rules_for_clientid, ?TYPE_ARRAY},
+ {clientid, ?POST_ARRAY_EXAMPLE}
+ ),
+ responses =>
+ #{
+ 204 => <<"Created">>,
+ 400 => emqx_dashboard_swagger:error_codes(
+ [?BAD_REQUEST], <<"Bad clientid or bad rule schema">>
+ )
+ }
}
- , delete =>
- #{ tags => [<<"authorization">>]
- , description => <<"Delete one record for clientid">>
- , parameters => [ref(clientid)]
- , responses =>
- #{ 204 => <<"Deleted">>
- , 400 => emqx_dashboard_swagger:error_codes(
- [?BAD_REQUEST], <<"Bad clientid">>)
- , 404 => emqx_dashboard_swagger:error_codes(
- [?NOT_FOUND], <<"ClientID Not Found">>)
- }
+ };
+schema("/authorization/sources/built_in_database/username/:username") ->
+ #{
+ 'operationId' => user,
+ get =>
+ #{
+ tags => [<<"authorization">>],
+ description => <<"Get record info for username">>,
+ parameters => [ref(username)],
+ responses =>
+ #{
+ 200 => swagger_with_example(
+ {rules_for_username, ?TYPE_REF},
+ {username, ?PUT_MAP_EXAMPLE}
+ ),
+ 404 => emqx_dashboard_swagger:error_codes(
+ [?NOT_FOUND], <<"Not Found">>
+ )
+ }
+ },
+ put =>
+ #{
+ tags => [<<"authorization">>],
+ description => <<"Set record for username">>,
+ parameters => [ref(username)],
+ 'requestBody' => swagger_with_example(
+ {rules_for_username, ?TYPE_REF},
+ {username, ?PUT_MAP_EXAMPLE}
+ ),
+ responses =>
+ #{
+ 204 => <<"Updated">>,
+ 400 => emqx_dashboard_swagger:error_codes(
+ [?BAD_REQUEST], <<"Bad username or bad rule schema">>
+ )
+ }
+ },
+ delete =>
+ #{
+ tags => [<<"authorization">>],
+ description => <<"Delete one record for username">>,
+ parameters => [ref(username)],
+ responses =>
+ #{
+ 204 => <<"Deleted">>,
+ 400 => emqx_dashboard_swagger:error_codes(
+ [?BAD_REQUEST], <<"Bad username">>
+ ),
+ 404 => emqx_dashboard_swagger:error_codes(
+ [?NOT_FOUND], <<"Username Not Found">>
+ )
+ }
}
- };
+ };
+schema("/authorization/sources/built_in_database/clientid/:clientid") ->
+ #{
+ 'operationId' => client,
+ get =>
+ #{
+ tags => [<<"authorization">>],
+ description => <<"Get record info for clientid">>,
+ parameters => [ref(clientid)],
+ responses =>
+ #{
+ 200 => swagger_with_example(
+ {rules_for_clientid, ?TYPE_REF},
+ {clientid, ?PUT_MAP_EXAMPLE}
+ ),
+ 404 => emqx_dashboard_swagger:error_codes(
+ [?NOT_FOUND], <<"Not Found">>
+ )
+ }
+ },
+ put =>
+ #{
+ tags => [<<"authorization">>],
+ description => <<"Set record for clientid">>,
+ parameters => [ref(clientid)],
+ 'requestBody' => swagger_with_example(
+ {rules_for_clientid, ?TYPE_REF},
+ {clientid, ?PUT_MAP_EXAMPLE}
+ ),
+ responses =>
+ #{
+ 204 => <<"Updated">>,
+ 400 => emqx_dashboard_swagger:error_codes(
+ [?BAD_REQUEST], <<"Bad clientid or bad rule schema">>
+ )
+ }
+ },
+ delete =>
+ #{
+ tags => [<<"authorization">>],
+ description => <<"Delete one record for clientid">>,
+ parameters => [ref(clientid)],
+ responses =>
+ #{
+ 204 => <<"Deleted">>,
+ 400 => emqx_dashboard_swagger:error_codes(
+ [?BAD_REQUEST], <<"Bad clientid">>
+ ),
+ 404 => emqx_dashboard_swagger:error_codes(
+ [?NOT_FOUND], <<"ClientID Not Found">>
+ )
+ }
+ }
+ };
schema("/authorization/sources/built_in_database/all") ->
- #{ 'operationId' => all
- , get =>
- #{ tags => [<<"authorization">>]
- , description => <<"Show the list of rules for all">>
- , responses =>
- #{200 => swagger_with_example({rules, ?TYPE_REF}, {all, ?PUT_MAP_EXAMPLE})}
+ #{
+ 'operationId' => all,
+ get =>
+ #{
+ tags => [<<"authorization">>],
+ description => <<"Show the list of rules for all">>,
+ responses =>
+ #{200 => swagger_with_example({rules, ?TYPE_REF}, {all, ?PUT_MAP_EXAMPLE})}
+ },
+ post =>
+ #{
+ tags => [<<"authorization">>],
+ description => <<
+ "Create/Update the list of rules for all. "
+ "Set a empty list to clean up rules"
+ >>,
+ 'requestBody' =>
+ swagger_with_example({rules, ?TYPE_REF}, {all, ?PUT_MAP_EXAMPLE}),
+ responses =>
+ #{
+ 204 => <<"Updated">>,
+ 400 => emqx_dashboard_swagger:error_codes(
+ [?BAD_REQUEST], <<"Bad rule schema">>
+ )
+ }
}
- , post =>
- #{ tags => [<<"authorization">>]
- , description => <<"Create/Update the list of rules for all. "
- "Set a empty list to clean up rules">>
- , 'requestBody' =>
- swagger_with_example({rules, ?TYPE_REF}, {all, ?PUT_MAP_EXAMPLE})
- , responses =>
- #{ 204 => <<"Updated">>
- , 400 => emqx_dashboard_swagger:error_codes(
- [?BAD_REQUEST], <<"Bad rule schema">>)
- }
- }
- };
+ };
schema("/authorization/sources/built_in_database/purge-all") ->
- #{ 'operationId' => purge
- , delete =>
- #{ tags => [<<"authorization">>]
- , description => <<"Purge all records">>
- , responses =>
- #{ 204 => <<"Deleted">>
- , 400 => emqx_dashboard_swagger:error_codes(
- [?BAD_REQUEST], <<"Bad Request">>)
- }
+ #{
+ 'operationId' => purge,
+ delete =>
+ #{
+ tags => [<<"authorization">>],
+ description => <<"Purge all records">>,
+ responses =>
+ #{
+ 204 => <<"Deleted">>,
+ 400 => emqx_dashboard_swagger:error_codes(
+ [?BAD_REQUEST], <<"Bad Request">>
+ )
+ }
}
- }.
+ }.
fields(rule_item) ->
- [ {topic, mk(string(),
- #{ required => true
- , desc => <<"Rule on specific topic">>
- , example => <<"test/topic/1">>
- })}
- , {permission, mk(enum([allow, deny]),
- #{ desc => <<"Permission">>
- , required => true
- , example => allow
- })}
- , {action, mk(enum([publish, subscribe, all]),
- #{ required => true
- , example => publish
- , desc => <<"Authorized action">>
- })}
+ [
+ {topic,
+ mk(
+ string(),
+ #{
+ required => true,
+ desc => <<"Rule on specific topic">>,
+ example => <<"test/topic/1">>
+ }
+ )},
+ {permission,
+ mk(
+ enum([allow, deny]),
+ #{
+ desc => <<"Permission">>,
+ required => true,
+ example => allow
+ }
+ )},
+ {action,
+ mk(
+ enum([publish, subscribe, all]),
+ #{
+ required => true,
+ example => publish,
+ desc => <<"Authorized action">>
+ }
+ )}
];
fields(clientid) ->
- [ {clientid, mk(binary(),
- #{ in => path
- , required => true
- , desc => <<"ClientID">>
- , example => <<"client1">>
- })}
+ [
+ {clientid,
+ mk(
+ binary(),
+ #{
+ in => path,
+ required => true,
+ desc => <<"ClientID">>,
+ example => <<"client1">>
+ }
+ )}
];
fields(username) ->
- [ {username, mk(binary(),
- #{ in => path
- , required => true
- , desc => <<"Username">>
- , example => <<"user1">>})}
+ [
+ {username,
+ mk(
+ binary(),
+ #{
+ in => path,
+ required => true,
+ desc => <<"Username">>,
+ example => <<"user1">>
+ }
+ )}
];
fields(rules_for_username) ->
- fields(rules)
- ++ fields(username);
+ fields(rules) ++
+ fields(username);
fields(username_response_data) ->
- [ {data, mk(array(ref(rules_for_username)), #{})}
- , {meta, ref(meta)}
+ [
+ {data, mk(array(ref(rules_for_username)), #{})},
+ {meta, ref(meta)}
];
fields(rules_for_clientid) ->
- fields(rules)
- ++ fields(clientid);
+ fields(rules) ++
+ fields(clientid);
fields(clientid_response_data) ->
- [ {data, mk(array(ref(rules_for_clientid)), #{})}
- , {meta, ref(meta)}
+ [
+ {data, mk(array(ref(rules_for_clientid)), #{})},
+ {meta, ref(meta)}
];
fields(rules) ->
[{rules, mk(array(ref(rule_item)))}];
fields(meta) ->
- emqx_dashboard_swagger:fields(page)
- ++ emqx_dashboard_swagger:fields(limit)
- ++ [{count, mk(integer(), #{example => 1})}].
+ emqx_dashboard_swagger:fields(page) ++
+ emqx_dashboard_swagger:fields(limit) ++
+ [{count, mk(integer(), #{example => 1})}].
%%--------------------------------------------------------------------
%% HTTP API
%%--------------------------------------------------------------------
users(get, #{query_string := QueryString}) ->
- Response = emqx_mgmt_api:node_query(node(), QueryString,
- ?ACL_TABLE, ?ACL_USERNAME_QSCHEMA, ?QUERY_USERNAME_FUN),
+ Response = emqx_mgmt_api:node_query(
+ node(),
+ QueryString,
+ ?ACL_TABLE,
+ ?ACL_USERNAME_QSCHEMA,
+ ?QUERY_USERNAME_FUN
+ ),
emqx_mgmt_util:generate_response(Response);
users(post, #{body := Body}) when is_list(Body) ->
- lists:foreach(fun(#{<<"username">> := Username, <<"rules">> := Rules}) ->
- emqx_authz_mnesia:store_rules({username, Username}, format_rules(Rules))
- end, Body),
+ lists:foreach(
+ fun(#{<<"username">> := Username, <<"rules">> := Rules}) ->
+ emqx_authz_mnesia:store_rules({username, Username}, format_rules(Rules))
+ end,
+ Body
+ ),
{204}.
clients(get, #{query_string := QueryString}) ->
- Response = emqx_mgmt_api:node_query(node(), QueryString,
- ?ACL_TABLE, ?ACL_CLIENTID_QSCHEMA, ?QUERY_CLIENTID_FUN),
+ Response = emqx_mgmt_api:node_query(
+ node(),
+ QueryString,
+ ?ACL_TABLE,
+ ?ACL_CLIENTID_QSCHEMA,
+ ?QUERY_CLIENTID_FUN
+ ),
emqx_mgmt_util:generate_response(Response);
clients(post, #{body := Body}) when is_list(Body) ->
- lists:foreach(fun(#{<<"clientid">> := ClientID, <<"rules">> := Rules}) ->
- emqx_authz_mnesia:store_rules({clientid, ClientID}, format_rules(Rules))
- end, Body),
+ lists:foreach(
+ fun(#{<<"clientid">> := ClientID, <<"rules">> := Rules}) ->
+ emqx_authz_mnesia:store_rules({clientid, ClientID}, format_rules(Rules))
+ end,
+ Body
+ ),
{204}.
user(get, #{bindings := #{username := Username}}) ->
case emqx_authz_mnesia:get_rules({username, Username}) of
- not_found -> {404, #{code => <<"NOT_FOUND">>, message => <<"Not Found">>}};
+ not_found ->
+ {404, #{code => <<"NOT_FOUND">>, message => <<"Not Found">>}};
{ok, Rules} ->
- {200, #{username => Username,
- rules => [ #{topic => Topic,
- action => Action,
- permission => Permission
- } || {Permission, Action, Topic} <- Rules]}
- }
+ {200, #{
+ username => Username,
+ rules => [
+ #{
+ topic => Topic,
+ action => Action,
+ permission => Permission
+ }
+ || {Permission, Action, Topic} <- Rules
+ ]
+ }}
end;
-user(put, #{bindings := #{username := Username},
- body := #{<<"username">> := Username, <<"rules">> := Rules}}) ->
+user(put, #{
+ bindings := #{username := Username},
+ body := #{<<"username">> := Username, <<"rules">> := Rules}
+}) ->
emqx_authz_mnesia:store_rules({username, Username}, format_rules(Rules)),
{204};
user(delete, #{bindings := #{username := Username}}) ->
@@ -351,17 +474,25 @@ user(delete, #{bindings := #{username := Username}}) ->
client(get, #{bindings := #{clientid := ClientID}}) ->
case emqx_authz_mnesia:get_rules({clientid, ClientID}) of
- not_found -> {404, #{code => <<"NOT_FOUND">>, message => <<"Not Found">>}};
+ not_found ->
+ {404, #{code => <<"NOT_FOUND">>, message => <<"Not Found">>}};
{ok, Rules} ->
- {200, #{clientid => ClientID,
- rules => [ #{topic => Topic,
- action => Action,
- permission => Permission
- } || {Permission, Action, Topic} <- Rules]}
- }
+ {200, #{
+ clientid => ClientID,
+ rules => [
+ #{
+ topic => Topic,
+ action => Action,
+ permission => Permission
+ }
+ || {Permission, Action, Topic} <- Rules
+ ]
+ }}
end;
-client(put, #{bindings := #{clientid := ClientID},
- body := #{<<"clientid">> := ClientID, <<"rules">> := Rules}}) ->
+client(put, #{
+ bindings := #{clientid := ClientID},
+ body := #{<<"clientid">> := ClientID, <<"rules">> := Rules}
+}) ->
emqx_authz_mnesia:store_rules({clientid, ClientID}, format_rules(Rules)),
{204};
client(delete, #{bindings := #{clientid := ClientID}}) ->
@@ -378,11 +509,16 @@ all(get, _) ->
not_found ->
{200, #{rules => []}};
{ok, Rules} ->
- {200, #{rules => [ #{topic => Topic,
- action => Action,
- permission => Permission
- } || {Permission, Action, Topic} <- Rules]}
- }
+ {200, #{
+ rules => [
+ #{
+ topic => Topic,
+ action => Action,
+ permission => Permission
+ }
+ || {Permission, Action, Topic} <- Rules
+ ]
+ }}
end;
all(post, #{body := #{<<"rules">> := Rules}}) ->
emqx_authz_mnesia:store_rules(all, format_rules(Rules)),
@@ -394,13 +530,16 @@ purge(delete, _) ->
ok = emqx_authz_mnesia:purge_rules(),
{204};
[#{<<"enable">> := true}] ->
- {400, #{code => <<"BAD_REQUEST">>,
- message =>
- <<"'built_in_database' type source must be disabled before purge.">>}};
+ {400, #{
+ code => <<"BAD_REQUEST">>,
+ message =>
+ <<"'built_in_database' type source must be disabled before purge.">>
+ }};
[] ->
- {404, #{code => <<"BAD_REQUEST">>,
- message => <<"'built_in_database' type source is not found.">>
- }}
+ {404, #{
+ code => <<"BAD_REQUEST">>,
+ message => <<"'built_in_database' type source is not found.">>
+ }}
end.
%%--------------------------------------------------------------------
@@ -408,25 +547,43 @@ purge(delete, _) ->
query_username(Tab, {_QString, []}, Continuation, Limit) ->
Ms = emqx_authz_mnesia:list_username_rules(),
- emqx_mgmt_api:select_table_with_count(Tab, Ms, Continuation, Limit,
- fun format_result/1);
-
+ emqx_mgmt_api:select_table_with_count(
+ Tab,
+ Ms,
+ Continuation,
+ Limit,
+ fun format_result/1
+ );
query_username(Tab, {_QString, FuzzyQString}, Continuation, Limit) ->
Ms = emqx_authz_mnesia:list_username_rules(),
FuzzyFilterFun = fuzzy_filter_fun(FuzzyQString),
- emqx_mgmt_api:select_table_with_count(Tab, {Ms, FuzzyFilterFun}, Continuation, Limit,
- fun format_result/1).
+ emqx_mgmt_api:select_table_with_count(
+ Tab,
+ {Ms, FuzzyFilterFun},
+ Continuation,
+ Limit,
+ fun format_result/1
+ ).
query_clientid(Tab, {_QString, []}, Continuation, Limit) ->
Ms = emqx_authz_mnesia:list_clientid_rules(),
- emqx_mgmt_api:select_table_with_count(Tab, Ms, Continuation, Limit,
- fun format_result/1);
-
+ emqx_mgmt_api:select_table_with_count(
+ Tab,
+ Ms,
+ Continuation,
+ Limit,
+ fun format_result/1
+ );
query_clientid(Tab, {_QString, FuzzyQString}, Continuation, Limit) ->
Ms = emqx_authz_mnesia:list_clientid_rules(),
FuzzyFilterFun = fuzzy_filter_fun(FuzzyQString),
- emqx_mgmt_api:select_table_with_count(Tab, {Ms, FuzzyFilterFun}, Continuation, Limit,
- fun format_result/1).
+ emqx_mgmt_api:select_table_with_count(
+ Tab,
+ {Ms, FuzzyFilterFun},
+ Continuation,
+ Limit,
+ fun format_result/1
+ ).
%%--------------------------------------------------------------------
%% Match funcs
@@ -434,17 +591,23 @@ query_clientid(Tab, {_QString, FuzzyQString}, Continuation, Limit) ->
%% Fuzzy username funcs
fuzzy_filter_fun(Fuzzy) ->
fun(MsRaws) when is_list(MsRaws) ->
- lists:filter( fun(E) -> run_fuzzy_filter(E, Fuzzy) end
- , MsRaws)
+ lists:filter(
+ fun(E) -> run_fuzzy_filter(E, Fuzzy) end,
+ MsRaws
+ )
end.
run_fuzzy_filter(_, []) ->
true;
-run_fuzzy_filter( E = [{username, Username}, _Rule]
- , [{username, like, UsernameSubStr} | Fuzzy]) ->
+run_fuzzy_filter(
+ E = [{username, Username}, _Rule],
+ [{username, like, UsernameSubStr} | Fuzzy]
+) ->
binary:match(Username, UsernameSubStr) /= nomatch andalso run_fuzzy_filter(E, Fuzzy);
-run_fuzzy_filter( E = [{clientid, ClientId}, _Rule]
- , [{clientid, like, ClientIdSubStr} | Fuzzy]) ->
+run_fuzzy_filter(
+ E = [{clientid, ClientId}, _Rule],
+ [{clientid, like, ClientIdSubStr} | Fuzzy]
+) ->
binary:match(ClientId, ClientIdSubStr) /= nomatch andalso run_fuzzy_filter(E, Fuzzy).
%%--------------------------------------------------------------------
@@ -452,31 +615,52 @@ run_fuzzy_filter( E = [{clientid, ClientId}, _Rule]
%% format rule from api
format_rules(Rules) when is_list(Rules) ->
- lists:foldl(fun(#{<<"topic">> := Topic,
- <<"action">> := Action,
- <<"permission">> := Permission
- }, AccIn) when ?PUBSUB(Action)
- andalso ?ALLOW_DENY(Permission) ->
- AccIn ++ [{ atom(Permission), atom(Action), Topic }]
- end, [], Rules).
+ lists:foldl(
+ fun(
+ #{
+ <<"topic">> := Topic,
+ <<"action">> := Action,
+ <<"permission">> := Permission
+ },
+ AccIn
+ ) when
+ ?PUBSUB(Action) andalso
+ ?ALLOW_DENY(Permission)
+ ->
+ AccIn ++ [{atom(Permission), atom(Action), Topic}]
+ end,
+ [],
+ Rules
+ ).
%% format result from mnesia tab
format_result([{username, Username}, {rules, Rules}]) ->
- #{username => Username,
- rules => [ #{topic => Topic,
- action => Action,
- permission => Permission
- } || {Permission, Action, Topic} <- Rules]
- };
+ #{
+ username => Username,
+ rules => [
+ #{
+ topic => Topic,
+ action => Action,
+ permission => Permission
+ }
+ || {Permission, Action, Topic} <- Rules
+ ]
+ };
format_result([{clientid, ClientID}, {rules, Rules}]) ->
- #{clientid => ClientID,
- rules => [ #{topic => Topic,
- action => Action,
- permission => Permission
- } || {Permission, Action, Topic} <- Rules]
- }.
+ #{
+ clientid => ClientID,
+ rules => [
+ #{
+ topic => Topic,
+ action => Action,
+ permission => Permission
+ }
+ || {Permission, Action, Topic} <- Rules
+ ]
+ }.
atom(B) when is_binary(B) ->
- try binary_to_existing_atom(B, utf8)
+ try
+ binary_to_existing_atom(B, utf8)
catch
_Error:_Expection -> binary_to_atom(B)
end;
@@ -488,25 +672,27 @@ atom(A) when is_atom(A) -> A.
swagger_with_example({Ref, TypeP}, {_Name, _Type} = Example) ->
emqx_dashboard_swagger:schema_with_examples(
- case TypeP of
- ?TYPE_REF -> ref(?MODULE, Ref);
- ?TYPE_ARRAY -> array(ref(?MODULE, Ref))
- end,
- rules_example(Example)).
+ case TypeP of
+ ?TYPE_REF -> ref(?MODULE, Ref);
+ ?TYPE_ARRAY -> array(ref(?MODULE, Ref))
+ end,
+ rules_example(Example)
+ ).
rules_example({ExampleName, ExampleType}) ->
{Summary, Example} =
case ExampleName of
username -> {<<"Username">>, ?USERNAME_RULES_EXAMPLE};
clientid -> {<<"ClientID">>, ?CLIENTID_RULES_EXAMPLE};
- all -> {<<"All">>, ?ALL_RULES_EXAMPLE}
+ all -> {<<"All">>, ?ALL_RULES_EXAMPLE}
end,
Value =
case ExampleType of
- ?PAGE_QUERY_EXAMPLE -> #{
- data => [Example],
- meta => ?META_EXAMPLE
- };
+ ?PAGE_QUERY_EXAMPLE ->
+ #{
+ data => [Example],
+ meta => ?META_EXAMPLE
+ };
?PUT_MAP_EXAMPLE ->
Example;
?POST_ARRAY_EXAMPLE ->
@@ -515,6 +701,6 @@ rules_example({ExampleName, ExampleType}) ->
#{
'password_based:built_in_database' => #{
summary => Summary,
- value => Value
+ value => Value
}
}.
diff --git a/apps/emqx_authz/src/emqx_authz_api_schema.erl b/apps/emqx_authz/src/emqx_authz_api_schema.erl
index 35822bd52..78c8539ae 100644
--- a/apps/emqx_authz/src/emqx_authz_api_schema.erl
+++ b/apps/emqx_authz/src/emqx_authz_api_schema.erl
@@ -25,58 +25,77 @@
-export([fields/1, authz_sources_types/1]).
fields(http) ->
- authz_common_fields(http)
- ++ [ {url, fun url/1}
- , {method, #{ type => enum([get, post])
- , default => get}}
- , {headers, fun headers/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))));
+ authz_common_fields(http) ++
+ [
+ {url, fun url/1},
+ {method, #{
+ type => enum([get, post]),
+ default => get
+ }},
+ {headers, fun headers/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))
+ )
+ );
fields('built_in_database') ->
authz_common_fields('built_in_database');
fields(mongo_single) ->
- authz_mongo_common_fields()
- ++ emqx_connector_mongo:fields(single);
+ authz_mongo_common_fields() ++
+ emqx_connector_mongo:fields(single);
fields(mongo_rs) ->
- authz_mongo_common_fields()
- ++ emqx_connector_mongo:fields(rs);
+ authz_mongo_common_fields() ++
+ emqx_connector_mongo:fields(rs);
fields(mongo_sharded) ->
- authz_mongo_common_fields()
- ++ emqx_connector_mongo:fields(sharded);
+ authz_mongo_common_fields() ++
+ emqx_connector_mongo:fields(sharded);
fields(mysql) ->
- authz_common_fields(mysql)
- ++ [ {query, #{type => binary()}}]
- ++ emqx_connector_mysql:fields(config);
+ authz_common_fields(mysql) ++
+ [{query, #{type => binary()}}] ++
+ emqx_connector_mysql:fields(config);
fields(postgresql) ->
- authz_common_fields(postgresql)
- ++ [ {query, #{type => binary()}}]
- ++ proplists:delete(named_queries, emqx_connector_pgsql:fields(config));
+ authz_common_fields(postgresql) ++
+ [{query, #{type => binary()}}] ++
+ proplists:delete(named_queries, emqx_connector_pgsql:fields(config));
fields(redis_single) ->
- authz_redis_common_fields()
- ++ emqx_connector_redis:fields(single);
+ authz_redis_common_fields() ++
+ emqx_connector_redis:fields(single);
fields(redis_sentinel) ->
- authz_redis_common_fields()
- ++ emqx_connector_redis:fields(sentinel);
+ authz_redis_common_fields() ++
+ emqx_connector_redis:fields(sentinel);
fields(redis_cluster) ->
- authz_redis_common_fields()
- ++ emqx_connector_redis:fields(cluster);
+ authz_redis_common_fields() ++
+ emqx_connector_redis:fields(cluster);
fields(file) ->
- authz_common_fields(file)
- ++ [ { rules, #{ type => binary()
- , required => true
- , example =>
- <<"{allow,{username,\"^dashboard?\"},","subscribe,[\"$SYS/#\"]}.\n",
- "{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}.">>}}
- ];
+ authz_common_fields(file) ++
+ [
+ {rules, #{
+ type => binary(),
+ required => true,
+ example =>
+ <<"{allow,{username,\"^dashboard?\"},", "subscribe,[\"$SYS/#\"]}.\n",
+ "{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}.">>
+ }}
+ ];
fields(position) ->
- [ { position
- , mk( string()
- , #{ desc => <<"Where to place the source">>
- , required => true
- , in => body})}].
+ [
+ {position,
+ mk(
+ string(),
+ #{
+ desc => <<"Where to place the source">>,
+ required => true,
+ in => body
+ }
+ )}
+ ].
%%------------------------------------------------------------------------------
%% http type funcs
@@ -86,41 +105,52 @@ url(validator) -> [?NOT_EMPTY("the value of the field 'url' cannot be empty")];
url(required) -> true;
url(_) -> undefined.
-headers(type) -> map();
+headers(type) ->
+ map();
headers(converter) ->
fun(Headers) ->
- maps:merge(default_headers(), transform_header_name(Headers))
+ maps:merge(default_headers(), transform_header_name(Headers))
end;
-headers(default) -> default_headers();
-headers(_) -> undefined.
+headers(default) ->
+ default_headers();
+headers(_) ->
+ undefined.
%% headers
default_headers() ->
- maps:put(<<"content-type">>,
- <<"application/json">>,
- default_headers_no_content_type()).
+ maps:put(
+ <<"content-type">>,
+ <<"application/json">>,
+ default_headers_no_content_type()
+ ).
default_headers_no_content_type() ->
- #{ <<"accept">> => <<"application/json">>
- , <<"cache-control">> => <<"no-cache">>
- , <<"connection">> => <<"keep-alive">>
- , <<"keep-alive">> => <<"timeout=30, max=1000">>
- }.
+ #{
+ <<"accept">> => <<"application/json">>,
+ <<"cache-control">> => <<"no-cache">>,
+ <<"connection">> => <<"keep-alive">>,
+ <<"keep-alive">> => <<"timeout=30, max=1000">>
+ }.
transform_header_name(Headers) ->
- maps:fold(fun(K0, V, Acc) ->
- K = list_to_binary(string:to_lower(to_list(K0))),
- maps:put(K, V, Acc)
- end, #{}, Headers).
+ maps:fold(
+ fun(K0, V, Acc) ->
+ K = list_to_binary(string:to_lower(to_list(K0))),
+ maps:put(K, V, Acc)
+ end,
+ #{},
+ Headers
+ ).
%%------------------------------------------------------------------------------
%% MonogDB type funcs
authz_mongo_common_fields() ->
authz_common_fields(mongodb) ++
- [ {collection, fun collection/1}
- , {selector, fun selector/1}
- ].
+ [
+ {collection, fun collection/1},
+ {selector, fun selector/1}
+ ].
collection(type) -> binary();
collection(_) -> undefined.
@@ -133,19 +163,24 @@ selector(_) -> undefined.
authz_redis_common_fields() ->
authz_common_fields(redis) ++
- [ {cmd, #{ type => binary()
- , example => <<"HGETALL mqtt_authz">>}}].
+ [
+ {cmd, #{
+ type => binary(),
+ example => <<"HGETALL mqtt_authz">>
+ }}
+ ].
%%------------------------------------------------------------------------------
%% Authz api type funcs
-authz_common_fields(Type) when is_atom(Type)->
- [ {enable, fun enable/1}
- , {type, #{ type => enum([Type])
- , default => Type
- , in => body
- }
- }
+authz_common_fields(Type) when is_atom(Type) ->
+ [
+ {enable, fun enable/1},
+ {type, #{
+ type => enum([Type]),
+ default => Type,
+ in => body
+ }}
].
enable(type) -> boolean();
@@ -158,20 +193,25 @@ enable(_) -> undefined.
authz_sources_types(Type) ->
case Type of
- simple -> [mongodb, redis];
- detailed -> [ mongo_single
- , mongo_rs
- , mongo_sharded
- , redis_single
- , redis_sentinel
- , redis_cluster]
- end
- ++
- [ http
- , 'built_in_database'
- , mysql
- , postgresql
- , file].
+ simple ->
+ [mongodb, redis];
+ detailed ->
+ [
+ mongo_single,
+ mongo_rs,
+ mongo_sharded,
+ redis_single,
+ redis_sentinel,
+ redis_cluster
+ ]
+ end ++
+ [
+ http,
+ 'built_in_database',
+ mysql,
+ postgresql,
+ file
+ ].
to_list(A) when is_atom(A) ->
atom_to_list(A);
diff --git a/apps/emqx_authz/src/emqx_authz_api_settings.erl b/apps/emqx_authz/src/emqx_authz_api_settings.erl
index b4b2ab5cc..cd00d131e 100644
--- a/apps/emqx_authz/src/emqx_authz_api_settings.erl
+++ b/apps/emqx_authz/src/emqx_authz_api_settings.erl
@@ -20,10 +20,11 @@
-import(hoconsc, [mk/1, ref/2]).
--export([ api_spec/0
- , paths/0
- , schema/1
- ]).
+-export([
+ api_spec/0,
+ paths/0,
+ schema/1
+]).
-export([settings/2]).
@@ -40,33 +41,42 @@ paths() ->
%%--------------------------------------------------------------------
schema("/authorization/settings") ->
- #{ 'operationId' => settings
- , get =>
- #{ description => <<"Get authorization settings">>
- , responses =>
- #{200 => ref_authz_schema()}
+ #{
+ 'operationId' => settings,
+ get =>
+ #{
+ description => <<"Get authorization settings">>,
+ responses =>
+ #{200 => ref_authz_schema()}
+ },
+ put =>
+ #{
+ description => <<"Update authorization settings">>,
+ 'requestBody' => ref_authz_schema(),
+ responses =>
+ #{
+ 200 => ref_authz_schema(),
+ 400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Bad Request">>)
+ }
}
- , put =>
- #{ description => <<"Update authorization settings">>
- , 'requestBody' => ref_authz_schema()
- , responses =>
- #{ 200 => ref_authz_schema()
- , 400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Bad Request">>)}
- }
- }.
+ }.
ref_authz_schema() ->
proplists:delete(sources, emqx_conf_schema:fields("authorization")).
settings(get, _Params) ->
{200, authorization_settings()};
-
-settings(put, #{body := #{<<"no_match">> := NoMatch,
- <<"deny_action">> := DenyAction,
- <<"cache">> := Cache}}) ->
+settings(put, #{
+ body := #{
+ <<"no_match">> := NoMatch,
+ <<"deny_action">> := DenyAction,
+ <<"cache">> := Cache
+ }
+}) ->
{ok, _} = emqx_authz_utils:update_config([authorization, no_match], NoMatch),
{ok, _} = emqx_authz_utils:update_config(
- [authorization, deny_action], DenyAction),
+ [authorization, deny_action], DenyAction
+ ),
{ok, _} = emqx_authz_utils:update_config([authorization, cache], Cache),
ok = emqx_authz_cache:drain_cache(),
{200, authorization_settings()}.
diff --git a/apps/emqx_authz/src/emqx_authz_api_sources.erl b/apps/emqx_authz/src/emqx_authz_api_sources.erl
index 22a4f0f86..759fd92f8 100644
--- a/apps/emqx_authz/src/emqx_authz_api_sources.erl
+++ b/apps/emqx_authz/src/emqx_authz_api_sources.erl
@@ -29,167 +29,226 @@
-define(API_SCHEMA_MODULE, emqx_authz_api_schema).
--export([ get_raw_sources/0
- , get_raw_source/1
- , source_status/2
- , lookup_from_local_node/1
- , lookup_from_all_nodes/1
- ]).
+-export([
+ get_raw_sources/0,
+ get_raw_source/1,
+ source_status/2,
+ lookup_from_local_node/1,
+ lookup_from_all_nodes/1
+]).
--export([ api_spec/0
- , paths/0
- , schema/1
- ]).
+-export([
+ api_spec/0,
+ paths/0,
+ schema/1
+]).
--export([ sources/2
- , source/2
- , move_source/2
- ]).
+-export([
+ sources/2,
+ source/2,
+ move_source/2
+]).
api_spec() ->
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}).
paths() ->
- [ "/authorization/sources"
- , "/authorization/sources/:type"
- , "/authorization/sources/:type/status"
- , "/authorization/sources/:type/move"].
+ [
+ "/authorization/sources",
+ "/authorization/sources/:type",
+ "/authorization/sources/:type/status",
+ "/authorization/sources/:type/move"
+ ].
%%--------------------------------------------------------------------
%% Schema for each URI
%%--------------------------------------------------------------------
schema("/authorization/sources") ->
- #{ 'operationId' => sources
- , get =>
- #{ description => <<"List all authorization sources">>
- , responses =>
- #{ 200 => mk( array(hoconsc:union(authz_sources_type_refs()))
- , #{desc => <<"Authorization source">>})
- }
+ #{
+ 'operationId' => sources,
+ get =>
+ #{
+ description => <<"List all authorization sources">>,
+ responses =>
+ #{
+ 200 => mk(
+ array(hoconsc:union(authz_sources_type_refs())),
+ #{desc => <<"Authorization source">>}
+ )
+ }
+ },
+ post =>
+ #{
+ description => <<"Add a new source">>,
+ 'requestBody' => mk(
+ hoconsc:union(authz_sources_type_refs()),
+ #{desc => <<"Source config">>}
+ ),
+ responses =>
+ #{
+ 204 => <<"Authorization source created successfully">>,
+ 400 => emqx_dashboard_swagger:error_codes(
+ [?BAD_REQUEST],
+ <<"Bad Request">>
+ )
+ }
}
- , post =>
- #{ description => <<"Add a new source">>
- , 'requestBody' => mk( hoconsc:union(authz_sources_type_refs())
- , #{desc => <<"Source config">>})
- , responses =>
- #{ 204 => <<"Authorization source created successfully">>
- , 400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST],
- <<"Bad Request">>)
- }
- }
- };
+ };
schema("/authorization/sources/:type") ->
- #{ 'operationId' => source
- , get =>
- #{ description => <<"Get a authorization source">>
- , parameters => parameters_field()
- , responses =>
- #{ 200 => mk( hoconsc:union(authz_sources_type_refs())
- , #{desc => <<"Authorization source">>})
- , 404 => emqx_dashboard_swagger:error_codes([?NOT_FOUND], <<"Not Found">>)
- }
+ #{
+ 'operationId' => source,
+ get =>
+ #{
+ description => <<"Get a authorization source">>,
+ parameters => parameters_field(),
+ responses =>
+ #{
+ 200 => mk(
+ hoconsc:union(authz_sources_type_refs()),
+ #{desc => <<"Authorization source">>}
+ ),
+ 404 => emqx_dashboard_swagger:error_codes([?NOT_FOUND], <<"Not Found">>)
+ }
+ },
+ put =>
+ #{
+ description => <<"Update source">>,
+ parameters => parameters_field(),
+ 'requestBody' => mk(hoconsc:union(authz_sources_type_refs())),
+ responses =>
+ #{
+ 204 => <<"Authorization source updated successfully">>,
+ 400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Bad Request">>)
+ }
+ },
+ delete =>
+ #{
+ description => <<"Delete source">>,
+ parameters => parameters_field(),
+ responses =>
+ #{
+ 204 => <<"Deleted successfully">>,
+ 400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Bad Request">>)
+ }
}
- , put =>
- #{ description => <<"Update source">>
- , parameters => parameters_field()
- , 'requestBody' => mk(hoconsc:union(authz_sources_type_refs()))
- , responses =>
- #{ 204 => <<"Authorization source updated successfully">>
- , 400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Bad Request">>)
- }
- }
- , delete =>
- #{ description => <<"Delete source">>
- , parameters => parameters_field()
- , responses =>
- #{ 204 => <<"Deleted successfully">>
- , 400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Bad Request">>)
- }
- }
- };
+ };
schema("/authorization/sources/:type/status") ->
- #{ 'operationId' => source_status
- , get =>
- #{ description => <<"Get a authorization source">>
- , parameters => parameters_field()
- , responses =>
- #{ 200 => emqx_dashboard_swagger:schema_with_examples(
- hoconsc:ref(emqx_authn_schema, "metrics_status_fields"),
- status_metrics_example())
- , 400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Bad request">>)
- , 404 => emqx_dashboard_swagger:error_codes([?NOT_FOUND], <<"Not Found">>)
- }
+ #{
+ 'operationId' => source_status,
+ get =>
+ #{
+ description => <<"Get a authorization source">>,
+ parameters => parameters_field(),
+ responses =>
+ #{
+ 200 => emqx_dashboard_swagger:schema_with_examples(
+ hoconsc:ref(emqx_authn_schema, "metrics_status_fields"),
+ status_metrics_example()
+ ),
+ 400 => emqx_dashboard_swagger:error_codes(
+ [?BAD_REQUEST], <<"Bad request">>
+ ),
+ 404 => emqx_dashboard_swagger:error_codes([?NOT_FOUND], <<"Not Found">>)
+ }
}
- };
+ };
schema("/authorization/sources/:type/move") ->
- #{ 'operationId' => move_source
- , post =>
- #{ description => <<"Change the order of sources">>
- , parameters => parameters_field()
- , 'requestBody' =>
- emqx_dashboard_swagger:schema_with_examples(
- ref(?API_SCHEMA_MODULE, position),
- position_example())
- , responses =>
- #{ 204 => <<"No Content">>
- , 400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Bad Request">>)
- , 404 => emqx_dashboard_swagger:error_codes([?NOT_FOUND], <<"Not Found">>)
- }
+ #{
+ 'operationId' => move_source,
+ post =>
+ #{
+ description => <<"Change the order of sources">>,
+ parameters => parameters_field(),
+ 'requestBody' =>
+ emqx_dashboard_swagger:schema_with_examples(
+ ref(?API_SCHEMA_MODULE, position),
+ position_example()
+ ),
+ responses =>
+ #{
+ 204 => <<"No Content">>,
+ 400 => emqx_dashboard_swagger:error_codes(
+ [?BAD_REQUEST], <<"Bad Request">>
+ ),
+ 404 => emqx_dashboard_swagger:error_codes([?NOT_FOUND], <<"Not Found">>)
+ }
}
- }.
-
+ }.
%%--------------------------------------------------------------------
%% Operation functions
%%--------------------------------------------------------------------
-sources(Method, #{bindings := #{type := Type} = Bindings } = Req)
- when is_atom(Type) ->
+sources(Method, #{bindings := #{type := Type} = Bindings} = Req) when
+ is_atom(Type)
+->
sources(Method, Req#{bindings => Bindings#{type => atom_to_binary(Type, utf8)}});
sources(get, _) ->
- Sources = lists:foldl(fun (#{<<"type">> := <<"file">>,
- <<"enable">> := Enable, <<"path">> := Path}, AccIn) ->
- case file:read_file(Path) of
- {ok, Rules} ->
- lists:append(AccIn, [#{type => file,
- enable => Enable,
- rules => Rules
- }]);
- {error, _} ->
- lists:append(AccIn, [#{type => file,
- enable => Enable,
- rules => <<"">>
- }])
- end;
- (Source, AccIn) ->
- lists:append(AccIn, [read_certs(Source)])
- end, [], get_raw_sources()),
+ Sources = lists:foldl(
+ fun
+ (
+ #{
+ <<"type">> := <<"file">>,
+ <<"enable">> := Enable,
+ <<"path">> := Path
+ },
+ AccIn
+ ) ->
+ case file:read_file(Path) of
+ {ok, Rules} ->
+ lists:append(AccIn, [
+ #{
+ type => file,
+ enable => Enable,
+ rules => Rules
+ }
+ ]);
+ {error, _} ->
+ lists:append(AccIn, [
+ #{
+ type => file,
+ enable => Enable,
+ rules => <<"">>
+ }
+ ])
+ end;
+ (Source, AccIn) ->
+ lists:append(AccIn, [read_certs(Source)])
+ end,
+ [],
+ get_raw_sources()
+ ),
{200, #{sources => Sources}};
sources(post, #{body := #{<<"type">> := <<"file">>} = Body}) ->
create_authz_file(Body);
sources(post, #{body := Body}) ->
update_config(?CMD_PREPEND, Body).
-source(Method, #{bindings := #{type := Type} = Bindings } = Req)
- when is_atom(Type) ->
+source(Method, #{bindings := #{type := Type} = Bindings} = Req) when
+ is_atom(Type)
+->
source(Method, Req#{bindings => Bindings#{type => atom_to_binary(Type, utf8)}});
source(get, #{bindings := #{type := Type}}) ->
case get_raw_source(Type) of
- [] -> {404, #{message => <<"Not found ", Type/binary>>}};
+ [] ->
+ {404, #{message => <<"Not found ", Type/binary>>}};
[#{<<"type">> := <<"file">>, <<"enable">> := Enable, <<"path">> := Path}] ->
case file:read_file(Path) of
{ok, Rules} ->
- {200, #{type => file,
- enable => Enable,
- rules => Rules
- }
- };
+ {200, #{
+ type => file,
+ enable => Enable,
+ rules => Rules
+ }};
{error, Reason} ->
- {500, #{code => <<"INTERNAL_ERROR">>,
- message => bin(Reason)}}
+ {500, #{
+ code => <<"INTERNAL_ERROR">>,
+ message => bin(Reason)
+ }}
end;
- [Source] -> {200, read_certs(Source)}
+ [Source] ->
+ {200, read_certs(Source)}
end;
source(put, #{bindings := #{type := <<"file">>}, body := #{<<"type">> := <<"file">>} = Body}) ->
update_authz_file(Body);
@@ -201,44 +260,61 @@ source(delete, #{bindings := #{type := Type}}) ->
source_status(get, #{bindings := #{type := Type}}) ->
BinType = atom_to_binary(Type, utf8),
case get_raw_source(BinType) of
- [] -> {404, #{code => <<"NOT_FOUND">>,
- message => <<"Not found", BinType/binary>>}};
+ [] ->
+ {404, #{
+ code => <<"NOT_FOUND">>,
+ message => <<"Not found", BinType/binary>>
+ }};
[#{<<"type">> := <<"file">>}] ->
- {400, #{code => <<"BAD_REQUEST">>,
- message => <<"Not Support Status">>}};
+ {400, #{
+ code => <<"BAD_REQUEST">>,
+ message => <<"Not Support Status">>
+ }};
[_] ->
case emqx_authz:lookup(Type) of
- #{annotations := #{id := ResourceId }} -> lookup_from_all_nodes(ResourceId);
+ #{annotations := #{id := ResourceId}} -> lookup_from_all_nodes(ResourceId);
_ -> {400, #{code => <<"BAD_REQUEST">>, message => <<"Resource Disable">>}}
end
end.
-move_source(Method, #{bindings := #{type := Type} = Bindings } = Req)
- when is_atom(Type) ->
+move_source(Method, #{bindings := #{type := Type} = Bindings} = Req) when
+ is_atom(Type)
+->
move_source(Method, Req#{bindings => Bindings#{type => atom_to_binary(Type, utf8)}});
move_source(post, #{bindings := #{type := Type}, body := #{<<"position">> := Position}}) ->
- case parse_position(Position) of
+ case parse_position(Position) of
{ok, NPosition} ->
try emqx_authz:move(Type, NPosition) of
- {ok, _} -> {204};
+ {ok, _} ->
+ {204};
{error, {not_found_source, _Type}} ->
- {404, #{code => <<"NOT_FOUND">>,
- message => <<"source ", Type/binary, " not found">>}};
+ {404, #{
+ code => <<"NOT_FOUND">>,
+ message => <<"source ", Type/binary, " not found">>
+ }};
{error, {emqx_conf_schema, _}} ->
- {400, #{code => <<"BAD_REQUEST">>,
- message => <<"BAD_SCHEMA">>}};
+ {400, #{
+ code => <<"BAD_REQUEST">>,
+ message => <<"BAD_SCHEMA">>
+ }};
{error, Reason} ->
- {400, #{code => <<"BAD_REQUEST">>,
- message => bin(Reason)}}
+ {400, #{
+ code => <<"BAD_REQUEST">>,
+ message => bin(Reason)
+ }}
catch
- error : {unknown_authz_source_type, Unknown} ->
+ error:{unknown_authz_source_type, Unknown} ->
NUnknown = bin(Unknown),
- {400, #{code => <<"BAD_REQUEST">>,
- message => <<"Unknown authz Source Type: ", NUnknown/binary>>}}
+ {400, #{
+ code => <<"BAD_REQUEST">>,
+ message => <<"Unknown authz Source Type: ", NUnknown/binary>>
+ }}
end;
{error, Reason} ->
- {400, #{code => <<"BAD_REQUEST">>,
- message => bin(Reason)}}
+ {400, #{
+ code => <<"BAD_REQUEST">>,
+ message => bin(Reason)
+ }}
end.
%%--------------------------------------------------------------------
@@ -249,46 +325,53 @@ lookup_from_local_node(ResourceId) ->
NodeId = node(self()),
case emqx_resource:get_instance(ResourceId) of
{error, not_found} -> {error, {NodeId, not_found_resource}};
- {ok, _, #{ status := Status, metrics := Metrics }} ->
- {ok, {NodeId, Status, Metrics}}
+ {ok, _, #{status := Status, metrics := Metrics}} -> {ok, {NodeId, Status, Metrics}}
end.
lookup_from_all_nodes(ResourceId) ->
Nodes = mria_mnesia:running_nodes(),
case is_ok(emqx_authz_proto_v1:lookup_from_all_nodes(Nodes, ResourceId)) of
{ok, ResList} ->
- {StatusMap, MetricsMap, _} = make_result_map(ResList),
- AggregateStatus = aggregate_status(maps:values(StatusMap)),
- AggregateMetrics = aggregate_metrics(maps:values(MetricsMap)),
- Fun = fun (_, V1) -> restructure_map(V1) end,
- MKMap = fun (Name) -> fun ({Key, Val}) -> #{ node => Key, Name => Val } end end,
- HelpFun = fun (M, Name) -> lists:map(MKMap(Name), maps:to_list(M)) end,
- case AggregateStatus of
- empty_metrics_and_status -> {400, #{code => <<"BAD_REQUEST">>,
- message => <<"Resource Not Support Status">>}};
- _ -> {200, #{node_status => HelpFun(StatusMap, status),
- node_metrics => HelpFun(maps:map(Fun, MetricsMap), metrics),
- status => AggregateStatus,
- metrics => restructure_map(AggregateMetrics)
- }
- }
- end;
+ {StatusMap, MetricsMap, _} = make_result_map(ResList),
+ AggregateStatus = aggregate_status(maps:values(StatusMap)),
+ AggregateMetrics = aggregate_metrics(maps:values(MetricsMap)),
+ Fun = fun(_, V1) -> restructure_map(V1) end,
+ MKMap = fun(Name) -> fun({Key, Val}) -> #{node => Key, Name => Val} end end,
+ HelpFun = fun(M, Name) -> lists:map(MKMap(Name), maps:to_list(M)) end,
+ case AggregateStatus of
+ empty_metrics_and_status ->
+ {400, #{
+ code => <<"BAD_REQUEST">>,
+ message => <<"Resource Not Support Status">>
+ }};
+ _ ->
+ {200, #{
+ node_status => HelpFun(StatusMap, status),
+ node_metrics => HelpFun(maps:map(Fun, MetricsMap), metrics),
+ status => AggregateStatus,
+ metrics => restructure_map(AggregateMetrics)
+ }}
+ end;
{error, ErrL} ->
- {500, #{code => <<"INTERNAL_ERROR">>,
- message => bin_t(io_lib:format("~p", [ErrL]))}}
+ {500, #{
+ code => <<"INTERNAL_ERROR">>,
+ message => bin_t(io_lib:format("~p", [ErrL]))
+ }}
end.
-aggregate_status([]) -> empty_metrics_and_status;
+aggregate_status([]) ->
+ empty_metrics_and_status;
aggregate_status(AllStatus) ->
- Head = fun ([A | _]) -> A end,
+ Head = fun([A | _]) -> A end,
HeadVal = Head(AllStatus),
- AllRes = lists:all(fun (Val) -> Val == HeadVal end, AllStatus),
+ AllRes = lists:all(fun(Val) -> Val == HeadVal end, AllStatus),
case AllRes of
true -> HeadVal;
false -> inconsistent
end.
-aggregate_metrics([]) -> empty_metrics_and_status;
+aggregate_metrics([]) ->
+ empty_metrics_and_status;
aggregate_metrics([HeadMetrics | AllMetrics]) ->
CombinerFun =
fun ComFun(Val1, Val2) ->
@@ -297,8 +380,9 @@ aggregate_metrics([HeadMetrics | AllMetrics]) ->
false -> Val1 + Val2
end
end,
- Fun = fun (ElemMap, AccMap) ->
- emqx_map_lib:merge_with(CombinerFun, ElemMap, AccMap) end,
+ Fun = fun(ElemMap, AccMap) ->
+ emqx_map_lib:merge_with(CombinerFun, ElemMap, AccMap)
+ end,
lists:foldl(Fun, HeadMetrics, AllMetrics).
make_result_map(ResList) ->
@@ -306,39 +390,45 @@ make_result_map(ResList) ->
fun(Elem, {StatusMap, MetricsMap, ErrorMap}) ->
case Elem of
{ok, {NodeId, Status, Metrics}} ->
- {maps:put(NodeId, Status, StatusMap),
- maps:put(NodeId, Metrics, MetricsMap),
- ErrorMap
+ {
+ maps:put(NodeId, Status, StatusMap),
+ maps:put(NodeId, Metrics, MetricsMap),
+ ErrorMap
};
{error, {NodeId, Reason}} ->
- {StatusMap,
- MetricsMap,
- maps:put(NodeId, Reason, ErrorMap)
- }
+ {StatusMap, MetricsMap, maps:put(NodeId, Reason, ErrorMap)}
end
end,
lists:foldl(Fun, {maps:new(), maps:new(), maps:new()}, ResList).
-restructure_map(#{counters := #{failed := Failed, matched := Match, success := Succ},
- rate := #{matched := #{current := Rate, last5m := Rate5m, max := RateMax}
- }
- }
- ) ->
- #{matched => Match,
- success => Succ,
- failed => Failed,
- rate => Rate,
- rate_last5m => Rate5m,
- rate_max => RateMax
- };
+restructure_map(#{
+ counters := #{failed := Failed, matched := Match, success := Succ},
+ rate := #{matched := #{current := Rate, last5m := Rate5m, max := RateMax}}
+}) ->
+ #{
+ matched => Match,
+ success => Succ,
+ failed => Failed,
+ rate => Rate,
+ rate_last5m => Rate5m,
+ rate_max => RateMax
+ };
restructure_map(Error) ->
- Error.
+ Error.
bin_t(S) when is_list(S) ->
list_to_binary(S).
is_ok(ResL) ->
- case lists:filter(fun({ok, _}) -> false; (_) -> true end, ResL) of
+ case
+ lists:filter(
+ fun
+ ({ok, _}) -> false;
+ (_) -> true
+ end,
+ ResL
+ )
+ of
[] -> {ok, [Res || {ok, Res} <- ResL]};
ErrL -> {error, ErrL}
end.
@@ -352,43 +442,60 @@ get_raw_sources() ->
merge_default_headers(Sources).
merge_default_headers(Sources) ->
- lists:map(fun(Source) ->
- case maps:find(<<"headers">>, Source) of
- {ok, Headers} ->
- NewHeaders =
- case Source of
- #{<<"method">> := <<"get">>} ->
- (emqx_authz_schema:headers_no_content_type(converter))(Headers);
- #{<<"method">> := <<"post">>} ->
- (emqx_authz_schema:headers(converter))(Headers);
- _ -> Headers
- end,
- Source#{<<"headers">> => NewHeaders};
- error -> Source
- end
- end, Sources).
+ lists:map(
+ fun(Source) ->
+ case maps:find(<<"headers">>, Source) of
+ {ok, Headers} ->
+ NewHeaders =
+ case Source of
+ #{<<"method">> := <<"get">>} ->
+ (emqx_authz_schema:headers_no_content_type(converter))(Headers);
+ #{<<"method">> := <<"post">>} ->
+ (emqx_authz_schema:headers(converter))(Headers);
+ _ ->
+ Headers
+ end,
+ Source#{<<"headers">> => NewHeaders};
+ error ->
+ Source
+ end
+ end,
+ Sources
+ ).
get_raw_source(Type) ->
- lists:filter(fun (#{<<"type">> := T}) ->
- T =:= Type
- end, get_raw_sources()).
+ lists:filter(
+ fun(#{<<"type">> := T}) ->
+ T =:= Type
+ end,
+ get_raw_sources()
+ ).
update_config(Cmd, Sources) ->
case emqx_authz:update(Cmd, Sources) of
- {ok, _} -> {204};
+ {ok, _} ->
+ {204};
{error, {pre_config_update, emqx_authz, Reason}} ->
- {400, #{code => <<"BAD_REQUEST">>,
- message => bin(Reason)}};
+ {400, #{
+ code => <<"BAD_REQUEST">>,
+ message => bin(Reason)
+ }};
{error, {post_config_update, emqx_authz, Reason}} ->
- {400, #{code => <<"BAD_REQUEST">>,
- message => bin(Reason)}};
+ {400, #{
+ code => <<"BAD_REQUEST">>,
+ message => bin(Reason)
+ }};
%% TODO: The `Reason` may cann't be trans to json term. (i.e. ecpool start failed)
{error, {emqx_conf_schema, _}} ->
- {400, #{code => <<"BAD_REQUEST">>,
- message => <<"BAD_SCHEMA">>}};
+ {400, #{
+ code => <<"BAD_REQUEST">>,
+ message => <<"BAD_SCHEMA">>
+ }};
{error, Reason} ->
- {400, #{code => <<"BAD_REQUEST">>,
- message => bin(Reason)}}
+ {400, #{
+ code => <<"BAD_REQUEST">>,
+ message => bin(Reason)
+ }}
end.
read_certs(#{<<"ssl">> := SSL} = Source) ->
@@ -399,12 +506,16 @@ read_certs(#{<<"ssl">> := SSL} = Source) ->
{ok, NewSSL} ->
Source#{<<"ssl">> => NewSSL}
end;
-read_certs(Source) -> Source.
+read_certs(Source) ->
+ Source.
parameters_field() ->
- [ {type, mk( enum(?API_SCHEMA_MODULE:authz_sources_types(simple))
- , #{in => path, desc => <<"Authorization type">>})
- }
+ [
+ {type,
+ mk(
+ enum(?API_SCHEMA_MODULE:authz_sources_types(simple)),
+ #{in => path, desc => <<"Authorization type">>}
+ )}
].
parse_position(<<"front">>) ->
@@ -423,50 +534,68 @@ parse_position(_) ->
{error, <<"Invalid parameter. Unknow position">>}.
position_example() ->
- #{ front =>
- #{ summary => <<"front example">>
- , value => #{<<"position">> => <<"front">>}}
- , rear =>
- #{ summary => <<"rear example">>
- , value => #{<<"position">> => <<"rear">>}}
- , relative_before =>
- #{ summary => <<"relative example">>
- , value => #{<<"position">> => <<"before:file">>}}
- , relative_after =>
- #{ summary => <<"relative example">>
- , value => #{<<"position">> => <<"after:file">>}}
- }.
+ #{
+ front =>
+ #{
+ summary => <<"front example">>,
+ value => #{<<"position">> => <<"front">>}
+ },
+ rear =>
+ #{
+ summary => <<"rear example">>,
+ value => #{<<"position">> => <<"rear">>}
+ },
+ relative_before =>
+ #{
+ summary => <<"relative example">>,
+ value => #{<<"position">> => <<"before:file">>}
+ },
+ relative_after =>
+ #{
+ summary => <<"relative example">>,
+ value => #{<<"position">> => <<"after:file">>}
+ }
+ }.
authz_sources_type_refs() ->
- [ref(?API_SCHEMA_MODULE, Type)
- || Type <- emqx_authz_api_schema:authz_sources_types(detailed)].
+ [
+ ref(?API_SCHEMA_MODULE, Type)
+ || Type <- emqx_authz_api_schema:authz_sources_types(detailed)
+ ].
bin(Term) -> erlang:iolist_to_binary(io_lib:format("~p", [Term])).
status_metrics_example() ->
- #{ metrics => #{ matched => 0,
- success => 0,
- failed => 0,
- rate => 0.0,
- rate_last5m => 0.0,
- rate_max => 0.0
- },
- node_metrics => [ #{node => node(),
- metrics => #{ matched => 0,
- success => 0,
- failed => 0,
- rate => 0.0,
- rate_last5m => 0.0,
- rate_max => 0.0
- }
- }
- ],
- status => connected,
- node_status => [ #{node => node(),
- status => connected
- }
- ]
- }.
+ #{
+ metrics => #{
+ matched => 0,
+ success => 0,
+ failed => 0,
+ rate => 0.0,
+ rate_last5m => 0.0,
+ rate_max => 0.0
+ },
+ node_metrics => [
+ #{
+ node => node(),
+ metrics => #{
+ matched => 0,
+ success => 0,
+ failed => 0,
+ rate => 0.0,
+ rate_last5m => 0.0,
+ rate_max => 0.0
+ }
+ }
+ ],
+ status => connected,
+ node_status => [
+ #{
+ node => node(),
+ status => connected
+ }
+ ]
+ }.
create_authz_file(Body) ->
do_update_authz_file(?CMD_PREPEND, Body).
@@ -476,4 +605,4 @@ update_authz_file(Body) ->
do_update_authz_file(Cmd, Body) ->
%% API update will placed in `authz` subdirectory inside EMQX's `data_dir`
- update_config(Cmd, Body).
+ update_config(Cmd, Body).
diff --git a/apps/emqx_authz/src/emqx_authz_file.erl b/apps/emqx_authz/src/emqx_authz_file.erl
index 729646714..38ff8447a 100644
--- a/apps/emqx_authz/src/emqx_authz_file.erl
+++ b/apps/emqx_authz/src/emqx_authz_file.erl
@@ -27,28 +27,32 @@
-endif.
%% APIs
--export([ description/0
- , init/1
- , destroy/1
- , authorize/4
- ]).
+-export([
+ description/0,
+ init/1,
+ destroy/1,
+ authorize/4
+]).
description() ->
"AuthZ with static rules".
init(#{path := Path} = Source) ->
- Rules = case file:consult(Path) of
- {ok, Terms} ->
- [emqx_authz_rule:compile(Term) || Term <- Terms];
- {error, Reason} when is_atom(Reason) ->
- ?SLOG(alert, #{msg => failed_to_read_acl_file,
- path => Path,
- explain => emqx_misc:explain_posix(Reason)}),
- throw(failed_to_read_acl_file);
- {error, Reason} ->
- ?SLOG(alert, #{msg => bad_acl_file_content, path => Path, reason => Reason}),
- throw(bad_acl_file_content)
- end,
+ Rules =
+ case file:consult(Path) of
+ {ok, Terms} ->
+ [emqx_authz_rule:compile(Term) || Term <- Terms];
+ {error, Reason} when is_atom(Reason) ->
+ ?SLOG(alert, #{
+ msg => failed_to_read_acl_file,
+ path => Path,
+ explain => emqx_misc:explain_posix(Reason)
+ }),
+ throw(failed_to_read_acl_file);
+ {error, Reason} ->
+ ?SLOG(alert, #{msg => bad_acl_file_content, path => Path, reason => Reason}),
+ throw(bad_acl_file_content)
+ end,
Source#{annotations => #{rules => Rules}}.
destroy(_Source) -> ok.
diff --git a/apps/emqx_authz/src/emqx_authz_http.erl b/apps/emqx_authz/src/emqx_authz_http.erl
index 592c59b03..94dfcecf3 100644
--- a/apps/emqx_authz/src/emqx_authz_http.erl
+++ b/apps/emqx_authz/src/emqx_authz_http.erl
@@ -24,25 +24,28 @@
-behaviour(emqx_authz).
%% AuthZ Callbacks
--export([ description/0
- , init/1
- , destroy/1
- , authorize/4
- , parse_url/1
- ]).
+-export([
+ description/0,
+ init/1,
+ destroy/1,
+ authorize/4,
+ parse_url/1
+]).
-ifdef(TEST).
-compile(export_all).
-compile(nowarn_export_all).
-endif.
--define(PLACEHOLDERS, [?PH_USERNAME,
- ?PH_CLIENTID,
- ?PH_PEERHOST,
- ?PH_PROTONAME,
- ?PH_MOUNTPOINT,
- ?PH_TOPIC,
- ?PH_ACTION]).
+-define(PLACEHOLDERS, [
+ ?PH_USERNAME,
+ ?PH_CLIENTID,
+ ?PH_PEERHOST,
+ ?PH_PROTONAME,
+ ?PH_MOUNTPOINT,
+ ?PH_TOPIC,
+ ?PH_ACTION
+]).
description() ->
"AuthZ with http".
@@ -57,14 +60,17 @@ init(Config) ->
destroy(#{annotations := #{id := Id}}) ->
ok = emqx_resource:remove_local(Id).
-authorize( Client
- , PubSub
- , Topic
- , #{ type := http
- , annotations := #{id := ResourceID}
- , method := Method
- , request_timeout := RequestTimeout
- } = Config) ->
+authorize(
+ Client,
+ PubSub,
+ Topic,
+ #{
+ type := http,
+ annotations := #{id := ResourceID},
+ method := Method,
+ request_timeout := RequestTimeout
+ } = Config
+) ->
Request = generate_request(PubSub, Topic, Client, Config),
case emqx_resource:query(ResourceID, {Method, Request, RequestTimeout}) of
{ok, 200, _Headers} ->
@@ -78,38 +84,47 @@ authorize( Client
{ok, _Status, _Headers, _Body} ->
nomatch;
{error, Reason} ->
- ?SLOG(error, #{msg => "http_server_query_failed",
- resource => ResourceID,
- reason => Reason}),
+ ?SLOG(error, #{
+ msg => "http_server_query_failed",
+ resource => ResourceID,
+ reason => Reason
+ }),
ignore
end.
-parse_config(#{ url := URL
- , method := Method
- , headers := Headers
- , request_timeout := ReqTimeout
- } = Conf) ->
+parse_config(
+ #{
+ url := URL,
+ method := Method,
+ headers := Headers,
+ request_timeout := ReqTimeout
+ } = Conf
+) ->
{BaseURLWithPath, Query} = parse_fullpath(URL),
BaseURLMap = parse_url(BaseURLWithPath),
- Conf#{ method => Method
- , base_url => maps:remove(query, BaseURLMap)
- , base_query_template => emqx_authz_utils:parse_deep(
- cow_qs:parse_qs(bin(Query)),
- ?PLACEHOLDERS)
- , body_template => emqx_authz_utils:parse_deep(
- maps:to_list(maps:get(body, Conf, #{})),
- ?PLACEHOLDERS)
- , headers => Headers
- , request_timeout => ReqTimeout
- %% pool_type default value `random`
- , pool_type => random
- }.
+ Conf#{
+ method => Method,
+ base_url => maps:remove(query, BaseURLMap),
+ base_query_template => emqx_authz_utils:parse_deep(
+ cow_qs:parse_qs(bin(Query)),
+ ?PLACEHOLDERS
+ ),
+ body_template => emqx_authz_utils:parse_deep(
+ maps:to_list(maps:get(body, Conf, #{})),
+ ?PLACEHOLDERS
+ ),
+ headers => Headers,
+ request_timeout => ReqTimeout,
+ %% pool_type default value `random`
+ pool_type => random
+ }.
parse_fullpath(RawURL) ->
cow_http:parse_fullpath(bin(RawURL)).
-parse_url(URL)
- when URL =:= undefined ->
+parse_url(URL) when
+ URL =:= undefined
+->
#{};
parse_url(URL) ->
{ok, URIMap} = emqx_http_lib:uri_parse(URL),
@@ -120,28 +135,31 @@ parse_url(URL) ->
URIMap
end.
-generate_request( PubSub
- , Topic
- , Client
- , #{ method := Method
- , base_url := #{path := Path}
- , base_query_template := BaseQueryTemplate
- , headers := Headers
- , body_template := BodyTemplate
- }) ->
+generate_request(
+ PubSub,
+ Topic,
+ Client,
+ #{
+ method := Method,
+ base_url := #{path := Path},
+ base_query_template := BaseQueryTemplate,
+ headers := Headers,
+ body_template := BodyTemplate
+ }
+) ->
Values = client_vars(Client, PubSub, Topic),
Body = emqx_authz_utils:render_deep(BodyTemplate, Values),
NBaseQuery = emqx_authz_utils:render_deep(BaseQueryTemplate, Values),
case Method of
- get ->
+ get ->
NPath = append_query(Path, NBaseQuery ++ Body),
{NPath, Headers};
_ ->
NPath = append_query(Path, NBaseQuery),
NBody = serialize_body(
- proplists:get_value(<<"Accept">>, Headers, <<"application/json">>),
- Body
- ),
+ proplists:get_value(<<"Accept">>, Headers, <<"application/json">>),
+ Body
+ ),
{NPath, Headers, NBody}
end.
@@ -161,9 +179,13 @@ query_string([], Acc) ->
<<>>
end;
query_string([{K, V} | More], Acc) ->
- query_string( More
- , [ ["&", emqx_http_lib:uri_encode(K), "=", emqx_http_lib:uri_encode(V)]
- | Acc]).
+ query_string(
+ More,
+ [
+ ["&", emqx_http_lib:uri_encode(K), "=", emqx_http_lib:uri_encode(V)]
+ | Acc
+ ]
+ ).
serialize_body(<<"application/json">>, Body) ->
jsx:encode(Body);
@@ -172,9 +194,9 @@ serialize_body(<<"application/x-www-form-urlencoded">>, Body) ->
client_vars(Client, PubSub, Topic) ->
Client#{
- action => PubSub,
- topic => Topic
- }.
+ action => PubSub,
+ topic => Topic
+ }.
bin(A) when is_atom(A) -> atom_to_binary(A, utf8);
bin(B) when is_binary(B) -> B;
diff --git a/apps/emqx_authz/src/emqx_authz_mnesia.erl b/apps/emqx_authz/src/emqx_authz_mnesia.erl
index f02419b0c..0bb85b96a 100644
--- a/apps/emqx_authz/src/emqx_authz_mnesia.erl
+++ b/apps/emqx_authz/src/emqx_authz_mnesia.erl
@@ -29,38 +29,40 @@
-define(ACL_TABLE_USERNAME, 1).
-define(ACL_TABLE_CLIENTID, 2).
--type(username() :: {username, binary()}).
--type(clientid() :: {clientid, binary()}).
--type(who() :: username() | clientid() | all).
+-type username() :: {username, binary()}.
+-type clientid() :: {clientid, binary()}.
+-type who() :: username() | clientid() | all.
--type(rule() :: {emqx_authz_rule:permission(), emqx_authz_rule:action(), emqx_topic:topic()}).
--type(rules() :: [rule()]).
+-type rule() :: {emqx_authz_rule:permission(), emqx_authz_rule:action(), emqx_topic:topic()}.
+-type rules() :: [rule()].
-record(emqx_acl, {
- who :: ?ACL_TABLE_ALL | {?ACL_TABLE_USERNAME, binary()} | {?ACL_TABLE_CLIENTID, binary()},
- rules :: rules()
- }).
+ who :: ?ACL_TABLE_ALL | {?ACL_TABLE_USERNAME, binary()} | {?ACL_TABLE_CLIENTID, binary()},
+ rules :: rules()
+}).
-behaviour(emqx_authz).
%% AuthZ Callbacks
--export([ description/0
- , init/1
- , destroy/1
- , authorize/4
- ]).
+-export([
+ description/0,
+ init/1,
+ destroy/1,
+ authorize/4
+]).
%% Management API
--export([ mnesia/1
- , init_tables/0
- , store_rules/2
- , purge_rules/0
- , get_rules/1
- , delete_rules/1
- , list_clientid_rules/0
- , list_username_rules/0
- , record_count/0
- ]).
+-export([
+ mnesia/1,
+ init_tables/0,
+ store_rules/2,
+ purge_rules/0,
+ get_rules/1,
+ delete_rules/1,
+ list_clientid_rules/0,
+ list_username_rules/0,
+ record_count/0
+]).
-ifdef(TEST).
-compile(export_all).
@@ -69,14 +71,15 @@
-boot_mnesia({mnesia, [boot]}).
--spec(mnesia(boot | copy) -> ok).
+-spec mnesia(boot | copy) -> ok.
mnesia(boot) ->
ok = mria:create_table(?ACL_TABLE, [
- {type, ordered_set},
- {rlog_shard, ?ACL_SHARDED},
- {storage, disc_copies},
- {attributes, record_info(fields, ?ACL_TABLE)},
- {storage_properties, [{ets, [{read_concurrency, true}]}]}]).
+ {type, ordered_set},
+ {rlog_shard, ?ACL_SHARDED},
+ {storage, disc_copies},
+ {attributes, record_info(fields, ?ACL_TABLE)},
+ {storage_properties, [{ets, [{read_concurrency, true}]}]}
+ ]).
%%--------------------------------------------------------------------
%% emqx_authz callbacks
@@ -89,19 +92,25 @@ init(Source) -> Source.
destroy(_Source) -> ok.
-authorize(#{username := Username,
- clientid := Clientid
- } = Client, PubSub, Topic, #{type := 'built_in_database'}) ->
-
- Rules = case mnesia:dirty_read(?ACL_TABLE, {?ACL_TABLE_CLIENTID, Clientid}) of
- [] -> [];
- [#emqx_acl{rules = Rules0}] when is_list(Rules0) -> Rules0
- end
- ++ case mnesia:dirty_read(?ACL_TABLE, {?ACL_TABLE_USERNAME, Username}) of
+authorize(
+ #{
+ username := Username,
+ clientid := Clientid
+ } = Client,
+ PubSub,
+ Topic,
+ #{type := 'built_in_database'}
+) ->
+ Rules =
+ case mnesia:dirty_read(?ACL_TABLE, {?ACL_TABLE_CLIENTID, Clientid}) of
+ [] -> [];
+ [#emqx_acl{rules = Rules0}] when is_list(Rules0) -> Rules0
+ end ++
+ case mnesia:dirty_read(?ACL_TABLE, {?ACL_TABLE_USERNAME, Username}) of
[] -> [];
[#emqx_acl{rules = Rules1}] when is_list(Rules1) -> Rules1
- end
- ++ case mnesia:dirty_read(?ACL_TABLE, ?ACL_TABLE_ALL) of
+ end ++
+ case mnesia:dirty_read(?ACL_TABLE, ?ACL_TABLE_ALL) of
[] -> [];
[#emqx_acl{rules = Rules2}] when is_list(Rules2) -> Rules2
end,
@@ -112,12 +121,12 @@ authorize(#{username := Username,
%%--------------------------------------------------------------------
%% Init
--spec(init_tables() -> ok).
+-spec init_tables() -> ok.
init_tables() ->
ok = mria_rlog:wait_for_shards([?ACL_SHARDED], infinity).
%% @doc Update authz rules
--spec(store_rules(who(), rules()) -> ok).
+-spec store_rules(who(), rules()) -> ok.
store_rules({username, Username}, Rules) ->
Record = #emqx_acl{who = {?ACL_TABLE_USERNAME, Username}, rules = normalize_rules(Rules)},
mria:dirty_write(Record);
@@ -129,16 +138,17 @@ store_rules(all, Rules) ->
mria:dirty_write(Record).
%% @doc Clean all authz rules for (username & clientid & all)
--spec(purge_rules() -> ok).
+-spec purge_rules() -> ok.
purge_rules() ->
ok = lists:foreach(
- fun(Key) ->
- ok = mria:dirty_delete(?ACL_TABLE, Key)
- end,
- mnesia:dirty_all_keys(?ACL_TABLE)).
+ fun(Key) ->
+ ok = mria:dirty_delete(?ACL_TABLE, Key)
+ end,
+ mnesia:dirty_all_keys(?ACL_TABLE)
+ ).
%% @doc Get one record
--spec(get_rules(who()) -> {ok, rules()} | not_found).
+-spec get_rules(who()) -> {ok, rules()} | not_found.
get_rules({username, Username}) ->
do_get_rules({?ACL_TABLE_USERNAME, Username});
get_rules({clientid, Clientid}) ->
@@ -147,7 +157,7 @@ get_rules(all) ->
do_get_rules(?ACL_TABLE_ALL).
%% @doc Delete one record
--spec(delete_rules(who()) -> ok).
+-spec delete_rules(who()) -> ok.
delete_rules({username, Username}) ->
mria:dirty_delete(?ACL_TABLE, {?ACL_TABLE_USERNAME, Username});
delete_rules({clientid, Clientid}) ->
@@ -155,21 +165,23 @@ delete_rules({clientid, Clientid}) ->
delete_rules(all) ->
mria:dirty_delete(?ACL_TABLE, ?ACL_TABLE_ALL).
--spec(list_username_rules() -> ets:match_spec()).
+-spec list_username_rules() -> ets:match_spec().
list_username_rules() ->
ets:fun2ms(
- fun(#emqx_acl{who = {?ACL_TABLE_USERNAME, Username}, rules = Rules}) ->
- [{username, Username}, {rules, Rules}]
- end).
+ fun(#emqx_acl{who = {?ACL_TABLE_USERNAME, Username}, rules = Rules}) ->
+ [{username, Username}, {rules, Rules}]
+ end
+ ).
--spec(list_clientid_rules() -> ets:match_spec()).
+-spec list_clientid_rules() -> ets:match_spec().
list_clientid_rules() ->
ets:fun2ms(
- fun(#emqx_acl{who = {?ACL_TABLE_CLIENTID, Clientid}, rules = Rules}) ->
- [{clientid, Clientid}, {rules, Rules}]
- end).
+ fun(#emqx_acl{who = {?ACL_TABLE_CLIENTID, Clientid}, rules = Rules}) ->
+ [{clientid, Clientid}, {rules, Rules}]
+ end
+ ).
--spec(record_count() -> non_neg_integer()).
+-spec record_count() -> non_neg_integer().
record_count() ->
mnesia:table_info(?ACL_TABLE, size).
@@ -181,9 +193,7 @@ normalize_rules(Rules) ->
lists:map(fun normalize_rule/1, Rules).
normalize_rule({Permission, Action, Topic}) ->
- {normalize_permission(Permission),
- normalize_action(Action),
- normalize_topic(Topic)};
+ {normalize_permission(Permission), normalize_action(Action), normalize_topic(Topic)};
normalize_rule(Rule) ->
error({invalid_rule, Rule}).
@@ -206,8 +216,9 @@ do_get_rules(Key) ->
[] -> not_found
end.
-do_authorize(_Client, _PubSub, _Topic, []) -> nomatch;
-do_authorize(Client, PubSub, Topic, [ {Permission, Action, TopicFilter} | Tail]) ->
+do_authorize(_Client, _PubSub, _Topic, []) ->
+ nomatch;
+do_authorize(Client, PubSub, Topic, [{Permission, Action, TopicFilter} | Tail]) ->
Rule = emqx_authz_rule:compile({Permission, all, Action, [TopicFilter]}),
case emqx_authz_rule:match(Client, PubSub, Topic, Rule) of
{matched, Permission} -> {matched, Permission};
diff --git a/apps/emqx_authz/src/emqx_authz_mongodb.erl b/apps/emqx_authz/src/emqx_authz_mongodb.erl
index 158bff152..2bac33003 100644
--- a/apps/emqx_authz/src/emqx_authz_mongodb.erl
+++ b/apps/emqx_authz/src/emqx_authz_mongodb.erl
@@ -24,62 +24,83 @@
-behaviour(emqx_authz).
%% AuthZ Callbacks
--export([ description/0
- , init/1
- , destroy/1
- , authorize/4
- ]).
+-export([
+ description/0,
+ init/1,
+ destroy/1,
+ authorize/4
+]).
-ifdef(TEST).
-compile(export_all).
-compile(nowarn_export_all).
-endif.
--define(PLACEHOLDERS, [?PH_USERNAME,
- ?PH_CLIENTID,
- ?PH_PEERHOST]).
+-define(PLACEHOLDERS, [
+ ?PH_USERNAME,
+ ?PH_CLIENTID,
+ ?PH_PEERHOST
+]).
description() ->
"AuthZ with MongoDB".
init(#{selector := Selector} = Source) ->
case emqx_authz_utils:create_resource(emqx_connector_mongo, Source) of
- {error, Reason} -> error({load_config_error, Reason});
- {ok, Id} -> Source#{annotations => #{id => Id},
- selector_template => emqx_authz_utils:parse_deep(
- Selector,
- ?PLACEHOLDERS)}
+ {error, Reason} ->
+ error({load_config_error, Reason});
+ {ok, Id} ->
+ Source#{
+ annotations => #{id => Id},
+ selector_template => emqx_authz_utils:parse_deep(
+ Selector,
+ ?PLACEHOLDERS
+ )
+ }
end.
destroy(#{annotations := #{id := Id}}) ->
ok = emqx_resource:remove_local(Id).
-authorize(Client, PubSub, Topic,
- #{collection := Collection,
- selector_template := SelectorTemplate,
- annotations := #{id := ResourceID}
- }) ->
+authorize(
+ Client,
+ PubSub,
+ Topic,
+ #{
+ collection := Collection,
+ selector_template := SelectorTemplate,
+ annotations := #{id := ResourceID}
+ }
+) ->
RenderedSelector = emqx_authz_utils:render_deep(SelectorTemplate, Client),
- Result = try
- emqx_resource:query(ResourceID, {find, Collection, RenderedSelector, #{}})
- catch
- error:Error -> {error, Error}
- end,
+ Result =
+ try
+ emqx_resource:query(ResourceID, {find, Collection, RenderedSelector, #{}})
+ catch
+ error:Error -> {error, Error}
+ end,
case Result of
{error, Reason} ->
- ?SLOG(error, #{msg => "query_mongo_error",
- reason => Reason,
- collection => Collection,
- selector => RenderedSelector,
- resource_id => ResourceID}),
+ ?SLOG(error, #{
+ msg => "query_mongo_error",
+ reason => Reason,
+ collection => Collection,
+ selector => RenderedSelector,
+ resource_id => ResourceID
+ }),
+ nomatch;
+ [] ->
nomatch;
- [] -> nomatch;
Rows ->
- Rules = [ emqx_authz_rule:compile({Permission, all, Action, Topics})
- || #{<<"topics">> := Topics,
- <<"permission">> := Permission,
- <<"action">> := Action} <- Rows],
+ Rules = [
+ emqx_authz_rule:compile({Permission, all, Action, Topics})
+ || #{
+ <<"topics">> := Topics,
+ <<"permission">> := Permission,
+ <<"action">> := Action
+ } <- Rows
+ ],
do_authorize(Client, PubSub, Topic, Rules)
end.
diff --git a/apps/emqx_authz/src/emqx_authz_mysql.erl b/apps/emqx_authz/src/emqx_authz_mysql.erl
index 31e8dcd98..d88273e14 100644
--- a/apps/emqx_authz/src/emqx_authz_mysql.erl
+++ b/apps/emqx_authz/src/emqx_authz_mysql.erl
@@ -24,66 +24,89 @@
-behaviour(emqx_authz).
%% AuthZ Callbacks
--export([ description/0
- , init/1
- , destroy/1
- , authorize/4
- ]).
+-export([
+ description/0,
+ init/1,
+ destroy/1,
+ authorize/4
+]).
-ifdef(TEST).
-compile(export_all).
-compile(nowarn_export_all).
-endif.
--define(PLACEHOLDERS, [?PH_USERNAME,
- ?PH_CLIENTID,
- ?PH_PEERHOST,
- ?PH_CERT_CN_NAME,
- ?PH_CERT_SUBJECT]).
+-define(PLACEHOLDERS, [
+ ?PH_USERNAME,
+ ?PH_CLIENTID,
+ ?PH_PEERHOST,
+ ?PH_CERT_CN_NAME,
+ ?PH_CERT_SUBJECT
+]).
description() ->
"AuthZ with Mysql".
init(#{query := SQL} = Source) ->
case emqx_authz_utils:create_resource(emqx_connector_mysql, Source) of
- {error, Reason} -> error({load_config_error, Reason});
- {ok, Id} -> Source#{annotations =>
- #{id => Id,
- query => emqx_authz_utils:parse_sql(
- SQL,
- '?',
- ?PLACEHOLDERS)}}
+ {error, Reason} ->
+ error({load_config_error, Reason});
+ {ok, Id} ->
+ Source#{
+ annotations =>
+ #{
+ id => Id,
+ query => emqx_authz_utils:parse_sql(
+ SQL,
+ '?',
+ ?PLACEHOLDERS
+ )
+ }
+ }
end.
destroy(#{annotations := #{id := Id}}) ->
ok = emqx_resource:remove_local(Id).
-authorize(Client, PubSub, Topic,
- #{annotations := #{id := ResourceID,
- query := {Query, Params}
- }
- }) ->
+authorize(
+ Client,
+ PubSub,
+ Topic,
+ #{
+ annotations := #{
+ id := ResourceID,
+ query := {Query, Params}
+ }
+ }
+) ->
RenderParams = emqx_authz_utils:render_sql_params(Params, Client),
case emqx_resource:query(ResourceID, {sql, Query, RenderParams}) of
- {ok, _Columns, []} -> nomatch;
+ {ok, _Columns, []} ->
+ nomatch;
{ok, Columns, Rows} ->
do_authorize(Client, PubSub, Topic, Columns, Rows);
{error, Reason} ->
- ?SLOG(error, #{ msg => "query_mysql_error"
- , reason => Reason
- , query => Query
- , params => RenderParams
- , resource_id => ResourceID}),
+ ?SLOG(error, #{
+ msg => "query_mysql_error",
+ reason => Reason,
+ query => Query,
+ params => RenderParams,
+ resource_id => ResourceID
+ }),
nomatch
end.
-
do_authorize(_Client, _PubSub, _Topic, _Columns, []) ->
nomatch;
do_authorize(Client, PubSub, Topic, Columns, [Row | Tail]) ->
- case emqx_authz_rule:match(Client, PubSub, Topic,
- emqx_authz_rule:compile(format_result(Columns, Row))
- ) of
+ case
+ emqx_authz_rule:match(
+ Client,
+ PubSub,
+ Topic,
+ emqx_authz_rule:compile(format_result(Columns, Row))
+ )
+ of
{matched, Permission} -> {matched, Permission};
nomatch -> do_authorize(Client, PubSub, Topic, Columns, Tail)
end.
@@ -97,5 +120,5 @@ format_result(Columns, Row) ->
index(Elem, List) ->
index(Elem, List, 1).
index(_Elem, [], _Index) -> {error, not_found};
-index(Elem, [ Elem | _List], Index) -> Index;
-index(Elem, [ _ | List], Index) -> index(Elem, List, Index + 1).
+index(Elem, [Elem | _List], Index) -> Index;
+index(Elem, [_ | List], Index) -> index(Elem, List, Index + 1).
diff --git a/apps/emqx_authz/src/emqx_authz_postgresql.erl b/apps/emqx_authz/src/emqx_authz_postgresql.erl
index 275b39f2b..4305addb7 100644
--- a/apps/emqx_authz/src/emqx_authz_postgresql.erl
+++ b/apps/emqx_authz/src/emqx_authz_postgresql.erl
@@ -24,42 +24,53 @@
-behaviour(emqx_authz).
%% AuthZ Callbacks
--export([ description/0
- , init/1
- , destroy/1
- , authorize/4
- ]).
+-export([
+ description/0,
+ init/1,
+ destroy/1,
+ authorize/4
+]).
-ifdef(TEST).
-compile(export_all).
-compile(nowarn_export_all).
-endif.
--define(PLACEHOLDERS, [?PH_USERNAME,
- ?PH_CLIENTID,
- ?PH_PEERHOST,
- ?PH_CERT_CN_NAME,
- ?PH_CERT_SUBJECT]).
+-define(PLACEHOLDERS, [
+ ?PH_USERNAME,
+ ?PH_CLIENTID,
+ ?PH_PEERHOST,
+ ?PH_CERT_CN_NAME,
+ ?PH_CERT_SUBJECT
+]).
description() ->
"AuthZ with PostgreSQL".
init(#{query := SQL0} = Source) ->
{SQL, PlaceHolders} = emqx_authz_utils:parse_sql(
- SQL0,
- '$n',
- ?PLACEHOLDERS),
+ SQL0,
+ '$n',
+ ?PLACEHOLDERS
+ ),
ResourceID = emqx_authz_utils:make_resource_id(emqx_connector_pgsql),
- case emqx_resource:create_local(
+ case
+ emqx_resource:create_local(
ResourceID,
?RESOURCE_GROUP,
emqx_connector_pgsql,
Source#{named_queries => #{ResourceID => SQL}},
- #{}) of
+ #{}
+ )
+ of
{ok, _} ->
- Source#{annotations =>
- #{id => ResourceID,
- placeholders => PlaceHolders}};
+ Source#{
+ annotations =>
+ #{
+ id => ResourceID,
+ placeholders => PlaceHolders
+ }
+ };
{error, Reason} ->
error({load_config_error, Reason})
end.
@@ -67,30 +78,44 @@ init(#{query := SQL0} = Source) ->
destroy(#{annotations := #{id := Id}}) ->
ok = emqx_resource:remove_local(Id).
-authorize(Client, PubSub, Topic,
- #{annotations := #{id := ResourceID,
- placeholders := Placeholders
- }
- }) ->
+authorize(
+ Client,
+ PubSub,
+ Topic,
+ #{
+ annotations := #{
+ id := ResourceID,
+ placeholders := Placeholders
+ }
+ }
+) ->
RenderedParams = emqx_authz_utils:render_sql_params(Placeholders, Client),
case emqx_resource:query(ResourceID, {prepared_query, ResourceID, RenderedParams}) of
- {ok, _Columns, []} -> nomatch;
+ {ok, _Columns, []} ->
+ nomatch;
{ok, Columns, Rows} ->
do_authorize(Client, PubSub, Topic, Columns, Rows);
{error, Reason} ->
- ?SLOG(error, #{ msg => "query_postgresql_error"
- , reason => Reason
- , params => RenderedParams
- , resource_id => ResourceID}),
+ ?SLOG(error, #{
+ msg => "query_postgresql_error",
+ reason => Reason,
+ params => RenderedParams,
+ resource_id => ResourceID
+ }),
nomatch
end.
do_authorize(_Client, _PubSub, _Topic, _Columns, []) ->
nomatch;
do_authorize(Client, PubSub, Topic, Columns, [Row | Tail]) ->
- case emqx_authz_rule:match(Client, PubSub, Topic,
- emqx_authz_rule:compile(format_result(Columns, Row))
- ) of
+ case
+ emqx_authz_rule:match(
+ Client,
+ PubSub,
+ Topic,
+ emqx_authz_rule:compile(format_result(Columns, Row))
+ )
+ of
{matched, Permission} -> {matched, Permission};
nomatch -> do_authorize(Client, PubSub, Topic, Columns, Tail)
end.
@@ -104,6 +129,9 @@ format_result(Columns, Row) ->
index(Key, N, TupleList) when is_integer(N) ->
Tuple = lists:keyfind(Key, N, TupleList),
index(Tuple, TupleList, 1);
-index(_Tuple, [], _Index) -> {error, not_found};
-index(Tuple, [Tuple | _TupleList], Index) -> Index;
-index(Tuple, [_ | TupleList], Index) -> index(Tuple, TupleList, Index + 1).
+index(_Tuple, [], _Index) ->
+ {error, not_found};
+index(Tuple, [Tuple | _TupleList], Index) ->
+ Index;
+index(Tuple, [_ | TupleList], Index) ->
+ index(Tuple, TupleList, Index + 1).
diff --git a/apps/emqx_authz/src/emqx_authz_redis.erl b/apps/emqx_authz/src/emqx_authz_redis.erl
index 7e9ea8325..54ec16475 100644
--- a/apps/emqx_authz/src/emqx_authz_redis.erl
+++ b/apps/emqx_authz/src/emqx_authz_redis.erl
@@ -24,22 +24,25 @@
-behaviour(emqx_authz).
%% AuthZ Callbacks
--export([ description/0
- , init/1
- , destroy/1
- , authorize/4
- ]).
+-export([
+ description/0,
+ init/1,
+ destroy/1,
+ authorize/4
+]).
-ifdef(TEST).
-compile(export_all).
-compile(nowarn_export_all).
-endif.
--define(PLACEHOLDERS, [?PH_CERT_CN_NAME,
- ?PH_CERT_SUBJECT,
- ?PH_PEERHOST,
- ?PH_CLIENTID,
- ?PH_USERNAME]).
+-define(PLACEHOLDERS, [
+ ?PH_CERT_CN_NAME,
+ ?PH_CERT_SUBJECT,
+ ?PH_PEERHOST,
+ ?PH_CLIENTID,
+ ?PH_USERNAME
+]).
description() ->
"AuthZ with Redis".
@@ -48,38 +51,54 @@ init(#{cmd := CmdStr} = Source) ->
Cmd = tokens(CmdStr),
CmdTemplate = emqx_authz_utils:parse_deep(Cmd, ?PLACEHOLDERS),
case emqx_authz_utils:create_resource(emqx_connector_redis, Source) of
- {error, Reason} -> error({load_config_error, Reason});
- {ok, Id} -> Source#{annotations => #{id => Id},
- cmd_template => CmdTemplate}
+ {error, Reason} ->
+ error({load_config_error, Reason});
+ {ok, Id} ->
+ Source#{
+ annotations => #{id => Id},
+ cmd_template => CmdTemplate
+ }
end.
destroy(#{annotations := #{id := Id}}) ->
ok = emqx_resource:remove_local(Id).
-authorize(Client, PubSub, Topic,
- #{cmd_template := CmdTemplate,
- annotations := #{id := ResourceID}
- }) ->
+authorize(
+ Client,
+ PubSub,
+ Topic,
+ #{
+ cmd_template := CmdTemplate,
+ annotations := #{id := ResourceID}
+ }
+) ->
Cmd = emqx_authz_utils:render_deep(CmdTemplate, Client),
case emqx_resource:query(ResourceID, {cmd, Cmd}) of
- {ok, []} -> nomatch;
+ {ok, []} ->
+ nomatch;
{ok, Rows} ->
do_authorize(Client, PubSub, Topic, Rows);
{error, Reason} ->
- ?SLOG(error, #{ msg => "query_redis_error"
- , reason => Reason
- , cmd => Cmd
- , resource_id => ResourceID}),
+ ?SLOG(error, #{
+ msg => "query_redis_error",
+ reason => Reason,
+ cmd => Cmd,
+ resource_id => ResourceID
+ }),
nomatch
end.
do_authorize(_Client, _PubSub, _Topic, []) ->
nomatch;
do_authorize(Client, PubSub, Topic, [TopicFilter, Action | Tail]) ->
- case emqx_authz_rule:match(
- Client, PubSub, Topic,
- emqx_authz_rule:compile({allow, all, Action, [TopicFilter]})
- ) of
+ case
+ emqx_authz_rule:match(
+ Client,
+ PubSub,
+ Topic,
+ emqx_authz_rule:compile({allow, all, Action, [TopicFilter]})
+ )
+ of
{matched, Permission} -> {matched, Permission};
nomatch -> do_authorize(Client, PubSub, Topic, Tail)
end.
diff --git a/apps/emqx_authz/src/emqx_authz_rule.erl b/apps/emqx_authz/src/emqx_authz_rule.erl
index 4333cf0e7..c5ad0b657 100644
--- a/apps/emqx_authz/src/emqx_authz_rule.erl
+++ b/apps/emqx_authz/src/emqx_authz_rule.erl
@@ -26,50 +26,66 @@
-endif.
%% APIs
--export([ match/4
- , matches/4
- , compile/1
- ]).
+-export([
+ match/4,
+ matches/4,
+ compile/1
+]).
--type(ipaddress() :: {ipaddr, esockd_cidr:cidr_string()} |
- {ipaddrs, list(esockd_cidr:cidr_string())}).
+-type ipaddress() ::
+ {ipaddr, esockd_cidr:cidr_string()}
+ | {ipaddrs, list(esockd_cidr:cidr_string())}.
--type(username() :: {username, binary()}).
+-type username() :: {username, binary()}.
--type(clientid() :: {clientid, binary()}).
+-type clientid() :: {clientid, binary()}.
--type(who() :: ipaddress() | username() | clientid() |
- {'and', [ipaddress() | username() | clientid()]} |
- {'or', [ipaddress() | username() | clientid()]} |
- all).
+-type who() ::
+ ipaddress()
+ | username()
+ | clientid()
+ | {'and', [ipaddress() | username() | clientid()]}
+ | {'or', [ipaddress() | username() | clientid()]}
+ | all.
--type(action() :: subscribe | publish | all).
--type(permission() :: allow | deny).
+-type action() :: subscribe | publish | all.
+-type permission() :: allow | deny.
--type(rule() :: {permission(), who(), action(), list(emqx_types:topic())}).
+-type rule() :: {permission(), who(), action(), list(emqx_types:topic())}.
--export_type([ action/0
- , permission/0
- ]).
+-export_type([
+ action/0,
+ permission/0
+]).
-compile({Permission, all})
- when ?ALLOW_DENY(Permission) -> {Permission, all, all, [compile_topic(<<"#">>)]};
-compile({Permission, Who, Action, TopicFilters})
- when ?ALLOW_DENY(Permission), ?PUBSUB(Action), is_list(TopicFilters) ->
- { atom(Permission), compile_who(Who), atom(Action)
- , [compile_topic(Topic) || Topic <- TopicFilters]}.
+compile({Permission, all}) when
+ ?ALLOW_DENY(Permission)
+->
+ {Permission, all, all, [compile_topic(<<"#">>)]};
+compile({Permission, Who, Action, TopicFilters}) when
+ ?ALLOW_DENY(Permission), ?PUBSUB(Action), is_list(TopicFilters)
+->
+ {atom(Permission), compile_who(Who), atom(Action), [
+ compile_topic(Topic)
+ || Topic <- TopicFilters
+ ]}.
-compile_who(all) -> all;
-compile_who({user, Username}) -> compile_who({username, Username});
+compile_who(all) ->
+ all;
+compile_who({user, Username}) ->
+ compile_who({username, Username});
compile_who({username, {re, Username}}) ->
{ok, MP} = re:compile(bin(Username)),
{username, MP};
-compile_who({username, Username}) -> {username, {eq, bin(Username)}};
-compile_who({client, Clientid}) -> compile_who({clientid, Clientid});
+compile_who({username, Username}) ->
+ {username, {eq, bin(Username)}};
+compile_who({client, Clientid}) ->
+ compile_who({clientid, Clientid});
compile_who({clientid, {re, Clientid}}) ->
{ok, MP} = re:compile(bin(Clientid)),
{clientid, MP};
-compile_who({clientid, Clientid}) -> {clientid, {eq, bin(Clientid)}};
+compile_who({clientid, Clientid}) ->
+ {clientid, {eq, bin(Clientid)}};
compile_who({ipaddr, CIDR}) ->
{ipaddr, esockd_cidr:parse(CIDR, true)};
compile_who({ipaddrs, CIDRs}) ->
@@ -86,7 +102,7 @@ compile_topic({eq, Topic}) ->
compile_topic(Topic) ->
Words = emqx_topic:words(bin(Topic)),
case pattern(Words) of
- true -> {pattern, Words};
+ true -> {pattern, Words};
false -> Words
end.
@@ -94,7 +110,8 @@ pattern(Words) ->
lists:member(?PH_USERNAME, Words) orelse lists:member(?PH_CLIENTID, Words).
atom(B) when is_binary(B) ->
- try binary_to_existing_atom(B, utf8)
+ try
+ binary_to_existing_atom(B, utf8)
catch
_E:_S -> binary_to_atom(B)
end;
@@ -105,21 +122,24 @@ bin(L) when is_list(L) ->
bin(B) when is_binary(B) ->
B.
--spec(matches(emqx_types:clientinfo(), emqx_types:pubsub(), emqx_types:topic(), [rule()])
- -> {matched, allow} | {matched, deny} | nomatch).
-matches(_Client, _PubSub, _Topic, []) -> nomatch;
+-spec matches(emqx_types:clientinfo(), emqx_types:pubsub(), emqx_types:topic(), [rule()]) ->
+ {matched, allow} | {matched, deny} | nomatch.
+matches(_Client, _PubSub, _Topic, []) ->
+ nomatch;
matches(Client, PubSub, Topic, [{Permission, Who, Action, TopicFilters} | Tail]) ->
case match(Client, PubSub, Topic, {Permission, Who, Action, TopicFilters}) of
nomatch -> matches(Client, PubSub, Topic, Tail);
Matched -> Matched
end.
--spec(match(emqx_types:clientinfo(), emqx_types:pubsub(), emqx_types:topic(), rule())
- -> {matched, allow} | {matched, deny} | nomatch).
+-spec match(emqx_types:clientinfo(), emqx_types:pubsub(), emqx_types:topic(), rule()) ->
+ {matched, allow} | {matched, deny} | nomatch.
match(Client, PubSub, Topic, {Permission, Who, Action, TopicFilters}) ->
- case match_action(PubSub, Action) andalso
- match_who(Client, Who) andalso
- match_topics(Client, Topic, TopicFilters) of
+ case
+ match_action(PubSub, Action) andalso
+ match_who(Client, Who) andalso
+ match_topics(Client, Topic, TopicFilters)
+ of
true -> {matched, Permission};
_ -> nomatch
end.
@@ -129,16 +149,19 @@ match_action(subscribe, subscribe) -> true;
match_action(_, all) -> true;
match_action(_, _) -> false.
-match_who(_, all) -> true;
+match_who(_, all) ->
+ true;
match_who(#{username := undefined}, {username, _}) ->
false;
-match_who(#{username := Username}, {username, {eq, Username}}) -> true;
+match_who(#{username := Username}, {username, {eq, Username}}) ->
+ true;
match_who(#{username := Username}, {username, {re_pattern, _, _, _, _} = MP}) ->
case re:run(Username, MP) of
{match, _} -> true;
_ -> false
end;
-match_who(#{clientid := Clientid}, {clientid, {eq, Clientid}}) -> true;
+match_who(#{clientid := Clientid}, {clientid, {eq, Clientid}}) ->
+ true;
match_who(#{clientid := Clientid}, {clientid, {re_pattern, _, _, _, _} = MP}) ->
case re:run(Clientid, MP) of
{match, _} -> true;
@@ -151,28 +174,40 @@ match_who(#{peerhost := IpAddress}, {ipaddr, CIDR}) ->
match_who(#{peerhost := undefined}, {ipaddrs, _CIDR}) ->
false;
match_who(#{peerhost := IpAddress}, {ipaddrs, CIDRs}) ->
- lists:any(fun(CIDR) ->
- esockd_cidr:match(IpAddress, CIDR)
- end, CIDRs);
+ lists:any(
+ fun(CIDR) ->
+ esockd_cidr:match(IpAddress, CIDR)
+ end,
+ CIDRs
+ );
match_who(ClientInfo, {'and', Principals}) when is_list(Principals) ->
- lists:foldl(fun(Principal, Permission) ->
- match_who(ClientInfo, Principal) andalso Permission
- end, true, Principals);
+ lists:foldl(
+ fun(Principal, Permission) ->
+ match_who(ClientInfo, Principal) andalso Permission
+ end,
+ true,
+ Principals
+ );
match_who(ClientInfo, {'or', Principals}) when is_list(Principals) ->
- lists:foldl(fun(Principal, Permission) ->
- match_who(ClientInfo, Principal) orelse Permission
- end, false, Principals);
-match_who(_, _) -> false.
+ lists:foldl(
+ fun(Principal, Permission) ->
+ match_who(ClientInfo, Principal) orelse Permission
+ end,
+ false,
+ Principals
+ );
+match_who(_, _) ->
+ false.
match_topics(_ClientInfo, _Topic, []) ->
false;
match_topics(ClientInfo, Topic, [{pattern, PatternFilter} | Filters]) ->
TopicFilter = feed_var(ClientInfo, PatternFilter),
- match_topic(emqx_topic:words(Topic), TopicFilter)
- orelse match_topics(ClientInfo, Topic, Filters);
+ match_topic(emqx_topic:words(Topic), TopicFilter) orelse
+ match_topics(ClientInfo, Topic, Filters);
match_topics(ClientInfo, Topic, [TopicFilter | Filters]) ->
- match_topic(emqx_topic:words(Topic), TopicFilter)
- orelse match_topics(ClientInfo, Topic, Filters).
+ match_topic(emqx_topic:words(Topic), TopicFilter) orelse
+ match_topics(ClientInfo, Topic, Filters).
match_topic(Topic, {'eq', TopicFilter}) ->
Topic =:= TopicFilter;
diff --git a/apps/emqx_authz/src/emqx_authz_schema.erl b/apps/emqx_authz/src/emqx_authz_schema.erl
index d787da82d..1e34a2620 100644
--- a/apps/emqx_authz/src/emqx_authz_schema.erl
+++ b/apps/emqx_authz/src/emqx_authz_schema.erl
@@ -19,22 +19,25 @@
-include_lib("typerefl/include/types.hrl").
-include_lib("emqx_connector/include/emqx_connector.hrl").
--reflect_type([ permission/0
- , action/0
- ]).
+-reflect_type([
+ permission/0,
+ action/0
+]).
-type action() :: publish | subscribe | all.
-type permission() :: allow | deny.
--export([ namespace/0
- , roots/0
- , fields/1
- , validations/0
- ]).
+-export([
+ namespace/0,
+ roots/0,
+ fields/1,
+ validations/0
+]).
--export([ headers_no_content_type/1
- , headers/1
- ]).
+-export([
+ headers_no_content_type/1,
+ headers/1
+]).
-import(emqx_schema, [mk_duration/2]).
-include_lib("hocon/include/hoconsc.hrl").
@@ -50,73 +53,84 @@ namespace() -> authz.
roots() -> [].
fields("authorization") ->
- [ {sources, #{type => union_array(
- [ hoconsc:ref(?MODULE, file)
- , hoconsc:ref(?MODULE, http_get)
- , hoconsc:ref(?MODULE, http_post)
- , hoconsc:ref(?MODULE, mnesia)
- , hoconsc:ref(?MODULE, mongo_single)
- , hoconsc:ref(?MODULE, mongo_rs)
- , hoconsc:ref(?MODULE, mongo_sharded)
- , hoconsc:ref(?MODULE, mysql)
- , hoconsc:ref(?MODULE, postgresql)
- , hoconsc:ref(?MODULE, redis_single)
- , hoconsc:ref(?MODULE, redis_sentinel)
- , hoconsc:ref(?MODULE, redis_cluster)
- ]),
- default => [],
- desc =>
-"
-Authorization data sources.
-An array of authorization (ACL) data providers.
-It is designed as an array, not a hash-map, so the sources can be
-ordered to form a chain of access controls.
-
-When authorizing a 'publish' or 'subscribe' action, the configured
-sources are checked in order. When checking an ACL source,
-in case the client (identified by username or client ID) is not found,
-it moves on to the next source. And it stops immediately
-once an 'allow' or 'deny' decision is returned.
-
-If the client is not found in any of the sources,
-the default action configured in 'authorization.no_match' is applied.
-
-NOTE:
-The source elements are identified by their 'type'.
-It is NOT allowed to configure two or more sources of the same type.
-"
- }
- }
+ [
+ {sources, #{
+ type => union_array(
+ [
+ hoconsc:ref(?MODULE, file),
+ hoconsc:ref(?MODULE, http_get),
+ hoconsc:ref(?MODULE, http_post),
+ hoconsc:ref(?MODULE, mnesia),
+ hoconsc:ref(?MODULE, mongo_single),
+ hoconsc:ref(?MODULE, mongo_rs),
+ hoconsc:ref(?MODULE, mongo_sharded),
+ hoconsc:ref(?MODULE, mysql),
+ hoconsc:ref(?MODULE, postgresql),
+ hoconsc:ref(?MODULE, redis_single),
+ hoconsc:ref(?MODULE, redis_sentinel),
+ hoconsc:ref(?MODULE, redis_cluster)
+ ]
+ ),
+ default => [],
+ desc =>
+ "\n"
+ "Authorization data sources.
\n"
+ "An array of authorization (ACL) data providers.\n"
+ "It is designed as an array, not a hash-map, so the sources can be\n"
+ "ordered to form a chain of access controls.
\n"
+ "\n"
+ "When authorizing a 'publish' or 'subscribe' action, the configured\n"
+ "sources are checked in order. When checking an ACL source,\n"
+ "in case the client (identified by username or client ID) is not found,\n"
+ "it moves on to the next source. And it stops immediately\n"
+ "once an 'allow' or 'deny' decision is returned.
\n"
+ "\n"
+ "If the client is not found in any of the sources,\n"
+ "the default action configured in 'authorization.no_match' is applied.
\n"
+ "\n"
+ "NOTE:\n"
+ "The source elements are identified by their 'type'.\n"
+ "It is NOT allowed to configure two or more sources of the same type.\n"
+ }}
];
fields(file) ->
- [ {type, #{type => file}}
- , {enable, #{type => boolean(),
- default => true}}
- , {path, #{type => string(),
- required => true,
- desc => "
-Path to the file which contains the ACL rules.
-If the file provisioned before starting EMQX node,
-it can be placed anywhere as long as EMQX has read access to it.
-
-In case the rule-set is created from EMQX dashboard or management API,
-the file will be placed in `authz` subdirectory inside EMQX's `data_dir`,
-and the new rules will override all rules from the old config file.
-"
- }}
+ [
+ {type, #{type => file}},
+ {enable, #{
+ type => boolean(),
+ default => true
+ }},
+ {path, #{
+ type => string(),
+ required => true,
+ desc =>
+ "\n"
+ "Path to the file which contains the ACL rules.
\n"
+ "If the file provisioned before starting EMQX node,\n"
+ "it can be placed anywhere as long as EMQX has read access to it.\n"
+ "\n"
+ "In case the rule-set is created from EMQX dashboard or management API,\n"
+ "the file will be placed in `authz` subdirectory inside EMQX's `data_dir`,\n"
+ "and the new rules will override all rules from the old config file.\n"
+ }}
];
fields(http_get) ->
- [ {method, #{type => get, default => post}}
- , {headers, fun headers_no_content_type/1}
+ [
+ {method, #{type => get, default => post}},
+ {headers, fun headers_no_content_type/1}
] ++ http_common_fields();
fields(http_post) ->
- [ {method, #{type => post, default => post}}
- , {headers, fun headers/1}
+ [
+ {method, #{type => post, default => post}},
+ {headers, fun headers/1}
] ++ http_common_fields();
fields(mnesia) ->
- [ {type, #{type => 'built_in_database'}}
- , {enable, #{type => boolean(),
- default => true}}
+ [
+ {type, #{type => 'built_in_database'}},
+ {enable, #{
+ type => boolean(),
+ default => true
+ }}
];
fields(mongo_single) ->
mongo_common_fields() ++ emqx_connector_mongo:fields(single);
@@ -126,62 +140,88 @@ fields(mongo_sharded) ->
mongo_common_fields() ++ emqx_connector_mongo:fields(sharded);
fields(mysql) ->
connector_fields(mysql) ++
- [ {query, query()} ];
+ [{query, query()}];
fields(postgresql) ->
- [ {query, query()}
- , {type, #{type => postgresql}}
- , {enable, #{type => boolean(),
- default => true}}
+ [
+ {query, query()},
+ {type, #{type => postgresql}},
+ {enable, #{
+ type => boolean(),
+ default => true
+ }}
] ++ emqx_connector_pgsql:fields(config);
fields(redis_single) ->
connector_fields(redis, single) ++
- [ {cmd, query()} ];
+ [{cmd, query()}];
fields(redis_sentinel) ->
connector_fields(redis, sentinel) ++
- [ {cmd, query()} ];
+ [{cmd, query()}];
fields(redis_cluster) ->
connector_fields(redis, cluster) ++
- [ {cmd, query()} ].
+ [{cmd, query()}].
http_common_fields() ->
- [ {url, fun url/1}
- , {request_timeout, mk_duration("Request timeout", #{default => "30s", desc => "Request timeout."})}
- , {body, #{type => map(), required => false, desc => "HTTP request body."}}
- ] ++ maps:to_list(maps:without([ base_url
- , pool_type],
- maps:from_list(connector_fields(http)))).
+ [
+ {url, fun url/1},
+ {request_timeout,
+ mk_duration("Request timeout", #{default => "30s", desc => "Request timeout."})},
+ {body, #{type => map(), required => false, desc => "HTTP request body."}}
+ ] ++
+ maps:to_list(
+ maps:without(
+ [
+ base_url,
+ pool_type
+ ],
+ maps:from_list(connector_fields(http))
+ )
+ ).
mongo_common_fields() ->
- [ {collection, #{type => atom(), desc => "`MongoDB` collection containing the authorization data."}}
- , {selector, #{type => map(), desc => "MQL query used to select the authorization record."}}
- , {type, #{type => mongodb, desc => "Database backend."}}
- , {enable, #{type => boolean(),
- default => true,
- desc => "Enable or disable the backend."}}
+ [
+ {collection, #{
+ type => atom(), desc => "`MongoDB` collection containing the authorization data."
+ }},
+ {selector, #{type => map(), desc => "MQL query used to select the authorization record."}},
+ {type, #{type => mongodb, desc => "Database backend."}},
+ {enable, #{
+ type => boolean(),
+ default => true,
+ desc => "Enable or disable the backend."
+ }}
].
validations() ->
- [ {check_ssl_opts, fun check_ssl_opts/1}
- , {check_headers, fun check_headers/1}
+ [
+ {check_ssl_opts, fun check_ssl_opts/1},
+ {check_headers, fun check_headers/1}
].
-headers(type) -> list({binary(), binary()});
-headers(desc) -> "List of HTTP headers.";
+headers(type) ->
+ list({binary(), binary()});
+headers(desc) ->
+ "List of HTTP headers.";
headers(converter) ->
fun(Headers) ->
maps:to_list(maps:merge(default_headers(), transform_header_name(Headers)))
end;
-headers(default) -> default_headers();
-headers(_) -> undefined.
+headers(default) ->
+ default_headers();
+headers(_) ->
+ undefined.
-headers_no_content_type(type) -> list({binary(), binary()});
-headers_no_content_type(desc) -> "List of HTTP headers.";
+headers_no_content_type(type) ->
+ list({binary(), binary()});
+headers_no_content_type(desc) ->
+ "List of HTTP headers.";
headers_no_content_type(converter) ->
fun(Headers) ->
- maps:to_list(maps:merge(default_headers_no_content_type(), transform_header_name(Headers)))
+ maps:to_list(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_no_content_type(default) ->
+ default_headers_no_content_type();
+headers_no_content_type(_) ->
+ undefined.
url(type) -> binary();
url(desc) -> "URL of the auth server.";
@@ -194,26 +234,34 @@ url(_) -> undefined.
%%--------------------------------------------------------------------
default_headers() ->
- maps:put(<<"content-type">>,
- <<"application/json">>,
- default_headers_no_content_type()).
+ maps:put(
+ <<"content-type">>,
+ <<"application/json">>,
+ default_headers_no_content_type()
+ ).
default_headers_no_content_type() ->
- #{ <<"accept">> => <<"application/json">>
- , <<"cache-control">> => <<"no-cache">>
- , <<"connection">> => <<"keep-alive">>
- , <<"keep-alive">> => <<"timeout=30, max=1000">>
- }.
+ #{
+ <<"accept">> => <<"application/json">>,
+ <<"cache-control">> => <<"no-cache">>,
+ <<"connection">> => <<"keep-alive">>,
+ <<"keep-alive">> => <<"timeout=30, max=1000">>
+ }.
transform_header_name(Headers) ->
- maps:fold(fun(K0, V, Acc) ->
- K = list_to_binary(string:to_lower(to_list(K0))),
- maps:put(K, V, Acc)
- end, #{}, Headers).
+ maps:fold(
+ fun(K0, V, Acc) ->
+ K = list_to_binary(string:to_lower(to_list(K0))),
+ maps:put(K, V, Acc)
+ end,
+ #{},
+ Headers
+ ).
check_ssl_opts(Conf) ->
case hocon_maps:get("config.url", Conf) of
- undefined -> true;
+ undefined ->
+ true;
Url ->
case emqx_authz_http:parse_url(Url) of
#{scheme := https} ->
@@ -221,19 +269,23 @@ check_ssl_opts(Conf) ->
true -> true;
_ -> {error, ssl_not_enable}
end;
- #{scheme := http} -> true;
- Bad -> {bad_scheme, Url, Bad}
+ #{scheme := http} ->
+ true;
+ Bad ->
+ {bad_scheme, Url, Bad}
end
end.
check_headers(Conf) ->
case hocon_maps:get("config.method", Conf) of
- undefined -> true;
+ undefined ->
+ true;
Method0 ->
Method = to_bin(Method0),
Headers = hocon_maps:get("config.headers", Conf),
case Method of
- <<"post">> -> true;
+ <<"post">> ->
+ true;
_ when Headers =:= undefined -> true;
_ when is_list(Headers) ->
case lists:member(<<"content-type">>, Headers) of
@@ -247,32 +299,37 @@ union_array(Item) when is_list(Item) ->
hoconsc:array(hoconsc:union(Item)).
query() ->
- #{type => binary(),
- desc => "",
- validator => fun(S) ->
- case size(S) > 0 of
- true -> ok;
- _ -> {error, "Request query"}
- end
- end
- }.
+ #{
+ type => binary(),
+ desc => "",
+ validator => fun(S) ->
+ case size(S) > 0 of
+ true -> ok;
+ _ -> {error, "Request query"}
+ end
+ end
+ }.
connector_fields(DB) ->
connector_fields(DB, config).
connector_fields(DB, Fields) ->
- Mod0 = io_lib:format("~ts_~ts",[emqx_connector, DB]),
- Mod = try
- list_to_existing_atom(Mod0)
- catch
- error:badarg ->
- list_to_atom(Mod0);
- error:Reason ->
- erlang:error(Reason)
- end,
- [ {type, #{type => DB, desc => "Database backend."}}
- , {enable, #{type => boolean(),
- default => true,
- desc => "Enable or disable the backend."}}
+ Mod0 = io_lib:format("~ts_~ts", [emqx_connector, DB]),
+ Mod =
+ try
+ list_to_existing_atom(Mod0)
+ catch
+ error:badarg ->
+ list_to_atom(Mod0);
+ error:Reason ->
+ erlang:error(Reason)
+ end,
+ [
+ {type, #{type => DB, desc => "Database backend."}},
+ {enable, #{
+ type => boolean(),
+ default => true,
+ desc => "Enable or disable the backend."
+ }}
] ++ erlang:apply(Mod, fields, [Fields]).
to_list(A) when is_atom(A) ->
diff --git a/apps/emqx_authz/src/emqx_authz_sup.erl b/apps/emqx_authz/src/emqx_authz_sup.erl
index ca50f5a83..47a639201 100644
--- a/apps/emqx_authz/src/emqx_authz_sup.erl
+++ b/apps/emqx_authz/src/emqx_authz_sup.erl
@@ -33,8 +33,10 @@ start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
init([]) ->
- SupFlags = #{strategy => one_for_all,
- intensity => 0,
- period => 1},
+ SupFlags = #{
+ strategy => one_for_all,
+ intensity => 0,
+ period => 1
+ },
ChildSpecs = [],
{ok, {SupFlags, ChildSpecs}}.
diff --git a/apps/emqx_authz/src/emqx_authz_utils.erl b/apps/emqx_authz/src/emqx_authz_utils.erl
index 4a0e447e9..c153dbc1f 100644
--- a/apps/emqx_authz/src/emqx_authz_utils.erl
+++ b/apps/emqx_authz/src/emqx_authz_utils.erl
@@ -19,15 +19,16 @@
-include_lib("emqx/include/emqx_placeholder.hrl").
-include_lib("emqx_authz.hrl").
--export([ cleanup_resources/0
- , make_resource_id/1
- , create_resource/2
- , update_config/2
- , parse_deep/2
- , parse_sql/3
- , render_deep/2
- , render_sql_params/2
- ]).
+-export([
+ cleanup_resources/0,
+ make_resource_id/1,
+ create_resource/2,
+ update_config/2,
+ parse_deep/2,
+ parse_sql/3,
+ render_deep/2,
+ render_sql_params/2
+]).
%%------------------------------------------------------------------------------
%% APIs
@@ -35,10 +36,15 @@
create_resource(Module, Config) ->
ResourceID = make_resource_id(Module),
- case emqx_resource:create_local(ResourceID,
- ?RESOURCE_GROUP,
- Module, Config,
- #{}) of
+ case
+ emqx_resource:create_local(
+ ResourceID,
+ ?RESOURCE_GROUP,
+ Module,
+ Config,
+ #{}
+ )
+ of
{ok, already_created} -> {ok, ResourceID};
{ok, _} -> {ok, ResourceID};
{error, Reason} -> {error, Reason}
@@ -46,37 +52,45 @@ create_resource(Module, Config) ->
cleanup_resources() ->
lists:foreach(
- fun emqx_resource:remove_local/1,
- emqx_resource:list_group_instances(?RESOURCE_GROUP)).
+ fun emqx_resource:remove_local/1,
+ emqx_resource:list_group_instances(?RESOURCE_GROUP)
+ ).
make_resource_id(Name) ->
NameBin = bin(Name),
emqx_resource:generate_id(NameBin).
update_config(Path, ConfigRequest) ->
- emqx_conf:update(Path, ConfigRequest, #{rawconf_with_defaults => true,
- override_to => cluster}).
+ emqx_conf:update(Path, ConfigRequest, #{
+ rawconf_with_defaults => true,
+ override_to => cluster
+ }).
parse_deep(Template, PlaceHolders) ->
emqx_placeholder:preproc_tmpl_deep(Template, #{placeholders => PlaceHolders}).
parse_sql(Template, ReplaceWith, PlaceHolders) ->
emqx_placeholder:preproc_sql(
- Template,
- #{replace_with => ReplaceWith,
- placeholders => PlaceHolders}).
+ Template,
+ #{
+ replace_with => ReplaceWith,
+ placeholders => PlaceHolders
+ }
+ ).
render_deep(Template, Values) ->
emqx_placeholder:proc_tmpl_deep(
- Template,
- client_vars(Values),
- #{return => full_binary, var_trans => fun handle_var/2}).
+ Template,
+ client_vars(Values),
+ #{return => full_binary, var_trans => fun handle_var/2}
+ ).
render_sql_params(ParamList, Values) ->
emqx_placeholder:proc_tmpl(
- ParamList,
- client_vars(Values),
- #{return => rawlist, var_trans => fun handle_sql_var/2}).
+ ParamList,
+ client_vars(Values),
+ #{return => rawlist, var_trans => fun handle_sql_var/2}
+ ).
%%------------------------------------------------------------------------------
%% Internal functions
@@ -84,9 +98,11 @@ render_sql_params(ParamList, Values) ->
client_vars(ClientInfo) ->
maps:from_list(
- lists:map(
- fun convert_client_var/1,
- maps:to_list(ClientInfo))).
+ lists:map(
+ fun convert_client_var/1,
+ maps:to_list(ClientInfo)
+ )
+ ).
convert_client_var({cn, CN}) -> {cert_common_name, CN};
convert_client_var({dn, DN}) -> {cert_subject, DN};
diff --git a/apps/emqx_authz/test/emqx_authz_SUITE.erl b/apps/emqx_authz/test/emqx_authz_SUITE.erl
index 2d2648913..40f918727 100644
--- a/apps/emqx_authz/test/emqx_authz_SUITE.erl
+++ b/apps/emqx_authz/test/emqx_authz_SUITE.erl
@@ -33,22 +33,29 @@ init_per_suite(Config) ->
meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]),
meck:expect(emqx_resource, create_local, fun(_, _, _, _) -> {ok, meck_data} end),
meck:expect(emqx_resource, remove_local, fun(_) -> ok end),
- meck:expect(emqx_authz, acl_conf_file,
- fun() ->
- emqx_common_test_helpers:deps_path(emqx_authz, "etc/acl.conf")
- end),
+ meck:expect(
+ emqx_authz,
+ acl_conf_file,
+ fun() ->
+ emqx_common_test_helpers:deps_path(emqx_authz, "etc/acl.conf")
+ end
+ ),
ok = emqx_common_test_helpers:start_apps(
- [emqx_connector, emqx_conf, emqx_authz],
- fun set_special_configs/1),
+ [emqx_connector, emqx_conf, emqx_authz],
+ fun set_special_configs/1
+ ),
Config.
end_per_suite(_Config) ->
{ok, _} = emqx:update_config(
- [authorization],
- #{<<"no_match">> => <<"allow">>,
- <<"cache">> => #{<<"enable">> => <<"true">>},
- <<"sources">> => []}),
+ [authorization],
+ #{
+ <<"no_match">> => <<"allow">>,
+ <<"cache">> => #{<<"enable">> => <<"true">>},
+ <<"sources">> => []
+ }
+ ),
ok = stop_apps([emqx_resource]),
emqx_common_test_helpers:stop_apps([emqx_authz, emqx_conf]),
meck:unload(emqx_resource),
@@ -66,64 +73,71 @@ set_special_configs(emqx_authz) ->
set_special_configs(_App) ->
ok.
--define(SOURCE1, #{<<"type">> => <<"http">>,
- <<"enable">> => true,
- <<"url">> => <<"https://example.com:443/a/b?c=d">>,
- <<"headers">> => #{},
- <<"method">> => <<"get">>,
- <<"request_timeout">> => 5000
- }).
--define(SOURCE2, #{<<"type">> => <<"mongodb">>,
- <<"enable">> => true,
- <<"mongo_type">> => <<"single">>,
- <<"server">> => <<"127.0.0.1:27017">>,
- <<"w_mode">> => <<"unsafe">>,
- <<"pool_size">> => 1,
- <<"database">> => <<"mqtt">>,
- <<"ssl">> => #{<<"enable">> => false},
- <<"collection">> => <<"authz">>,
- <<"selector">> => #{<<"a">> => <<"b">>}
- }).
--define(SOURCE3, #{<<"type">> => <<"mysql">>,
- <<"enable">> => true,
- <<"server">> => <<"127.0.0.1:27017">>,
- <<"pool_size">> => 1,
- <<"database">> => <<"mqtt">>,
- <<"username">> => <<"xx">>,
- <<"password">> => <<"ee">>,
- <<"auto_reconnect">> => true,
- <<"ssl">> => #{<<"enable">> => false},
- <<"query">> => <<"abcb">>
- }).
--define(SOURCE4, #{<<"type">> => <<"postgresql">>,
- <<"enable">> => true,
- <<"server">> => <<"127.0.0.1:27017">>,
- <<"pool_size">> => 1,
- <<"database">> => <<"mqtt">>,
- <<"username">> => <<"xx">>,
- <<"password">> => <<"ee">>,
- <<"auto_reconnect">> => true,
- <<"ssl">> => #{<<"enable">> => false},
- <<"query">> => <<"abcb">>
- }).
--define(SOURCE5, #{<<"type">> => <<"redis">>,
- <<"redis_type">> => <<"single">>,
- <<"enable">> => true,
- <<"server">> => <<"127.0.0.1:27017">>,
- <<"pool_size">> => 1,
- <<"database">> => 0,
- <<"password">> => <<"ee">>,
- <<"auto_reconnect">> => true,
- <<"ssl">> => #{<<"enable">> => false},
- <<"cmd">> => <<"HGETALL mqtt_authz:", ?PH_USERNAME/binary>>
- }).
--define(SOURCE6, #{<<"type">> => <<"file">>,
- <<"enable">> => true,
- <<"rules">> =>
-<<"{allow,{username,\"^dashboard?\"},subscribe,[\"$SYS/#\"]}."
- "\n{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}.">>
- }).
-
+-define(SOURCE1, #{
+ <<"type">> => <<"http">>,
+ <<"enable">> => true,
+ <<"url">> => <<"https://example.com:443/a/b?c=d">>,
+ <<"headers">> => #{},
+ <<"method">> => <<"get">>,
+ <<"request_timeout">> => 5000
+}).
+-define(SOURCE2, #{
+ <<"type">> => <<"mongodb">>,
+ <<"enable">> => true,
+ <<"mongo_type">> => <<"single">>,
+ <<"server">> => <<"127.0.0.1:27017">>,
+ <<"w_mode">> => <<"unsafe">>,
+ <<"pool_size">> => 1,
+ <<"database">> => <<"mqtt">>,
+ <<"ssl">> => #{<<"enable">> => false},
+ <<"collection">> => <<"authz">>,
+ <<"selector">> => #{<<"a">> => <<"b">>}
+}).
+-define(SOURCE3, #{
+ <<"type">> => <<"mysql">>,
+ <<"enable">> => true,
+ <<"server">> => <<"127.0.0.1:27017">>,
+ <<"pool_size">> => 1,
+ <<"database">> => <<"mqtt">>,
+ <<"username">> => <<"xx">>,
+ <<"password">> => <<"ee">>,
+ <<"auto_reconnect">> => true,
+ <<"ssl">> => #{<<"enable">> => false},
+ <<"query">> => <<"abcb">>
+}).
+-define(SOURCE4, #{
+ <<"type">> => <<"postgresql">>,
+ <<"enable">> => true,
+ <<"server">> => <<"127.0.0.1:27017">>,
+ <<"pool_size">> => 1,
+ <<"database">> => <<"mqtt">>,
+ <<"username">> => <<"xx">>,
+ <<"password">> => <<"ee">>,
+ <<"auto_reconnect">> => true,
+ <<"ssl">> => #{<<"enable">> => false},
+ <<"query">> => <<"abcb">>
+}).
+-define(SOURCE5, #{
+ <<"type">> => <<"redis">>,
+ <<"redis_type">> => <<"single">>,
+ <<"enable">> => true,
+ <<"server">> => <<"127.0.0.1:27017">>,
+ <<"pool_size">> => 1,
+ <<"database">> => 0,
+ <<"password">> => <<"ee">>,
+ <<"auto_reconnect">> => true,
+ <<"ssl">> => #{<<"enable">> => false},
+ <<"cmd">> => <<"HGETALL mqtt_authz:", ?PH_USERNAME/binary>>
+}).
+-define(SOURCE6, #{
+ <<"type">> => <<"file">>,
+ <<"enable">> => true,
+ <<"rules">> =>
+ <<
+ "{allow,{username,\"^dashboard?\"},subscribe,[\"$SYS/#\"]}."
+ "\n{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}."
+ >>
+}).
%%------------------------------------------------------------------------------
%% Testcases
@@ -138,95 +152,130 @@ t_update_source(_) ->
{ok, _} = emqx_authz:update(?CMD_APPEND, ?SOURCE5),
{ok, _} = emqx_authz:update(?CMD_APPEND, ?SOURCE6),
- ?assertMatch([ #{type := http, enable := true}
- , #{type := mongodb, enable := true}
- , #{type := mysql, enable := true}
- , #{type := postgresql, enable := true}
- , #{type := redis, enable := true}
- , #{type := file, enable := true}
- ], emqx_conf:get([authorization, sources], [])),
+ ?assertMatch(
+ [
+ #{type := http, enable := true},
+ #{type := mongodb, enable := true},
+ #{type := mysql, enable := true},
+ #{type := postgresql, enable := true},
+ #{type := redis, enable := true},
+ #{type := file, enable := true}
+ ],
+ emqx_conf:get([authorization, sources], [])
+ ),
- {ok, _} = emqx_authz:update({?CMD_REPLACE, http}, ?SOURCE1#{<<"enable">> := true}),
+ {ok, _} = emqx_authz:update({?CMD_REPLACE, http}, ?SOURCE1#{<<"enable">> := true}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, mongodb}, ?SOURCE2#{<<"enable">> := true}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, mysql}, ?SOURCE3#{<<"enable">> := true}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, postgresql}, ?SOURCE4#{<<"enable">> := true}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, redis}, ?SOURCE5#{<<"enable">> := true}),
- {ok, _} = emqx_authz:update({?CMD_REPLACE, file}, ?SOURCE6#{<<"enable">> := true}),
+ {ok, _} = emqx_authz:update({?CMD_REPLACE, file}, ?SOURCE6#{<<"enable">> := true}),
- {ok, _} = emqx_authz:update({?CMD_REPLACE, http}, ?SOURCE1#{<<"enable">> := false}),
+ {ok, _} = emqx_authz:update({?CMD_REPLACE, http}, ?SOURCE1#{<<"enable">> := false}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, mongodb}, ?SOURCE2#{<<"enable">> := false}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, mysql}, ?SOURCE3#{<<"enable">> := false}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, postgresql}, ?SOURCE4#{<<"enable">> := false}),
{ok, _} = emqx_authz:update({?CMD_REPLACE, redis}, ?SOURCE5#{<<"enable">> := false}),
- {ok, _} = emqx_authz:update({?CMD_REPLACE, file}, ?SOURCE6#{<<"enable">> := false}),
+ {ok, _} = emqx_authz:update({?CMD_REPLACE, file}, ?SOURCE6#{<<"enable">> := false}),
- ?assertMatch([ #{type := http, enable := false}
- , #{type := mongodb, enable := false}
- , #{type := mysql, enable := false}
- , #{type := postgresql, enable := false}
- , #{type := redis, enable := false}
- , #{type := file, enable := false}
- ], emqx_conf:get([authorization, sources], [])),
+ ?assertMatch(
+ [
+ #{type := http, enable := false},
+ #{type := mongodb, enable := false},
+ #{type := mysql, enable := false},
+ #{type := postgresql, enable := false},
+ #{type := redis, enable := false},
+ #{type := file, enable := false}
+ ],
+ emqx_conf:get([authorization, sources], [])
+ ),
{ok, _} = emqx_authz:update(?CMD_REPLACE, []).
t_delete_source(_) ->
{ok, _} = emqx_authz:update(?CMD_REPLACE, [?SOURCE1]),
- ?assertMatch([ #{type := http, enable := true}
- ], emqx_conf:get([authorization, sources], [])),
+ ?assertMatch([#{type := http, enable := true}], emqx_conf:get([authorization, sources], [])),
{ok, _} = emqx_authz:update({?CMD_DELETE, http}, #{}),
?assertMatch([], emqx_conf:get([authorization, sources], [])).
t_move_source(_) ->
- {ok, _} = emqx_authz:update(?CMD_REPLACE,
- [?SOURCE1, ?SOURCE2, ?SOURCE3,
- ?SOURCE4, ?SOURCE5, ?SOURCE6]),
- ?assertMatch([ #{type := http}
- , #{type := mongodb}
- , #{type := mysql}
- , #{type := postgresql}
- , #{type := redis}
- , #{type := file}
- ], emqx_authz:lookup()),
+ {ok, _} = emqx_authz:update(
+ ?CMD_REPLACE,
+ [
+ ?SOURCE1,
+ ?SOURCE2,
+ ?SOURCE3,
+ ?SOURCE4,
+ ?SOURCE5,
+ ?SOURCE6
+ ]
+ ),
+ ?assertMatch(
+ [
+ #{type := http},
+ #{type := mongodb},
+ #{type := mysql},
+ #{type := postgresql},
+ #{type := redis},
+ #{type := file}
+ ],
+ emqx_authz:lookup()
+ ),
{ok, _} = emqx_authz:move(postgresql, ?CMD_MOVE_FRONT),
- ?assertMatch([ #{type := postgresql}
- , #{type := http}
- , #{type := mongodb}
- , #{type := mysql}
- , #{type := redis}
- , #{type := file}
- ], emqx_authz:lookup()),
+ ?assertMatch(
+ [
+ #{type := postgresql},
+ #{type := http},
+ #{type := mongodb},
+ #{type := mysql},
+ #{type := redis},
+ #{type := file}
+ ],
+ emqx_authz:lookup()
+ ),
{ok, _} = emqx_authz:move(http, ?CMD_MOVE_REAR),
- ?assertMatch([ #{type := postgresql}
- , #{type := mongodb}
- , #{type := mysql}
- , #{type := redis}
- , #{type := file}
- , #{type := http}
- ], emqx_authz:lookup()),
+ ?assertMatch(
+ [
+ #{type := postgresql},
+ #{type := mongodb},
+ #{type := mysql},
+ #{type := redis},
+ #{type := file},
+ #{type := http}
+ ],
+ emqx_authz:lookup()
+ ),
{ok, _} = emqx_authz:move(mysql, ?CMD_MOVE_BEFORE(postgresql)),
- ?assertMatch([ #{type := mysql}
- , #{type := postgresql}
- , #{type := mongodb}
- , #{type := redis}
- , #{type := file}
- , #{type := http}
- ], emqx_authz:lookup()),
+ ?assertMatch(
+ [
+ #{type := mysql},
+ #{type := postgresql},
+ #{type := mongodb},
+ #{type := redis},
+ #{type := file},
+ #{type := http}
+ ],
+ emqx_authz:lookup()
+ ),
{ok, _} = emqx_authz:move(mongodb, ?CMD_MOVE_AFTER(http)),
- ?assertMatch([ #{type := mysql}
- , #{type := postgresql}
- , #{type := redis}
- , #{type := file}
- , #{type := http}
- , #{type := mongodb}
- ], emqx_authz:lookup()),
+ ?assertMatch(
+ [
+ #{type := mysql},
+ #{type := postgresql},
+ #{type := redis},
+ #{type := file},
+ #{type := http},
+ #{type := mongodb}
+ ],
+ emqx_authz:lookup()
+ ),
ok.
diff --git a/apps/emqx_authz/test/emqx_authz_api_mnesia_SUITE.erl b/apps/emqx_authz/test/emqx_authz_api_mnesia_SUITE.erl
index ee7f68c14..385a10eb9 100644
--- a/apps/emqx_authz/test/emqx_authz_api_mnesia_SUITE.erl
+++ b/apps/emqx_authz/test/emqx_authz_api_mnesia_SUITE.erl
@@ -32,16 +32,20 @@ groups() ->
init_per_suite(Config) ->
ok = emqx_common_test_helpers:start_apps(
- [emqx_conf, emqx_authz, emqx_dashboard],
- fun set_special_configs/1),
+ [emqx_conf, emqx_authz, emqx_dashboard],
+ fun set_special_configs/1
+ ),
Config.
end_per_suite(_Config) ->
{ok, _} = emqx:update_config(
- [authorization],
- #{<<"no_match">> => <<"allow">>,
- <<"cache">> => #{<<"enable">> => <<"true">>},
- <<"sources">> => []}),
+ [authorization],
+ #{
+ <<"no_match">> => <<"allow">>,
+ <<"cache">> => #{<<"enable">> => <<"true">>},
+ <<"sources">> => []
+ }
+ ),
emqx_common_test_helpers:stop_apps([emqx_dashboard, emqx_authz, emqx_conf]),
ok.
@@ -50,8 +54,10 @@ set_special_configs(emqx_dashboard) ->
set_special_configs(emqx_authz) ->
{ok, _} = emqx:update_config([authorization, cache, enable], false),
{ok, _} = emqx:update_config([authorization, no_match], deny),
- {ok, _} = emqx:update_config([authorization, sources],
- [#{<<"type">> => <<"built_in_database">>}]),
+ {ok, _} = emqx:update_config(
+ [authorization, sources],
+ [#{<<"type">> => <<"built_in_database">>}]
+ ),
ok;
set_special_configs(_App) ->
ok.
@@ -62,176 +68,248 @@ set_special_configs(_App) ->
t_api(_) ->
{ok, 204, _} =
- request( post
- , uri(["authorization", "sources", "built_in_database", "username"])
- , [?USERNAME_RULES_EXAMPLE]),
+ request(
+ post,
+ uri(["authorization", "sources", "built_in_database", "username"]),
+ [?USERNAME_RULES_EXAMPLE]
+ ),
{ok, 200, Request1} =
- request( get
- , uri(["authorization", "sources", "built_in_database", "username"])
- , []),
- #{<<"data">> := [#{<<"username">> := <<"user1">>, <<"rules">> := Rules1}],
- <<"meta">> := #{<<"count">> := 1,
- <<"limit">> := 100,
- <<"page">> := 1}} = jsx:decode(Request1),
+ request(
+ get,
+ uri(["authorization", "sources", "built_in_database", "username"]),
+ []
+ ),
+ #{
+ <<"data">> := [#{<<"username">> := <<"user1">>, <<"rules">> := Rules1}],
+ <<"meta">> := #{
+ <<"count">> := 1,
+ <<"limit">> := 100,
+ <<"page">> := 1
+ }
+ } = jsx:decode(Request1),
?assertEqual(3, length(Rules1)),
{ok, 200, Request1_1} =
- request( get
- , uri([ "authorization"
- , "sources"
- , "built_in_database"
- , "username?page=1&limit=20&like_username=noexist"])
- , []),
- #{<<"data">> := [],
- <<"meta">> := #{<<"count">> := 0,
- <<"limit">> := 20,
- <<"page">> := 1}} = jsx:decode(Request1_1),
-
+ request(
+ get,
+ uri([
+ "authorization",
+ "sources",
+ "built_in_database",
+ "username?page=1&limit=20&like_username=noexist"
+ ]),
+ []
+ ),
+ #{
+ <<"data">> := [],
+ <<"meta">> := #{
+ <<"count">> := 0,
+ <<"limit">> := 20,
+ <<"page">> := 1
+ }
+ } = jsx:decode(Request1_1),
{ok, 200, Request2} =
- request( get
- , uri(["authorization", "sources", "built_in_database", "username", "user1"])
- , []),
+ request(
+ get,
+ uri(["authorization", "sources", "built_in_database", "username", "user1"]),
+ []
+ ),
#{<<"username">> := <<"user1">>, <<"rules">> := Rules1} = jsx:decode(Request2),
-
{ok, 204, _} =
- request( put
- , uri(["authorization", "sources", "built_in_database", "username", "user1"])
- , ?USERNAME_RULES_EXAMPLE#{rules => []}),
+ request(
+ put,
+ uri(["authorization", "sources", "built_in_database", "username", "user1"]),
+ ?USERNAME_RULES_EXAMPLE#{rules => []}
+ ),
{ok, 200, Request3} =
- request( get
- , uri(["authorization", "sources", "built_in_database", "username", "user1"])
- , []),
+ request(
+ get,
+ uri(["authorization", "sources", "built_in_database", "username", "user1"]),
+ []
+ ),
#{<<"username">> := <<"user1">>, <<"rules">> := Rules2} = jsx:decode(Request3),
?assertEqual(0, length(Rules2)),
{ok, 204, _} =
- request( delete
- , uri(["authorization", "sources", "built_in_database", "username", "user1"])
- , []),
+ request(
+ delete,
+ uri(["authorization", "sources", "built_in_database", "username", "user1"]),
+ []
+ ),
{ok, 404, _} =
- request( get
- , uri(["authorization", "sources", "built_in_database", "username", "user1"])
- , []),
+ request(
+ get,
+ uri(["authorization", "sources", "built_in_database", "username", "user1"]),
+ []
+ ),
{ok, 404, _} =
- request( delete
- , uri(["authorization", "sources", "built_in_database", "username", "user1"])
- , []),
-
+ request(
+ delete,
+ uri(["authorization", "sources", "built_in_database", "username", "user1"]),
+ []
+ ),
{ok, 204, _} =
- request( post
- , uri(["authorization", "sources", "built_in_database", "clientid"])
- , [?CLIENTID_RULES_EXAMPLE]),
+ request(
+ post,
+ uri(["authorization", "sources", "built_in_database", "clientid"]),
+ [?CLIENTID_RULES_EXAMPLE]
+ ),
{ok, 200, Request4} =
- request( get
- , uri(["authorization", "sources", "built_in_database", "clientid"])
- , []),
+ request(
+ get,
+ uri(["authorization", "sources", "built_in_database", "clientid"]),
+ []
+ ),
{ok, 200, Request5} =
- request( get
- , uri(["authorization", "sources", "built_in_database", "clientid", "client1"])
- , []),
- #{<<"data">> := [#{<<"clientid">> := <<"client1">>, <<"rules">> := Rules3}],
- <<"meta">> := #{<<"count">> := 1, <<"limit">> := 100, <<"page">> := 1}}
- = jsx:decode(Request4),
+ request(
+ get,
+ uri(["authorization", "sources", "built_in_database", "clientid", "client1"]),
+ []
+ ),
+ #{
+ <<"data">> := [#{<<"clientid">> := <<"client1">>, <<"rules">> := Rules3}],
+ <<"meta">> := #{<<"count">> := 1, <<"limit">> := 100, <<"page">> := 1}
+ } =
+ jsx:decode(Request4),
#{<<"clientid">> := <<"client1">>, <<"rules">> := Rules3} = jsx:decode(Request5),
?assertEqual(3, length(Rules3)),
{ok, 204, _} =
- request( put
- , uri(["authorization", "sources", "built_in_database", "clientid", "client1"])
- , ?CLIENTID_RULES_EXAMPLE#{rules => []}),
+ request(
+ put,
+ uri(["authorization", "sources", "built_in_database", "clientid", "client1"]),
+ ?CLIENTID_RULES_EXAMPLE#{rules => []}
+ ),
{ok, 200, Request6} =
- request( get
- , uri(["authorization", "sources", "built_in_database", "clientid", "client1"])
- , []),
+ request(
+ get,
+ uri(["authorization", "sources", "built_in_database", "clientid", "client1"]),
+ []
+ ),
#{<<"clientid">> := <<"client1">>, <<"rules">> := Rules4} = jsx:decode(Request6),
?assertEqual(0, length(Rules4)),
{ok, 204, _} =
- request( delete
- , uri(["authorization", "sources", "built_in_database", "clientid", "client1"])
- , []),
+ request(
+ delete,
+ uri(["authorization", "sources", "built_in_database", "clientid", "client1"]),
+ []
+ ),
{ok, 404, _} =
- request( get
- , uri(["authorization", "sources", "built_in_database", "clientid", "client1"])
- , []),
+ request(
+ get,
+ uri(["authorization", "sources", "built_in_database", "clientid", "client1"]),
+ []
+ ),
{ok, 404, _} =
- request( delete
- , uri(["authorization", "sources", "built_in_database", "clientid", "client1"])
- , []),
-
+ request(
+ delete,
+ uri(["authorization", "sources", "built_in_database", "clientid", "client1"]),
+ []
+ ),
{ok, 204, _} =
- request( post
- , uri(["authorization", "sources", "built_in_database", "all"])
- , ?ALL_RULES_EXAMPLE),
+ request(
+ post,
+ uri(["authorization", "sources", "built_in_database", "all"]),
+ ?ALL_RULES_EXAMPLE
+ ),
{ok, 200, Request7} =
- request( get
- , uri(["authorization", "sources", "built_in_database", "all"])
- , []),
+ request(
+ get,
+ uri(["authorization", "sources", "built_in_database", "all"]),
+ []
+ ),
#{<<"rules">> := Rules5} = jsx:decode(Request7),
?assertEqual(3, length(Rules5)),
{ok, 204, _} =
- request( post
- , uri(["authorization", "sources", "built_in_database", "all"])
+ request(
+ post,
+ uri(["authorization", "sources", "built_in_database", "all"]),
- , ?ALL_RULES_EXAMPLE#{rules => []}),
+ ?ALL_RULES_EXAMPLE#{rules => []}
+ ),
{ok, 200, Request8} =
- request( get
- , uri(["authorization", "sources", "built_in_database", "all"])
- , []),
+ request(
+ get,
+ uri(["authorization", "sources", "built_in_database", "all"]),
+ []
+ ),
#{<<"rules">> := Rules6} = jsx:decode(Request8),
?assertEqual(0, length(Rules6)),
{ok, 204, _} =
- request( post
- , uri(["authorization", "sources", "built_in_database", "username"])
- , [ #{username => erlang:integer_to_binary(N), rules => []}
- || N <- lists:seq(1, 20) ]),
+ request(
+ post,
+ uri(["authorization", "sources", "built_in_database", "username"]),
+ [
+ #{username => erlang:integer_to_binary(N), rules => []}
+ || N <- lists:seq(1, 20)
+ ]
+ ),
{ok, 200, Request9} =
- request( get
- , uri(["authorization", "sources", "built_in_database", "username?page=2&limit=5"])
- , []),
+ request(
+ get,
+ uri(["authorization", "sources", "built_in_database", "username?page=2&limit=5"]),
+ []
+ ),
#{<<"data">> := Data1} = jsx:decode(Request9),
?assertEqual(5, length(Data1)),
{ok, 204, _} =
- request( post
- , uri(["authorization", "sources", "built_in_database", "clientid"])
- , [ #{clientid => erlang:integer_to_binary(N), rules => []}
- || N <- lists:seq(1, 20) ]),
+ request(
+ post,
+ uri(["authorization", "sources", "built_in_database", "clientid"]),
+ [
+ #{clientid => erlang:integer_to_binary(N), rules => []}
+ || N <- lists:seq(1, 20)
+ ]
+ ),
{ok, 200, Request10} =
- request( get
- , uri(["authorization", "sources", "built_in_database", "clientid?limit=5"])
- , []),
+ request(
+ get,
+ uri(["authorization", "sources", "built_in_database", "clientid?limit=5"]),
+ []
+ ),
#{<<"data">> := Data2} = jsx:decode(Request10),
?assertEqual(5, length(Data2)),
{ok, 400, Msg1} =
- request( delete
- , uri(["authorization", "sources", "built_in_database", "purge-all"])
- , []),
+ request(
+ delete,
+ uri(["authorization", "sources", "built_in_database", "purge-all"]),
+ []
+ ),
?assertMatch({match, _}, re:run(Msg1, "must\sbe\sdisabled\sbefore")),
{ok, 204, _} =
- request( put
- , uri(["authorization", "sources", "built_in_database"])
- , #{<<"enable">> => true, <<"type">> => <<"built_in_database">>}),
+ request(
+ put,
+ uri(["authorization", "sources", "built_in_database"]),
+ #{<<"enable">> => true, <<"type">> => <<"built_in_database">>}
+ ),
%% test idempotence
{ok, 204, _} =
- request( put
- , uri(["authorization", "sources", "built_in_database"])
- , #{<<"enable">> => true, <<"type">> => <<"built_in_database">>}),
+ request(
+ put,
+ uri(["authorization", "sources", "built_in_database"]),
+ #{<<"enable">> => true, <<"type">> => <<"built_in_database">>}
+ ),
{ok, 204, _} =
- request( put
- , uri(["authorization", "sources", "built_in_database"])
- , #{<<"enable">> => false, <<"type">> => <<"built_in_database">>}),
+ request(
+ put,
+ uri(["authorization", "sources", "built_in_database"]),
+ #{<<"enable">> => false, <<"type">> => <<"built_in_database">>}
+ ),
{ok, 204, _} =
- request( delete
- , uri(["authorization", "sources", "built_in_database", "purge-all"])
- , []),
+ request(
+ delete,
+ uri(["authorization", "sources", "built_in_database", "purge-all"]),
+ []
+ ),
?assertEqual(0, emqx_authz_mnesia:record_count()),
ok.
diff --git a/apps/emqx_authz/test/emqx_authz_api_settings_SUITE.erl b/apps/emqx_authz/test/emqx_authz_api_settings_SUITE.erl
index a14535018..b53b7aa1b 100644
--- a/apps/emqx_authz/test/emqx_authz_api_settings_SUITE.erl
+++ b/apps/emqx_authz/test/emqx_authz_api_settings_SUITE.erl
@@ -31,16 +31,20 @@ groups() ->
init_per_suite(Config) ->
ok = emqx_common_test_helpers:start_apps(
- [emqx_conf, emqx_authz, emqx_dashboard],
- fun set_special_configs/1),
+ [emqx_conf, emqx_authz, emqx_dashboard],
+ fun set_special_configs/1
+ ),
Config.
end_per_suite(_Config) ->
{ok, _} = emqx:update_config(
- [authorization],
- #{<<"no_match">> => <<"allow">>,
- <<"cache">> => #{<<"enable">> => <<"true">>},
- <<"sources">> => []}),
+ [authorization],
+ #{
+ <<"no_match">> => <<"allow">>,
+ <<"cache">> => #{<<"enable">> => <<"true">>},
+ <<"sources">> => []
+ }
+ ),
ok = stop_apps([emqx_resource, emqx_connector]),
emqx_common_test_helpers:stop_apps([emqx_dashboard, emqx_authz, emqx_conf]),
ok.
@@ -60,27 +64,29 @@ set_special_configs(_App) ->
%%------------------------------------------------------------------------------
t_api(_) ->
- Settings1 = #{<<"no_match">> => <<"deny">>,
- <<"deny_action">> => <<"disconnect">>,
- <<"cache">> => #{
- <<"enable">> => false,
- <<"max_size">> => 32,
- <<"ttl">> => 60000
- }
- },
+ Settings1 = #{
+ <<"no_match">> => <<"deny">>,
+ <<"deny_action">> => <<"disconnect">>,
+ <<"cache">> => #{
+ <<"enable">> => false,
+ <<"max_size">> => 32,
+ <<"ttl">> => 60000
+ }
+ },
{ok, 200, Result1} = request(put, uri(["authorization", "settings"]), Settings1),
{ok, 200, Result1} = request(get, uri(["authorization", "settings"]), []),
?assertEqual(Settings1, jsx:decode(Result1)),
- Settings2 = #{<<"no_match">> => <<"allow">>,
- <<"deny_action">> => <<"ignore">>,
- <<"cache">> => #{
- <<"enable">> => true,
- <<"max_size">> => 32,
- <<"ttl">> => 60000
- }
- },
+ Settings2 = #{
+ <<"no_match">> => <<"allow">>,
+ <<"deny_action">> => <<"ignore">>,
+ <<"cache">> => #{
+ <<"enable">> => true,
+ <<"max_size">> => 32,
+ <<"ttl">> => 60000
+ }
+ },
{ok, 200, Result2} = request(put, uri(["authorization", "settings"]), Settings2),
{ok, 200, Result2} = request(get, uri(["authorization", "settings"]), []),
diff --git a/apps/emqx_authz/test/emqx_authz_api_sources_SUITE.erl b/apps/emqx_authz/test/emqx_authz_api_sources_SUITE.erl
index 737091585..fe445fbf0 100644
--- a/apps/emqx_authz/test/emqx_authz_api_sources_SUITE.erl
+++ b/apps/emqx_authz/test/emqx_authz_api_sources_SUITE.erl
@@ -29,62 +29,70 @@
-define(PGSQL_HOST, "pgsql").
-define(REDIS_SINGLE_HOST, "redis").
--define(SOURCE1, #{<<"type">> => <<"http">>,
- <<"enable">> => true,
- <<"url">> => <<"https://fake.com:443/acl?username=", ?PH_USERNAME/binary>>,
- <<"headers">> => #{},
- <<"method">> => <<"get">>,
- <<"request_timeout">> => <<"5s">>
- }).
--define(SOURCE2, #{<<"type">> => <<"mongodb">>,
- <<"enable">> => true,
- <<"mongo_type">> => <<"single">>,
- <<"server">> => <>,
- <<"w_mode">> => <<"unsafe">>,
- <<"pool_size">> => 1,
- <<"database">> => <<"mqtt">>,
- <<"ssl">> => #{<<"enable">> => false},
- <<"collection">> => <<"fake">>,
- <<"selector">> => #{<<"a">> => <<"b">>}
- }).
--define(SOURCE3, #{<<"type">> => <<"mysql">>,
- <<"enable">> => true,
- <<"server">> => <>,
- <<"pool_size">> => 1,
- <<"database">> => <<"mqtt">>,
- <<"username">> => <<"xx">>,
- <<"password">> => <<"ee">>,
- <<"auto_reconnect">> => true,
- <<"ssl">> => #{<<"enable">> => false},
- <<"query">> => <<"abcb">>
- }).
--define(SOURCE4, #{<<"type">> => <<"postgresql">>,
- <<"enable">> => true,
- <<"server">> => <>,
- <<"pool_size">> => 1,
- <<"database">> => <<"mqtt">>,
- <<"username">> => <<"xx">>,
- <<"password">> => <<"ee">>,
- <<"auto_reconnect">> => true,
- <<"ssl">> => #{<<"enable">> => false},
- <<"query">> => <<"abcb">>
- }).
--define(SOURCE5, #{<<"type">> => <<"redis">>,
- <<"enable">> => true,
- <<"servers">> => <>,
- <<"pool_size">> => 1,
- <<"database">> => 0,
- <<"password">> => <<"ee">>,
- <<"auto_reconnect">> => true,
- <<"ssl">> => #{<<"enable">> => false},
- <<"cmd">> => <<"HGETALL mqtt_authz:", ?PH_USERNAME/binary>>
- }).
--define(SOURCE6, #{<<"type">> => <<"file">>,
- <<"enable">> => true,
- <<"rules">> =>
-<<"{allow,{username,\"^dashboard?\"},subscribe,[\"$SYS/#\"]}."
- "\n{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}.">>
- }).
+-define(SOURCE1, #{
+ <<"type">> => <<"http">>,
+ <<"enable">> => true,
+ <<"url">> => <<"https://fake.com:443/acl?username=", ?PH_USERNAME/binary>>,
+ <<"headers">> => #{},
+ <<"method">> => <<"get">>,
+ <<"request_timeout">> => <<"5s">>
+}).
+-define(SOURCE2, #{
+ <<"type">> => <<"mongodb">>,
+ <<"enable">> => true,
+ <<"mongo_type">> => <<"single">>,
+ <<"server">> => <>,
+ <<"w_mode">> => <<"unsafe">>,
+ <<"pool_size">> => 1,
+ <<"database">> => <<"mqtt">>,
+ <<"ssl">> => #{<<"enable">> => false},
+ <<"collection">> => <<"fake">>,
+ <<"selector">> => #{<<"a">> => <<"b">>}
+}).
+-define(SOURCE3, #{
+ <<"type">> => <<"mysql">>,
+ <<"enable">> => true,
+ <<"server">> => <>,
+ <<"pool_size">> => 1,
+ <<"database">> => <<"mqtt">>,
+ <<"username">> => <<"xx">>,
+ <<"password">> => <<"ee">>,
+ <<"auto_reconnect">> => true,
+ <<"ssl">> => #{<<"enable">> => false},
+ <<"query">> => <<"abcb">>
+}).
+-define(SOURCE4, #{
+ <<"type">> => <<"postgresql">>,
+ <<"enable">> => true,
+ <<"server">> => <>,
+ <<"pool_size">> => 1,
+ <<"database">> => <<"mqtt">>,
+ <<"username">> => <<"xx">>,
+ <<"password">> => <<"ee">>,
+ <<"auto_reconnect">> => true,
+ <<"ssl">> => #{<<"enable">> => false},
+ <<"query">> => <<"abcb">>
+}).
+-define(SOURCE5, #{
+ <<"type">> => <<"redis">>,
+ <<"enable">> => true,
+ <<"servers">> => <>,
+ <<"pool_size">> => 1,
+ <<"database">> => 0,
+ <<"password">> => <<"ee">>,
+ <<"auto_reconnect">> => true,
+ <<"ssl">> => #{<<"enable">> => false},
+ <<"cmd">> => <<"HGETALL mqtt_authz:", ?PH_USERNAME/binary>>
+}).
+-define(SOURCE6, #{
+ <<"type">> => <<"file">>,
+ <<"enable">> => true,
+ <<"rules">> =>
+ <<
+ "{allow,{username,\"^dashboard?\"},subscribe,[\"$SYS/#\"]}."
+ "\n{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}."
+ >>
+}).
-define(MATCH_RSA_KEY, <<"-----BEGIN RSA PRIVATE KEY", _/binary>>).
-define(MATCH_CERT, <<"-----BEGIN CERTIFICATE", _/binary>>).
@@ -100,24 +108,31 @@ init_per_suite(Config) ->
meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]),
meck:expect(emqx_resource, create_local, fun(_, _, _, _) -> {ok, meck_data} end),
meck:expect(emqx_resource, health_check, fun(St) -> {ok, St} end),
- meck:expect(emqx_resource, remove_local, fun(_) -> ok end ),
- meck:expect(emqx_authz, acl_conf_file,
- fun() ->
- emqx_common_test_helpers:deps_path(emqx_authz, "etc/acl.conf")
- end),
+ meck:expect(emqx_resource, remove_local, fun(_) -> ok end),
+ meck:expect(
+ emqx_authz,
+ acl_conf_file,
+ fun() ->
+ emqx_common_test_helpers:deps_path(emqx_authz, "etc/acl.conf")
+ end
+ ),
ok = emqx_common_test_helpers:start_apps(
- [emqx_conf, emqx_authz, emqx_dashboard],
- fun set_special_configs/1),
+ [emqx_conf, emqx_authz, emqx_dashboard],
+ fun set_special_configs/1
+ ),
ok = start_apps([emqx_resource, emqx_connector]),
Config.
end_per_suite(_Config) ->
{ok, _} = emqx:update_config(
- [authorization],
- #{<<"no_match">> => <<"allow">>,
- <<"cache">> => #{<<"enable">> => <<"true">>},
- <<"sources">> => []}),
+ [authorization],
+ #{
+ <<"no_match">> => <<"allow">>,
+ <<"cache">> => #{<<"enable">> => <<"true">>},
+ <<"sources">> => []
+ }
+ ),
%% resource and connector should be stop first,
%% or authz_[mysql|pgsql|redis..]_SUITE would be failed
ok = stop_apps([emqx_resource, emqx_connector]),
@@ -140,19 +155,24 @@ init_per_testcase(t_api, Config) ->
meck:expect(emqx_misc, gen_id, fun() -> "fake" end),
meck:new(emqx, [non_strict, passthrough, no_history, no_link]),
- meck:expect(emqx, data_dir,
- fun() ->
- {data_dir, Data} = lists:keyfind(data_dir, 1, Config),
- Data
- end),
+ meck:expect(
+ emqx,
+ data_dir,
+ fun() ->
+ {data_dir, Data} = lists:keyfind(data_dir, 1, Config),
+ Data
+ end
+ ),
Config;
-init_per_testcase(_, Config) -> Config.
+init_per_testcase(_, Config) ->
+ Config.
end_per_testcase(t_api, _Config) ->
meck:unload(emqx_misc),
meck:unload(emqx),
ok;
-end_per_testcase(_, _Config) -> ok.
+end_per_testcase(_, _Config) ->
+ ok.
%%------------------------------------------------------------------------------
%% Testcases
@@ -162,139 +182,190 @@ t_api(_) ->
{ok, 200, Result1} = request(get, uri(["authorization", "sources"]), []),
?assertEqual([], get_sources(Result1)),
- [ begin {ok, 204, _} = request(post, uri(["authorization", "sources"]), Source) end
- || Source <- lists:reverse([?SOURCE2, ?SOURCE3, ?SOURCE4, ?SOURCE5, ?SOURCE6])],
+ [
+ begin
+ {ok, 204, _} = request(post, uri(["authorization", "sources"]), Source)
+ end
+ || Source <- lists:reverse([?SOURCE2, ?SOURCE3, ?SOURCE4, ?SOURCE5, ?SOURCE6])
+ ],
{ok, 204, _} = request(post, uri(["authorization", "sources"]), ?SOURCE1),
- Snd = fun ({_, Val}) -> Val end,
+ Snd = fun({_, Val}) -> Val end,
LookupVal = fun LookupV(List, RestJson) ->
- case List of
- [Name] -> Snd(lists:keyfind(Name, 1, RestJson));
- [Name | NS] -> LookupV(NS, Snd(lists:keyfind(Name, 1, RestJson)))
- end
- end,
- EqualFun = fun (RList) ->
- fun ({M, V}) ->
- ?assertEqual(V,
- LookupVal([<<"metrics">>, M],
- RList)
- )
- end
- end,
+ case List of
+ [Name] -> Snd(lists:keyfind(Name, 1, RestJson));
+ [Name | NS] -> LookupV(NS, Snd(lists:keyfind(Name, 1, RestJson)))
+ end
+ end,
+ EqualFun = fun(RList) ->
+ fun({M, V}) ->
+ ?assertEqual(
+ V,
+ LookupVal(
+ [<<"metrics">>, M],
+ RList
+ )
+ )
+ end
+ end,
AssertFun =
- fun (ResultJson) ->
+ fun(ResultJson) ->
{ok, RList} = emqx_json:safe_decode(ResultJson),
- MetricsList = [{<<"failed">>, 0},
- {<<"matched">>, 0},
- {<<"rate">>, 0.0},
- {<<"rate_last5m">>, 0.0},
- {<<"rate_max">>, 0.0},
- {<<"success">>, 0}],
+ MetricsList = [
+ {<<"failed">>, 0},
+ {<<"matched">>, 0},
+ {<<"rate">>, 0.0},
+ {<<"rate_last5m">>, 0.0},
+ {<<"rate_max">>, 0.0},
+ {<<"success">>, 0}
+ ],
lists:map(EqualFun(RList), MetricsList)
end,
{ok, 200, Result2} = request(get, uri(["authorization", "sources"]), []),
Sources = get_sources(Result2),
- ?assertMatch([ #{<<"type">> := <<"http">>}
- , #{<<"type">> := <<"mongodb">>}
- , #{<<"type">> := <<"mysql">>}
- , #{<<"type">> := <<"postgresql">>}
- , #{<<"type">> := <<"redis">>}
- , #{<<"type">> := <<"file">>}
- ], Sources),
+ ?assertMatch(
+ [
+ #{<<"type">> := <<"http">>},
+ #{<<"type">> := <<"mongodb">>},
+ #{<<"type">> := <<"mysql">>},
+ #{<<"type">> := <<"postgresql">>},
+ #{<<"type">> := <<"redis">>},
+ #{<<"type">> := <<"file">>}
+ ],
+ Sources
+ ),
?assert(filelib:is_file(emqx_authz:acl_conf_file())),
- {ok, 204, _} = request(put, uri(["authorization", "sources", "http"]),
- ?SOURCE1#{<<"enable">> := false}),
+ {ok, 204, _} = request(
+ put,
+ uri(["authorization", "sources", "http"]),
+ ?SOURCE1#{<<"enable">> := false}
+ ),
{ok, 200, Result3} = request(get, uri(["authorization", "sources", "http"]), []),
?assertMatch(#{<<"type">> := <<"http">>, <<"enable">> := false}, jsx:decode(Result3)),
Keyfile = emqx_common_test_helpers:app_path(
- emqx,
- filename:join(["etc", "certs", "key.pem"])),
+ emqx,
+ filename:join(["etc", "certs", "key.pem"])
+ ),
Certfile = emqx_common_test_helpers:app_path(
- emqx,
- filename:join(["etc", "certs", "cert.pem"])),
+ emqx,
+ filename:join(["etc", "certs", "cert.pem"])
+ ),
Cacertfile = emqx_common_test_helpers:app_path(
- emqx,
- filename:join(["etc", "certs", "cacert.pem"])),
+ emqx,
+ filename:join(["etc", "certs", "cacert.pem"])
+ ),
- {ok, 204, _} = request(put, uri(["authorization", "sources", "mongodb"]),
- ?SOURCE2#{<<"ssl">> => #{
- <<"enable">> => <<"true">>,
- <<"cacertfile">> => Cacertfile,
- <<"certfile">> => Certfile,
- <<"keyfile">> => Keyfile,
- <<"verify">> => <<"verify_none">>
- }}),
+ {ok, 204, _} = request(
+ put,
+ uri(["authorization", "sources", "mongodb"]),
+ ?SOURCE2#{
+ <<"ssl">> => #{
+ <<"enable">> => <<"true">>,
+ <<"cacertfile">> => Cacertfile,
+ <<"certfile">> => Certfile,
+ <<"keyfile">> => Keyfile,
+ <<"verify">> => <<"verify_none">>
+ }
+ }
+ ),
{ok, 200, Result4} = request(get, uri(["authorization", "sources", "mongodb"]), []),
{ok, 200, Status4} = request(get, uri(["authorization", "sources", "mongodb", "status"]), []),
AssertFun(Status4),
- ?assertMatch(#{<<"type">> := <<"mongodb">>,
- <<"ssl">> := #{<<"enable">> := <<"true">>,
- <<"cacertfile">> := ?MATCH_CERT,
- <<"certfile">> := ?MATCH_CERT,
- <<"keyfile">> := ?MATCH_RSA_KEY,
- <<"verify">> := <<"verify_none">>
- }
- }, jsx:decode(Result4)),
+ ?assertMatch(
+ #{
+ <<"type">> := <<"mongodb">>,
+ <<"ssl">> := #{
+ <<"enable">> := <<"true">>,
+ <<"cacertfile">> := ?MATCH_CERT,
+ <<"certfile">> := ?MATCH_CERT,
+ <<"keyfile">> := ?MATCH_RSA_KEY,
+ <<"verify">> := <<"verify_none">>
+ }
+ },
+ jsx:decode(Result4)
+ ),
{ok, Cacert} = file:read_file(Cacertfile),
{ok, Cert} = file:read_file(Certfile),
{ok, Key} = file:read_file(Keyfile),
- {ok, 204, _} = request(put, uri(["authorization", "sources", "mongodb"]),
- ?SOURCE2#{<<"ssl">> => #{
- <<"enable">> => <<"true">>,
- <<"cacertfile">> => Cacert,
- <<"certfile">> => Cert,
- <<"keyfile">> => Key,
- <<"verify">> => <<"verify_none">>
- }}),
+ {ok, 204, _} = request(
+ put,
+ uri(["authorization", "sources", "mongodb"]),
+ ?SOURCE2#{
+ <<"ssl">> => #{
+ <<"enable">> => <<"true">>,
+ <<"cacertfile">> => Cacert,
+ <<"certfile">> => Cert,
+ <<"keyfile">> => Key,
+ <<"verify">> => <<"verify_none">>
+ }
+ }
+ ),
{ok, 200, Result5} = request(get, uri(["authorization", "sources", "mongodb"]), []),
{ok, 200, Status5} = request(get, uri(["authorization", "sources", "mongodb", "status"]), []),
AssertFun(Status5),
- ?assertMatch(#{<<"type">> := <<"mongodb">>,
- <<"ssl">> := #{<<"enable">> := <<"true">>,
- <<"cacertfile">> := ?MATCH_CERT,
- <<"certfile">> := ?MATCH_CERT,
- <<"keyfile">> := ?MATCH_RSA_KEY,
- <<"verify">> := <<"verify_none">>
- }
- }, jsx:decode(Result5)),
+ ?assertMatch(
+ #{
+ <<"type">> := <<"mongodb">>,
+ <<"ssl">> := #{
+ <<"enable">> := <<"true">>,
+ <<"cacertfile">> := ?MATCH_CERT,
+ <<"certfile">> := ?MATCH_CERT,
+ <<"keyfile">> := ?MATCH_RSA_KEY,
+ <<"verify">> := <<"verify_none">>
+ }
+ },
+ jsx:decode(Result5)
+ ),
-
- #{ssl := #{cacertfile := SavedCacertfile,
- certfile := SavedCertfile,
- keyfile := SavedKeyfile
- }} = emqx_authz:lookup(mongodb),
+ #{
+ ssl := #{
+ cacertfile := SavedCacertfile,
+ certfile := SavedCertfile,
+ keyfile := SavedKeyfile
+ }
+ } = emqx_authz:lookup(mongodb),
?assert(filelib:is_file(SavedCacertfile)),
?assert(filelib:is_file(SavedCertfile)),
?assert(filelib:is_file(SavedKeyfile)),
{ok, 204, _} = request(
- put,
- uri(["authorization", "sources", "mysql"]),
- ?SOURCE3#{<<"server">> := <<"192.168.1.100:3306">>}),
+ put,
+ uri(["authorization", "sources", "mysql"]),
+ ?SOURCE3#{<<"server">> := <<"192.168.1.100:3306">>}
+ ),
{ok, 204, _} = request(
- put,
- uri(["authorization", "sources", "postgresql"]),
- ?SOURCE4#{<<"server">> := <<"fake">>}),
+ put,
+ uri(["authorization", "sources", "postgresql"]),
+ ?SOURCE4#{<<"server">> := <<"fake">>}
+ ),
{ok, 204, _} = request(
- put,
- uri(["authorization", "sources", "redis"]),
- ?SOURCE5#{<<"servers">> := [<<"192.168.1.100:6379">>,
- <<"192.168.1.100:6380">>]}),
+ put,
+ uri(["authorization", "sources", "redis"]),
+ ?SOURCE5#{
+ <<"servers">> := [
+ <<"192.168.1.100:6379">>,
+ <<"192.168.1.100:6380">>
+ ]
+ }
+ ),
lists:foreach(
- fun(#{<<"type">> := Type}) ->
- {ok, 204, _} = request(
- delete,
- uri(["authorization", "sources", binary_to_list(Type)]),
- [])
- end, Sources),
+ fun(#{<<"type">> := Type}) ->
+ {ok, 204, _} = request(
+ delete,
+ uri(["authorization", "sources", binary_to_list(Type)]),
+ []
+ )
+ end,
+ Sources
+ ),
{ok, 200, Result6} = request(get, uri(["authorization", "sources"]), []),
?assertEqual([], get_sources(Result6)),
?assertEqual([], emqx:get_config([authorization, sources])),
@@ -302,48 +373,80 @@ t_api(_) ->
t_move_source(_) ->
{ok, _} = emqx_authz:update(replace, [?SOURCE1, ?SOURCE2, ?SOURCE3, ?SOURCE4, ?SOURCE5]),
- ?assertMatch([ #{type := http}
- , #{type := mongodb}
- , #{type := mysql}
- , #{type := postgresql}
- , #{type := redis}
- ], emqx_authz:lookup()),
+ ?assertMatch(
+ [
+ #{type := http},
+ #{type := mongodb},
+ #{type := mysql},
+ #{type := postgresql},
+ #{type := redis}
+ ],
+ emqx_authz:lookup()
+ ),
- {ok, 204, _} = request(post, uri(["authorization", "sources", "postgresql", "move"]),
- #{<<"position">> => <<"front">>}),
- ?assertMatch([ #{type := postgresql}
- , #{type := http}
- , #{type := mongodb}
- , #{type := mysql}
- , #{type := redis}
- ], emqx_authz:lookup()),
+ {ok, 204, _} = request(
+ post,
+ uri(["authorization", "sources", "postgresql", "move"]),
+ #{<<"position">> => <<"front">>}
+ ),
+ ?assertMatch(
+ [
+ #{type := postgresql},
+ #{type := http},
+ #{type := mongodb},
+ #{type := mysql},
+ #{type := redis}
+ ],
+ emqx_authz:lookup()
+ ),
- {ok, 204, _} = request(post, uri(["authorization", "sources", "http", "move"]),
- #{<<"position">> => <<"rear">>}),
- ?assertMatch([ #{type := postgresql}
- , #{type := mongodb}
- , #{type := mysql}
- , #{type := redis}
- , #{type := http}
- ], emqx_authz:lookup()),
+ {ok, 204, _} = request(
+ post,
+ uri(["authorization", "sources", "http", "move"]),
+ #{<<"position">> => <<"rear">>}
+ ),
+ ?assertMatch(
+ [
+ #{type := postgresql},
+ #{type := mongodb},
+ #{type := mysql},
+ #{type := redis},
+ #{type := http}
+ ],
+ emqx_authz:lookup()
+ ),
- {ok, 204, _} = request(post, uri(["authorization", "sources", "mysql", "move"]),
- #{<<"position">> => <<"before:postgresql">>}),
- ?assertMatch([ #{type := mysql}
- , #{type := postgresql}
- , #{type := mongodb}
- , #{type := redis}
- , #{type := http}
- ], emqx_authz:lookup()),
+ {ok, 204, _} = request(
+ post,
+ uri(["authorization", "sources", "mysql", "move"]),
+ #{<<"position">> => <<"before:postgresql">>}
+ ),
+ ?assertMatch(
+ [
+ #{type := mysql},
+ #{type := postgresql},
+ #{type := mongodb},
+ #{type := redis},
+ #{type := http}
+ ],
+ emqx_authz:lookup()
+ ),
- {ok, 204, _} = request(post, uri(["authorization", "sources", "mongodb", "move"]),
- #{<<"position">> => <<"after:http">>}),
- ?assertMatch([ #{type := mysql}
- , #{type := postgresql}
- , #{type := redis}
- , #{type := http}
- , #{type := mongodb}
- ], emqx_authz:lookup()),
+ {ok, 204, _} = request(
+ post,
+ uri(["authorization", "sources", "mongodb", "move"]),
+ #{<<"position">> => <<"after:http">>}
+ ),
+ ?assertMatch(
+ [
+ #{type := mysql},
+ #{type := postgresql},
+ #{type := redis},
+ #{type := http},
+ #{type := mongodb}
+ ],
+ emqx_authz:lookup()
+ ),
ok.
diff --git a/apps/emqx_authz/test/emqx_authz_file_SUITE.erl b/apps/emqx_authz/test/emqx_authz_file_SUITE.erl
index fb9849a26..9e7767d1e 100644
--- a/apps/emqx_authz/test/emqx_authz_file_SUITE.erl
+++ b/apps/emqx_authz/test/emqx_authz_file_SUITE.erl
@@ -22,12 +22,15 @@
-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
--define(RAW_SOURCE, #{<<"type">> => <<"file">>,
- <<"enable">> => true,
- <<"rules">> =>
-<<"{allow,{username,\"^dashboard?\"},subscribe,[\"$SYS/#\"]}."
- "\n{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}.">>
- }).
+-define(RAW_SOURCE, #{
+ <<"type">> => <<"file">>,
+ <<"enable">> => true,
+ <<"rules">> =>
+ <<
+ "{allow,{username,\"^dashboard?\"},subscribe,[\"$SYS/#\"]}."
+ "\n{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}."
+ >>
+}).
all() ->
emqx_common_test_helpers:all(?MODULE).
@@ -37,13 +40,17 @@ groups() ->
init_per_suite(Config) ->
ok = emqx_common_test_helpers:start_apps(
- [emqx_conf, emqx_authz],
- fun set_special_configs/1),
+ [emqx_conf, emqx_authz],
+ fun set_special_configs/1
+ ),
%% meck after authz started
- meck:expect(emqx_authz, acl_conf_file,
- fun() ->
- emqx_common_test_helpers:deps_path(emqx_authz, "etc/acl.conf")
- end),
+ meck:expect(
+ emqx_authz,
+ acl_conf_file,
+ fun() ->
+ emqx_common_test_helpers:deps_path(emqx_authz, "etc/acl.conf")
+ end
+ ),
Config.
end_per_suite(_Config) ->
@@ -57,7 +64,6 @@ init_per_testcase(_TestCase, Config) ->
set_special_configs(emqx_authz) ->
ok = emqx_authz_test_lib:reset_authorizers();
-
set_special_configs(_) ->
ok.
@@ -66,43 +72,55 @@ set_special_configs(_) ->
%%------------------------------------------------------------------------------
t_ok(_Config) ->
- ClientInfo = #{clientid => <<"clientid">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
- ok = setup_config(?RAW_SOURCE#{<<"rules">> => <<"{allow, {user, \"username\"}, publish, [\"t\"]}.">>}),
+ ok = setup_config(?RAW_SOURCE#{
+ <<"rules">> => <<"{allow, {user, \"username\"}, publish, [\"t\"]}.">>
+ }),
io:format("~p", [emqx_authz:acl_conf_file()]),
?assertEqual(
- allow,
- emqx_access_control:authorize(ClientInfo, publish, <<"t">>)),
+ allow,
+ emqx_access_control:authorize(ClientInfo, publish, <<"t">>)
+ ),
?assertEqual(
- deny,
- emqx_access_control:authorize(ClientInfo, subscribe, <<"t">>)).
+ deny,
+ emqx_access_control:authorize(ClientInfo, subscribe, <<"t">>)
+ ).
t_invalid_file(_Config) ->
?assertMatch(
- {error, bad_acl_file_content},
- emqx_authz:update(?CMD_REPLACE, [?RAW_SOURCE#{<<"rules">> => <<"{{invalid term">>}])).
+ {error, bad_acl_file_content},
+ emqx_authz:update(?CMD_REPLACE, [?RAW_SOURCE#{<<"rules">> => <<"{{invalid term">>}])
+ ).
t_update(_Config) ->
- ok = setup_config(?RAW_SOURCE#{<<"rules">> => <<"{allow, {user, \"username\"}, publish, [\"t\"]}.">>}),
+ ok = setup_config(?RAW_SOURCE#{
+ <<"rules">> => <<"{allow, {user, \"username\"}, publish, [\"t\"]}.">>
+ }),
?assertMatch(
- {error, _},
- emqx_authz:update(
- {?CMD_REPLACE, file},
- ?RAW_SOURCE#{<<"rules">> => <<"{{invalid term">>})),
+ {error, _},
+ emqx_authz:update(
+ {?CMD_REPLACE, file},
+ ?RAW_SOURCE#{<<"rules">> => <<"{{invalid term">>}
+ )
+ ),
?assertMatch(
- {ok, _},
- emqx_authz:update(
- {?CMD_REPLACE, file}, ?RAW_SOURCE)).
+ {ok, _},
+ emqx_authz:update(
+ {?CMD_REPLACE, file}, ?RAW_SOURCE
+ )
+ ).
%%------------------------------------------------------------------------------
%% Helpers
@@ -110,8 +128,9 @@ t_update(_Config) ->
setup_config(SpecialParams) ->
emqx_authz_test_lib:setup_config(
- ?RAW_SOURCE,
- SpecialParams).
+ ?RAW_SOURCE,
+ SpecialParams
+ ).
stop_apps(Apps) ->
lists:foreach(fun application:stop/1, Apps).
diff --git a/apps/emqx_authz/test/emqx_authz_http_SUITE.erl b/apps/emqx_authz/test/emqx_authz_http_SUITE.erl
index e6aeb703c..03016f7e2 100644
--- a/apps/emqx_authz/test/emqx_authz_http_SUITE.erl
+++ b/apps/emqx_authz/test/emqx_authz_http_SUITE.erl
@@ -32,9 +32,9 @@ all() ->
init_per_suite(Config) ->
ok = stop_apps([emqx_resource, emqx_connector, cowboy]),
ok = emqx_common_test_helpers:start_apps(
- [emqx_conf, emqx_authz],
- fun set_special_configs/1
- ),
+ [emqx_conf, emqx_authz],
+ fun set_special_configs/1
+ ),
ok = start_apps([emqx_resource, emqx_connector, cowboy]),
Config.
@@ -45,7 +45,6 @@ end_per_suite(_Config) ->
set_special_configs(emqx_authz) ->
ok = emqx_authz_test_lib:reset_authorizers();
-
set_special_configs(_) ->
ok.
@@ -62,251 +61,301 @@ end_per_testcase(_Case, _Config) ->
%%------------------------------------------------------------------------------
t_response_handling(_Config) ->
- ClientInfo = #{clientid => <<"clientid">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
%% OK, get, no body
ok = setup_handler_and_config(
- fun(Req0, State) ->
- Req = cowboy_req:reply(200, Req0),
- {ok, Req, State}
- end,
- #{}),
+ fun(Req0, State) ->
+ Req = cowboy_req:reply(200, Req0),
+ {ok, Req, State}
+ end,
+ #{}
+ ),
allow = emqx_access_control:authorize(ClientInfo, publish, <<"t">>),
%% OK, get, body & headers
ok = setup_handler_and_config(
- fun(Req0, State) ->
- Req = cowboy_req:reply(
- 200,
- #{<<"content-type">> => <<"text/plain">>},
- "Response body",
- Req0),
- {ok, Req, State}
- end,
- #{}),
+ fun(Req0, State) ->
+ Req = cowboy_req:reply(
+ 200,
+ #{<<"content-type">> => <<"text/plain">>},
+ "Response body",
+ Req0
+ ),
+ {ok, Req, State}
+ end,
+ #{}
+ ),
?assertEqual(
allow,
- emqx_access_control:authorize(ClientInfo, publish, <<"t">>)),
+ emqx_access_control:authorize(ClientInfo, publish, <<"t">>)
+ ),
%% OK, get, 204
ok = setup_handler_and_config(
- fun(Req0, State) ->
- Req = cowboy_req:reply(204, Req0),
- {ok, Req, State}
- end,
- #{}),
+ fun(Req0, State) ->
+ Req = cowboy_req:reply(204, Req0),
+ {ok, Req, State}
+ end,
+ #{}
+ ),
?assertEqual(
allow,
- emqx_access_control:authorize(ClientInfo, publish, <<"t">>)),
+ emqx_access_control:authorize(ClientInfo, publish, <<"t">>)
+ ),
%% Not OK, get, 400
ok = setup_handler_and_config(
- fun(Req0, State) ->
- Req = cowboy_req:reply(400, Req0),
- {ok, Req, State}
- end,
- #{}),
+ fun(Req0, State) ->
+ Req = cowboy_req:reply(400, Req0),
+ {ok, Req, State}
+ end,
+ #{}
+ ),
?assertEqual(
deny,
- emqx_access_control:authorize(ClientInfo, publish, <<"t">>)),
+ emqx_access_control:authorize(ClientInfo, publish, <<"t">>)
+ ),
%% Not OK, get, 400 + body & headers
ok = setup_handler_and_config(
- fun(Req0, State) ->
- Req = cowboy_req:reply(
- 400,
- #{<<"content-type">> => <<"text/plain">>},
- "Response body",
- Req0),
- {ok, Req, State}
- end,
- #{}),
+ fun(Req0, State) ->
+ Req = cowboy_req:reply(
+ 400,
+ #{<<"content-type">> => <<"text/plain">>},
+ "Response body",
+ Req0
+ ),
+ {ok, Req, State}
+ end,
+ #{}
+ ),
?assertEqual(
deny,
- emqx_access_control:authorize(ClientInfo, publish, <<"t">>)).
+ emqx_access_control:authorize(ClientInfo, publish, <<"t">>)
+ ).
t_query_params(_Config) ->
ok = setup_handler_and_config(
- fun(Req0, State) ->
- #{username := <<"user name">>,
- clientid := <<"client id">>,
- peerhost := <<"127.0.0.1">>,
- proto_name := <<"MQTT">>,
- mountpoint := <<"MOUNTPOINT">>,
- topic := <<"t">>,
- action := <<"publish">>
- } = cowboy_req:match_qs(
- [username,
- clientid,
- peerhost,
- proto_name,
- mountpoint,
- topic,
- action],
- Req0),
- Req = cowboy_req:reply(200, Req0),
- {ok, Req, State}
- end,
- #{<<"url">> => <<"http://127.0.0.1:33333/authz/users/?"
- "username=${username}&"
- "clientid=${clientid}&"
- "peerhost=${peerhost}&"
- "proto_name=${proto_name}&"
- "mountpoint=${mountpoint}&"
- "topic=${topic}&"
- "action=${action}">>
- }),
+ fun(Req0, State) ->
+ #{
+ username := <<"user name">>,
+ clientid := <<"client id">>,
+ peerhost := <<"127.0.0.1">>,
+ proto_name := <<"MQTT">>,
+ mountpoint := <<"MOUNTPOINT">>,
+ topic := <<"t">>,
+ action := <<"publish">>
+ } = cowboy_req:match_qs(
+ [
+ username,
+ clientid,
+ peerhost,
+ proto_name,
+ mountpoint,
+ topic,
+ action
+ ],
+ Req0
+ ),
+ Req = cowboy_req:reply(200, Req0),
+ {ok, Req, State}
+ end,
+ #{
+ <<"url">> => <<
+ "http://127.0.0.1:33333/authz/users/?"
+ "username=${username}&"
+ "clientid=${clientid}&"
+ "peerhost=${peerhost}&"
+ "proto_name=${proto_name}&"
+ "mountpoint=${mountpoint}&"
+ "topic=${topic}&"
+ "action=${action}"
+ >>
+ }
+ ),
- ClientInfo = #{clientid => <<"client id">>,
- username => <<"user name">>,
- peerhost => {127,0,0,1},
- protocol => <<"MQTT">>,
- mountpoint => <<"MOUNTPOINT">>,
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"client id">>,
+ username => <<"user name">>,
+ peerhost => {127, 0, 0, 1},
+ protocol => <<"MQTT">>,
+ mountpoint => <<"MOUNTPOINT">>,
+ zone => default,
+ listener => {tcp, default}
+ },
?assertEqual(
allow,
- emqx_access_control:authorize(ClientInfo, publish, <<"t">>)).
+ emqx_access_control:authorize(ClientInfo, publish, <<"t">>)
+ ).
t_json_body(_Config) ->
ok = setup_handler_and_config(
- fun(Req0, State) ->
- ?assertEqual(
- <<"/authz/users/">>,
- cowboy_req:path(Req0)),
+ fun(Req0, State) ->
+ ?assertEqual(
+ <<"/authz/users/">>,
+ cowboy_req:path(Req0)
+ ),
- {ok, RawBody, Req1} = cowboy_req:read_body(Req0),
+ {ok, RawBody, Req1} = cowboy_req:read_body(Req0),
- ?assertMatch(
- #{<<"username">> := <<"user name">>,
- <<"CLIENT">> := <<"client id">>,
- <<"peerhost">> := <<"127.0.0.1">>,
- <<"proto_name">> := <<"MQTT">>,
- <<"mountpoint">> := <<"MOUNTPOINT">>,
- <<"topic">> := <<"t">>,
- <<"action">> := <<"publish">>},
- jiffy:decode(RawBody, [return_maps])),
+ ?assertMatch(
+ #{
+ <<"username">> := <<"user name">>,
+ <<"CLIENT">> := <<"client id">>,
+ <<"peerhost">> := <<"127.0.0.1">>,
+ <<"proto_name">> := <<"MQTT">>,
+ <<"mountpoint">> := <<"MOUNTPOINT">>,
+ <<"topic">> := <<"t">>,
+ <<"action">> := <<"publish">>
+ },
+ jiffy:decode(RawBody, [return_maps])
+ ),
- Req = cowboy_req:reply(200, Req1),
- {ok, Req, State}
- end,
- #{<<"method">> => <<"post">>,
- <<"body">> => #{<<"username">> => <<"${username}">>,
- <<"CLIENT">> => <<"${clientid}">>,
- <<"peerhost">> => <<"${peerhost}">>,
- <<"proto_name">> => <<"${proto_name}">>,
- <<"mountpoint">> => <<"${mountpoint}">>,
- <<"topic">> => <<"${topic}">>,
- <<"action">> => <<"${action}">>}
- }),
+ Req = cowboy_req:reply(200, Req1),
+ {ok, Req, State}
+ end,
+ #{
+ <<"method">> => <<"post">>,
+ <<"body">> => #{
+ <<"username">> => <<"${username}">>,
+ <<"CLIENT">> => <<"${clientid}">>,
+ <<"peerhost">> => <<"${peerhost}">>,
+ <<"proto_name">> => <<"${proto_name}">>,
+ <<"mountpoint">> => <<"${mountpoint}">>,
+ <<"topic">> => <<"${topic}">>,
+ <<"action">> => <<"${action}">>
+ }
+ }
+ ),
- ClientInfo = #{clientid => <<"client id">>,
- username => <<"user name">>,
- peerhost => {127,0,0,1},
- protocol => <<"MQTT">>,
- mountpoint => <<"MOUNTPOINT">>,
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"client id">>,
+ username => <<"user name">>,
+ peerhost => {127, 0, 0, 1},
+ protocol => <<"MQTT">>,
+ mountpoint => <<"MOUNTPOINT">>,
+ zone => default,
+ listener => {tcp, default}
+ },
?assertEqual(
allow,
- emqx_access_control:authorize(ClientInfo, publish, <<"t">>)).
-
+ emqx_access_control:authorize(ClientInfo, publish, <<"t">>)
+ ).
t_form_body(_Config) ->
ok = setup_handler_and_config(
- fun(Req0, State) ->
- ?assertEqual(
- <<"/authz/users/">>,
- cowboy_req:path(Req0)),
+ fun(Req0, State) ->
+ ?assertEqual(
+ <<"/authz/users/">>,
+ cowboy_req:path(Req0)
+ ),
- {ok, [{PostVars, true}], Req1} = cowboy_req:read_urlencoded_body(Req0),
+ {ok, [{PostVars, true}], Req1} = cowboy_req:read_urlencoded_body(Req0),
- ?assertMatch(
- #{<<"username">> := <<"user name">>,
- <<"clientid">> := <<"client id">>,
- <<"peerhost">> := <<"127.0.0.1">>,
- <<"proto_name">> := <<"MQTT">>,
- <<"mountpoint">> := <<"MOUNTPOINT">>,
- <<"topic">> := <<"t">>,
- <<"action">> := <<"publish">>},
- jiffy:decode(PostVars, [return_maps])),
+ ?assertMatch(
+ #{
+ <<"username">> := <<"user name">>,
+ <<"clientid">> := <<"client id">>,
+ <<"peerhost">> := <<"127.0.0.1">>,
+ <<"proto_name">> := <<"MQTT">>,
+ <<"mountpoint">> := <<"MOUNTPOINT">>,
+ <<"topic">> := <<"t">>,
+ <<"action">> := <<"publish">>
+ },
+ jiffy:decode(PostVars, [return_maps])
+ ),
- Req = cowboy_req:reply(200, Req1),
- {ok, Req, State}
- end,
- #{<<"method">> => <<"post">>,
- <<"body">> => #{<<"username">> => <<"${username}">>,
- <<"clientid">> => <<"${clientid}">>,
- <<"peerhost">> => <<"${peerhost}">>,
- <<"proto_name">> => <<"${proto_name}">>,
- <<"mountpoint">> => <<"${mountpoint}">>,
- <<"topic">> => <<"${topic}">>,
- <<"action">> => <<"${action}">>},
- <<"headers">> => #{<<"content-type">> => <<"application/x-www-form-urlencoded">>}
- }),
+ Req = cowboy_req:reply(200, Req1),
+ {ok, Req, State}
+ end,
+ #{
+ <<"method">> => <<"post">>,
+ <<"body">> => #{
+ <<"username">> => <<"${username}">>,
+ <<"clientid">> => <<"${clientid}">>,
+ <<"peerhost">> => <<"${peerhost}">>,
+ <<"proto_name">> => <<"${proto_name}">>,
+ <<"mountpoint">> => <<"${mountpoint}">>,
+ <<"topic">> => <<"${topic}">>,
+ <<"action">> => <<"${action}">>
+ },
+ <<"headers">> => #{<<"content-type">> => <<"application/x-www-form-urlencoded">>}
+ }
+ ),
- ClientInfo = #{clientid => <<"client id">>,
- username => <<"user name">>,
- peerhost => {127,0,0,1},
- protocol => <<"MQTT">>,
- mountpoint => <<"MOUNTPOINT">>,
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"client id">>,
+ username => <<"user name">>,
+ peerhost => {127, 0, 0, 1},
+ protocol => <<"MQTT">>,
+ mountpoint => <<"MOUNTPOINT">>,
+ zone => default,
+ listener => {tcp, default}
+ },
?assertEqual(
allow,
- emqx_access_control:authorize(ClientInfo, publish, <<"t">>)).
-
+ emqx_access_control:authorize(ClientInfo, publish, <<"t">>)
+ ).
t_create_replace(_Config) ->
- ClientInfo = #{clientid => <<"clientid">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
%% Create with valid URL
ok = setup_handler_and_config(
- fun(Req0, State) ->
- Req = cowboy_req:reply(200, Req0),
- {ok, Req, State}
- end,
- #{<<"url">> =>
- <<"http://127.0.0.1:33333/authz/users/?topic=${topic}&action=${action}">>}),
+ fun(Req0, State) ->
+ Req = cowboy_req:reply(200, Req0),
+ {ok, Req, State}
+ end,
+ #{
+ <<"url">> =>
+ <<"http://127.0.0.1:33333/authz/users/?topic=${topic}&action=${action}">>
+ }
+ ),
?assertEqual(
allow,
- emqx_access_control:authorize(ClientInfo, publish, <<"t">>)),
+ emqx_access_control:authorize(ClientInfo, publish, <<"t">>)
+ ),
%% Changing to valid config
OkConfig = maps:merge(
- raw_http_authz_config(),
- #{<<"url">> =>
- <<"http://127.0.0.1:33333/authz/users/?topic=${topic}&action=${action}">>}),
+ raw_http_authz_config(),
+ #{
+ <<"url">> =>
+ <<"http://127.0.0.1:33333/authz/users/?topic=${topic}&action=${action}">>
+ }
+ ),
?assertMatch(
{ok, _},
- emqx_authz:update({?CMD_REPLACE, http}, OkConfig)),
+ emqx_authz:update({?CMD_REPLACE, http}, OkConfig)
+ ),
?assertEqual(
allow,
- emqx_access_control:authorize(ClientInfo, publish, <<"t">>)).
+ emqx_access_control:authorize(ClientInfo, publish, <<"t">>)
+ ).
%%------------------------------------------------------------------------------
%% Helpers
@@ -324,8 +373,9 @@ raw_http_authz_config() ->
setup_handler_and_config(Handler, Config) ->
ok = emqx_authz_http_test_server:set_handler(Handler),
ok = emqx_authz_test_lib:setup_config(
- raw_http_authz_config(),
- Config).
+ raw_http_authz_config(),
+ Config
+ ).
start_apps(Apps) ->
lists:foreach(fun application:ensure_all_started/1, Apps).
diff --git a/apps/emqx_authz/test/emqx_authz_http_test_server.erl b/apps/emqx_authz/test/emqx_authz_http_test_server.erl
index d1c3e2959..ea67f363b 100644
--- a/apps/emqx_authz/test/emqx_authz_http_test_server.erl
+++ b/apps/emqx_authz/test/emqx_authz_http_test_server.erl
@@ -26,10 +26,11 @@
-export([init/1]).
% API
--export([start_link/2,
- stop/0,
- set_handler/1
- ]).
+-export([
+ start_link/2,
+ stop/0,
+ set_handler/1
+]).
%%------------------------------------------------------------------------------
%% API
@@ -51,11 +52,14 @@ set_handler(F) when is_function(F, 2) ->
init([Port, Path]) ->
Dispatch = cowboy_router:compile(
- [
- {'_', [{Path, ?MODULE, []}]}
- ]),
- TransOpts = #{socket_opts => [{port, Port}],
- connection_type => supervisor},
+ [
+ {'_', [{Path, ?MODULE, []}]}
+ ]
+ ),
+ TransOpts = #{
+ socket_opts => [{port, Port}],
+ connection_type => supervisor
+ },
ProtoOpts = #{env => #{dispatch => Dispatch}},
Tab = ets:new(?MODULE, [set, named_table, public]),
@@ -78,9 +82,9 @@ init(Req, State) ->
default_handler(Req0, State) ->
Req = cowboy_req:reply(
- 400,
- #{<<"content-type">> => <<"text/plain">>},
- <<"">>,
- Req0),
+ 400,
+ #{<<"content-type">> => <<"text/plain">>},
+ <<"">>,
+ Req0
+ ),
{ok, Req, State}.
-
diff --git a/apps/emqx_authz/test/emqx_authz_mnesia_SUITE.erl b/apps/emqx_authz/test/emqx_authz_mnesia_SUITE.erl
index 0f926f1bd..8c0f282e8 100644
--- a/apps/emqx_authz/test/emqx_authz_mnesia_SUITE.erl
+++ b/apps/emqx_authz/test/emqx_authz_mnesia_SUITE.erl
@@ -29,8 +29,9 @@ groups() ->
init_per_suite(Config) ->
ok = emqx_common_test_helpers:start_apps(
- [emqx_conf, emqx_authz],
- fun set_special_configs/1),
+ [emqx_conf, emqx_authz],
+ fun set_special_configs/1
+ ),
Config.
end_per_suite(_Config) ->
@@ -47,7 +48,6 @@ end_per_testcase(_TestCase, _Config) ->
set_special_configs(emqx_authz) ->
ok = emqx_authz_test_lib:reset_authorizers();
-
set_special_configs(_) ->
ok.
@@ -64,16 +64,17 @@ t_all_topic_rules(_Config) ->
ok = test_topic_rules(all).
test_topic_rules(Key) ->
- ClientInfo = #{clientid => <<"clientid">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
SetupSamples = fun(CInfo, Samples) ->
- setup_client_samples(CInfo, Samples, Key)
- end,
+ setup_client_samples(CInfo, Samples, Key)
+ end,
ok = emqx_authz_test_lib:test_no_topic_rules(ClientInfo, SetupSamples),
@@ -82,41 +83,50 @@ test_topic_rules(Key) ->
ok = emqx_authz_test_lib:test_deny_topic_rules(ClientInfo, SetupSamples).
t_normalize_rules(_Config) ->
- ClientInfo = #{clientid => <<"clientid">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
ok = emqx_authz_mnesia:store_rules(
- {username, <<"username">>},
- [{allow, publish, "t"}]),
+ {username, <<"username">>},
+ [{allow, publish, "t"}]
+ ),
?assertEqual(
allow,
- emqx_access_control:authorize(ClientInfo, publish, <<"t">>)),
+ emqx_access_control:authorize(ClientInfo, publish, <<"t">>)
+ ),
?assertException(
- error,
- {invalid_rule, _},
- emqx_authz_mnesia:store_rules(
- {username, <<"username">>},
- [[allow, publish, <<"t">>]])),
+ error,
+ {invalid_rule, _},
+ emqx_authz_mnesia:store_rules(
+ {username, <<"username">>},
+ [[allow, publish, <<"t">>]]
+ )
+ ),
?assertException(
- error,
- {invalid_rule_action, _},
- emqx_authz_mnesia:store_rules(
- {username, <<"username">>},
- [{allow, pub, <<"t">>}])),
+ error,
+ {invalid_rule_action, _},
+ emqx_authz_mnesia:store_rules(
+ {username, <<"username">>},
+ [{allow, pub, <<"t">>}]
+ )
+ ),
?assertException(
- error,
- {invalid_rule_permission, _},
- emqx_authz_mnesia:store_rules(
- {username, <<"username">>},
- [{accept, publish, <<"t">>}])).
+ error,
+ {invalid_rule_permission, _},
+ emqx_authz_mnesia:store_rules(
+ {username, <<"username">>},
+ [{accept, publish, <<"t">>}]
+ )
+ ).
%%------------------------------------------------------------------------------
%% Helpers
@@ -131,20 +141,23 @@ raw_mnesia_authz_config() ->
setup_client_samples(ClientInfo, Samples, Key) ->
ok = emqx_authz_mnesia:purge_rules(),
Rules = lists:flatmap(
- fun(#{topics := Topics, permission := Permission, action := Action}) ->
- lists:map(
- fun(Topic) ->
- {binary_to_atom(Permission), binary_to_atom(Action), Topic}
- end,
- Topics)
- end,
- Samples),
+ fun(#{topics := Topics, permission := Permission, action := Action}) ->
+ lists:map(
+ fun(Topic) ->
+ {binary_to_atom(Permission), binary_to_atom(Action), Topic}
+ end,
+ Topics
+ )
+ end,
+ Samples
+ ),
#{username := Username, clientid := ClientId} = ClientInfo,
- Who = case Key of
- username -> {username, Username};
- clientid -> {clientid, ClientId};
- all -> all
- end,
+ Who =
+ case Key of
+ username -> {username, Username};
+ clientid -> {clientid, ClientId};
+ all -> all
+ end,
ok = emqx_authz_mnesia:store_rules(Who, Rules).
setup_config() ->
diff --git a/apps/emqx_authz/test/emqx_authz_mongodb_SUITE.erl b/apps/emqx_authz/test/emqx_authz_mongodb_SUITE.erl
index 26eaf5a45..e0220488f 100644
--- a/apps/emqx_authz/test/emqx_authz_mongodb_SUITE.erl
+++ b/apps/emqx_authz/test/emqx_authz_mongodb_SUITE.erl
@@ -38,9 +38,9 @@ init_per_suite(Config) ->
case emqx_common_test_helpers:is_tcp_server_available(?MONGO_HOST, ?MONGO_DEFAULT_PORT) of
true ->
ok = emqx_common_test_helpers:start_apps(
- [emqx_conf, emqx_authz],
- fun set_special_configs/1
- ),
+ [emqx_conf, emqx_authz],
+ fun set_special_configs/1
+ ),
ok = start_apps([emqx_resource, emqx_connector]),
Config;
false ->
@@ -54,7 +54,6 @@ end_per_suite(_Config) ->
set_special_configs(emqx_authz) ->
ok = emqx_authz_test_lib:reset_authorizers();
-
set_special_configs(_) ->
ok.
@@ -72,12 +71,13 @@ end_per_testcase(_TestCase, _Config) ->
%%------------------------------------------------------------------------------
t_topic_rules(_Config) ->
- ClientInfo = #{clientid => <<"clientid">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
ok = emqx_authz_test_lib:test_no_topic_rules(ClientInfo, fun setup_client_samples/2),
@@ -87,104 +87,137 @@ t_topic_rules(_Config) ->
t_complex_selector(_) ->
%% atom and string values also supported
- ClientInfo = #{clientid => clientid,
- username => "username",
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => clientid,
+ username => "username",
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
- Samples = [#{<<"x">> => #{<<"u">> => <<"username">>,
- <<"c">> => [#{<<"c">> => <<"clientid">>}],
- <<"y">> => 1},
- <<"permission">> => <<"allow">>,
- <<"action">> => <<"publish">>,
- <<"topics">> => [<<"t">>]
- }],
+ Samples = [
+ #{
+ <<"x">> => #{
+ <<"u">> => <<"username">>,
+ <<"c">> => [#{<<"c">> => <<"clientid">>}],
+ <<"y">> => 1
+ },
+ <<"permission">> => <<"allow">>,
+ <<"action">> => <<"publish">>,
+ <<"topics">> => [<<"t">>]
+ }
+ ],
ok = setup_samples(Samples),
ok = setup_config(
- #{<<"selector">> => #{<<"x">> => #{<<"u">> => <<"${username}">>,
- <<"c">> => [#{<<"c">> => <<"${clientid}">>}],
- <<"y">> => 1}
- }
- }),
+ #{
+ <<"selector">> => #{
+ <<"x">> => #{
+ <<"u">> => <<"${username}">>,
+ <<"c">> => [#{<<"c">> => <<"${clientid}">>}],
+ <<"y">> => 1
+ }
+ }
+ }
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, publish, <<"t">>}]).
+ ClientInfo,
+ [{allow, publish, <<"t">>}]
+ ).
t_mongo_error(_Config) ->
- ClientInfo = #{clientid => <<"clientid">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
ok = setup_samples([]),
ok = setup_config(
- #{<<"selector">> => #{<<"$badoperator">> => <<"$badoperator">>}}),
+ #{<<"selector">> => #{<<"$badoperator">> => <<"$badoperator">>}}
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{deny, publish, <<"t">>}]).
+ ClientInfo,
+ [{deny, publish, <<"t">>}]
+ ).
t_lookups(_Config) ->
- ClientInfo = #{clientid => <<"clientid">>,
- cn => <<"cn">>,
- dn => <<"dn">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ cn => <<"cn">>,
+ dn => <<"dn">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
- ByClientid = #{<<"clientid">> => <<"clientid">>,
- <<"topics">> => [<<"a">>],
- <<"action">> => <<"all">>,
- <<"permission">> => <<"allow">>},
+ ByClientid = #{
+ <<"clientid">> => <<"clientid">>,
+ <<"topics">> => [<<"a">>],
+ <<"action">> => <<"all">>,
+ <<"permission">> => <<"allow">>
+ },
ok = setup_samples([ByClientid]),
ok = setup_config(
- #{<<"selector">> => #{<<"clientid">> => <<"${clientid}">>}}),
+ #{<<"selector">> => #{<<"clientid">> => <<"${clientid}">>}}
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]),
+ ClientInfo,
+ [
+ {allow, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ),
- ByPeerhost = #{<<"peerhost">> => <<"127.0.0.1">>,
- <<"topics">> => [<<"a">>],
- <<"action">> => <<"all">>,
- <<"permission">> => <<"allow">>},
+ ByPeerhost = #{
+ <<"peerhost">> => <<"127.0.0.1">>,
+ <<"topics">> => [<<"a">>],
+ <<"action">> => <<"all">>,
+ <<"permission">> => <<"allow">>
+ },
ok = setup_samples([ByPeerhost]),
ok = setup_config(
- #{<<"selector">> => #{<<"peerhost">> => <<"${peerhost}">>}}),
+ #{<<"selector">> => #{<<"peerhost">> => <<"${peerhost}">>}}
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]).
+ ClientInfo,
+ [
+ {allow, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ).
t_bad_selector(_Config) ->
- ClientInfo = #{clientid => <<"clientid">>,
- cn => <<"cn">>,
- dn => <<"dn">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ cn => <<"cn">>,
+ dn => <<"dn">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
ok = setup_config(
- #{<<"selector">> => #{<<"$in">> => #{<<"a">> => 1}}}),
+ #{<<"selector">> => #{<<"$in">> => #{<<"a">> => 1}}}
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{deny, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]).
+ ClientInfo,
+ [
+ {deny, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ).
%%------------------------------------------------------------------------------
%% Helpers
@@ -201,17 +234,22 @@ setup_samples(AclRecords) ->
setup_client_samples(ClientInfo, Samples) ->
#{username := Username} = ClientInfo,
Records = lists:map(
- fun(Sample) ->
- #{topics := Topics,
- permission := Permission,
- action := Action} = Sample,
+ fun(Sample) ->
+ #{
+ topics := Topics,
+ permission := Permission,
+ action := Action
+ } = Sample,
- #{<<"topics">> => Topics,
- <<"permission">> => Permission,
- <<"action">> => Action,
- <<"username">> => Username}
- end,
- Samples),
+ #{
+ <<"topics">> => Topics,
+ <<"permission">> => Permission,
+ <<"action">> => Action,
+ <<"username">> => Username
+ }
+ end,
+ Samples
+ ),
setup_samples(Records),
setup_config(#{<<"selector">> => #{<<"username">> => <<"${username}">>}}).
@@ -221,8 +259,9 @@ reset_samples() ->
setup_config(SpecialParams) ->
emqx_authz_test_lib:setup_config(
- raw_mongo_authz_config(),
- SpecialParams).
+ raw_mongo_authz_config(),
+ SpecialParams
+ ).
raw_mongo_authz_config() ->
#{
@@ -238,14 +277,14 @@ raw_mongo_authz_config() ->
}.
mongo_server() ->
- iolist_to_binary(io_lib:format("~s",[?MONGO_HOST])).
+ iolist_to_binary(io_lib:format("~s", [?MONGO_HOST])).
mongo_config() ->
[
- {database, <<"mqtt">>},
- {host, ?MONGO_HOST},
- {port, ?MONGO_DEFAULT_PORT},
- {register, ?MONGO_CLIENT}
+ {database, <<"mqtt">>},
+ {host, ?MONGO_HOST},
+ {port, ?MONGO_DEFAULT_PORT},
+ {register, ?MONGO_CLIENT}
].
start_apps(Apps) ->
diff --git a/apps/emqx_authz/test/emqx_authz_mysql_SUITE.erl b/apps/emqx_authz/test/emqx_authz_mysql_SUITE.erl
index 193ed80fa..ce8d03984 100644
--- a/apps/emqx_authz/test/emqx_authz_mysql_SUITE.erl
+++ b/apps/emqx_authz/test/emqx_authz_mysql_SUITE.erl
@@ -37,16 +37,17 @@ init_per_suite(Config) ->
case emqx_common_test_helpers:is_tcp_server_available(?MYSQL_HOST, ?MYSQL_DEFAULT_PORT) of
true ->
ok = emqx_common_test_helpers:start_apps(
- [emqx_conf, emqx_authz],
- fun set_special_configs/1
- ),
+ [emqx_conf, emqx_authz],
+ fun set_special_configs/1
+ ),
ok = start_apps([emqx_resource, emqx_connector]),
{ok, _} = emqx_resource:create_local(
- ?MYSQL_RESOURCE,
- ?RESOURCE_GROUP,
- emqx_connector_mysql,
- mysql_config(),
- #{}),
+ ?MYSQL_RESOURCE,
+ ?RESOURCE_GROUP,
+ emqx_connector_mysql,
+ mysql_config(),
+ #{}
+ ),
Config;
false ->
{skip, no_mysql}
@@ -64,7 +65,6 @@ init_per_testcase(_TestCase, Config) ->
set_special_configs(emqx_authz) ->
ok = emqx_authz_test_lib:reset_authorizers();
-
set_special_configs(_) ->
ok.
@@ -73,12 +73,13 @@ set_special_configs(_) ->
%%------------------------------------------------------------------------------
t_topic_rules(_Config) ->
- ClientInfo = #{clientid => <<"clientid">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
ok = emqx_authz_test_lib:test_no_topic_rules(ClientInfo, fun setup_client_samples/2),
@@ -86,127 +87,190 @@ t_topic_rules(_Config) ->
ok = emqx_authz_test_lib:test_deny_topic_rules(ClientInfo, fun setup_client_samples/2).
-
t_lookups(_Config) ->
- ClientInfo = #{clientid => <<"clientid">>,
- cn => <<"cn">>,
- dn => <<"dn">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ cn => <<"cn">>,
+ dn => <<"dn">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
%% by clientid
ok = init_table(),
- ok = q(<<"INSERT INTO acl(clientid, topic, permission, action)"
- "VALUES(?, ?, ?, ?)">>,
- [<<"clientid">>, <<"a">>, <<"allow">>, <<"subscribe">>]),
+ ok = q(
+ <<
+ "INSERT INTO acl(clientid, topic, permission, action)"
+ "VALUES(?, ?, ?, ?)"
+ >>,
+ [<<"clientid">>, <<"a">>, <<"allow">>, <<"subscribe">>]
+ ),
ok = setup_config(
- #{<<"query">> => <<"SELECT permission, action, topic "
- "FROM acl WHERE clientid = ${clientid}">>}),
+ #{
+ <<"query">> => <<
+ "SELECT permission, action, topic "
+ "FROM acl WHERE clientid = ${clientid}"
+ >>
+ }
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]),
+ ClientInfo,
+ [
+ {allow, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ),
%% by peerhost
ok = init_table(),
- ok = q(<<"INSERT INTO acl(peerhost, topic, permission, action)"
- "VALUES(?, ?, ?, ?)">>,
- [<<"127.0.0.1">>, <<"a">>, <<"allow">>, <<"subscribe">>]),
+ ok = q(
+ <<
+ "INSERT INTO acl(peerhost, topic, permission, action)"
+ "VALUES(?, ?, ?, ?)"
+ >>,
+ [<<"127.0.0.1">>, <<"a">>, <<"allow">>, <<"subscribe">>]
+ ),
ok = setup_config(
- #{<<"query">> => <<"SELECT permission, action, topic "
- "FROM acl WHERE peerhost = ${peerhost}">>}),
+ #{
+ <<"query">> => <<
+ "SELECT permission, action, topic "
+ "FROM acl WHERE peerhost = ${peerhost}"
+ >>
+ }
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]),
+ ClientInfo,
+ [
+ {allow, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ),
%% by cn
ok = init_table(),
- ok = q(<<"INSERT INTO acl(cn, topic, permission, action)"
- "VALUES(?, ?, ?, ?)">>,
- [<<"cn">>, <<"a">>, <<"allow">>, <<"subscribe">>]),
+ ok = q(
+ <<
+ "INSERT INTO acl(cn, topic, permission, action)"
+ "VALUES(?, ?, ?, ?)"
+ >>,
+ [<<"cn">>, <<"a">>, <<"allow">>, <<"subscribe">>]
+ ),
ok = setup_config(
- #{<<"query">> => <<"SELECT permission, action, topic "
- "FROM acl WHERE cn = ${cert_common_name}">>}),
+ #{
+ <<"query">> => <<
+ "SELECT permission, action, topic "
+ "FROM acl WHERE cn = ${cert_common_name}"
+ >>
+ }
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]),
+ ClientInfo,
+ [
+ {allow, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ),
%% by dn
ok = init_table(),
- ok = q(<<"INSERT INTO acl(dn, topic, permission, action)"
- "VALUES(?, ?, ?, ?)">>,
- [<<"dn">>, <<"a">>, <<"allow">>, <<"subscribe">>]),
+ ok = q(
+ <<
+ "INSERT INTO acl(dn, topic, permission, action)"
+ "VALUES(?, ?, ?, ?)"
+ >>,
+ [<<"dn">>, <<"a">>, <<"allow">>, <<"subscribe">>]
+ ),
ok = setup_config(
- #{<<"query">> => <<"SELECT permission, action, topic "
- "FROM acl WHERE dn = ${cert_subject}">>}),
+ #{
+ <<"query">> => <<
+ "SELECT permission, action, topic "
+ "FROM acl WHERE dn = ${cert_subject}"
+ >>
+ }
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]).
+ ClientInfo,
+ [
+ {allow, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ).
t_mysql_error(_Config) ->
- ClientInfo = #{clientid => <<"clientid">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
ok = setup_config(
- #{<<"query">> => <<"SOME INVALID STATEMENT">>}),
+ #{<<"query">> => <<"SOME INVALID STATEMENT">>}
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{deny, subscribe, <<"a">>}]).
-
+ ClientInfo,
+ [{deny, subscribe, <<"a">>}]
+ ).
t_create_invalid(_Config) ->
BadConfig = maps:merge(
- raw_mysql_authz_config(),
- #{<<"server">> => <<"255.255.255.255:33333">>}),
+ raw_mysql_authz_config(),
+ #{<<"server">> => <<"255.255.255.255:33333">>}
+ ),
{ok, _} = emqx_authz:update(?CMD_REPLACE, [BadConfig]),
[_] = emqx_authz:lookup().
t_nonbinary_values(_Config) ->
- ClientInfo = #{clientid => clientid,
- username => "username",
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
-
+ ClientInfo = #{
+ clientid => clientid,
+ username => "username",
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
ok = init_table(),
- ok = q(<<"INSERT INTO acl(clientid, username, topic, permission, action)"
- "VALUES(?, ?, ?, ?, ?)">>,
- [<<"clientid">>, <<"username">>, <<"a">>, <<"allow">>, <<"subscribe">>]),
+ ok = q(
+ <<
+ "INSERT INTO acl(clientid, username, topic, permission, action)"
+ "VALUES(?, ?, ?, ?, ?)"
+ >>,
+ [<<"clientid">>, <<"username">>, <<"a">>, <<"allow">>, <<"subscribe">>]
+ ),
ok = setup_config(
- #{<<"query">> => <<"SELECT permission, action, topic "
- "FROM acl WHERE clientid = ${clientid} AND username = ${username}">>}),
+ #{
+ <<"query">> => <<
+ "SELECT permission, action, topic "
+ "FROM acl WHERE clientid = ${clientid} AND username = ${username}"
+ >>
+ }
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]).
+ ClientInfo,
+ [
+ {allow, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ).
%%------------------------------------------------------------------------------
%% Helpers
@@ -221,33 +285,39 @@ raw_mysql_authz_config() ->
<<"username">> => <<"root">>,
<<"password">> => <<"public">>,
- <<"query">> => <<"SELECT permission, action, topic "
- "FROM acl WHERE username = ${username}">>,
+ <<"query">> => <<
+ "SELECT permission, action, topic "
+ "FROM acl WHERE username = ${username}"
+ >>,
<<"server">> => mysql_server()
}.
q(Sql) ->
emqx_resource:query(
- ?MYSQL_RESOURCE,
- {sql, Sql}).
+ ?MYSQL_RESOURCE,
+ {sql, Sql}
+ ).
q(Sql, Params) ->
emqx_resource:query(
- ?MYSQL_RESOURCE,
- {sql, Sql, Params}).
+ ?MYSQL_RESOURCE,
+ {sql, Sql, Params}
+ ).
init_table() ->
ok = drop_table(),
- ok = q("CREATE TABLE acl(
- username VARCHAR(255),
- clientid VARCHAR(255),
- peerhost VARCHAR(255),
- cn VARCHAR(255),
- dn VARCHAR(255),
- topic VARCHAR(255),
- permission VARCHAR(255),
- action VARCHAR(255))").
+ ok = q(
+ "CREATE TABLE acl(\n"
+ " username VARCHAR(255),\n"
+ " clientid VARCHAR(255),\n"
+ " peerhost VARCHAR(255),\n"
+ " cn VARCHAR(255),\n"
+ " dn VARCHAR(255),\n"
+ " topic VARCHAR(255),\n"
+ " permission VARCHAR(255),\n"
+ " action VARCHAR(255))"
+ ).
drop_table() ->
ok = q("DROP TABLE IF EXISTS acl").
@@ -256,37 +326,50 @@ setup_client_samples(ClientInfo, Samples) ->
#{username := Username} = ClientInfo,
ok = init_table(),
ok = lists:foreach(
- fun(#{topics := Topics, permission := Permission, action := Action}) ->
- lists:foreach(
- fun(Topic) ->
- q(<<"INSERT INTO acl(username, topic, permission, action)"
- "VALUES(?, ?, ?, ?)">>,
- [Username, Topic, Permission, Action])
- end,
- Topics)
- end,
- Samples),
+ fun(#{topics := Topics, permission := Permission, action := Action}) ->
+ lists:foreach(
+ fun(Topic) ->
+ q(
+ <<
+ "INSERT INTO acl(username, topic, permission, action)"
+ "VALUES(?, ?, ?, ?)"
+ >>,
+ [Username, Topic, Permission, Action]
+ )
+ end,
+ Topics
+ )
+ end,
+ Samples
+ ),
setup_config(
- #{<<"query">> => <<"SELECT permission, action, topic "
- "FROM acl WHERE username = ${username}">>}).
+ #{
+ <<"query">> => <<
+ "SELECT permission, action, topic "
+ "FROM acl WHERE username = ${username}"
+ >>
+ }
+ ).
setup_config(SpecialParams) ->
emqx_authz_test_lib:setup_config(
- raw_mysql_authz_config(),
- SpecialParams).
+ raw_mysql_authz_config(),
+ SpecialParams
+ ).
mysql_server() ->
- iolist_to_binary(io_lib:format("~s",[?MYSQL_HOST])).
+ iolist_to_binary(io_lib:format("~s", [?MYSQL_HOST])).
mysql_config() ->
- #{auto_reconnect => true,
- database => <<"mqtt">>,
- username => <<"root">>,
- password => <<"public">>,
- pool_size => 8,
- server => {?MYSQL_HOST, ?MYSQL_DEFAULT_PORT},
- ssl => #{enable => false}
- }.
+ #{
+ auto_reconnect => true,
+ database => <<"mqtt">>,
+ username => <<"root">>,
+ password => <<"public">>,
+ pool_size => 8,
+ server => {?MYSQL_HOST, ?MYSQL_DEFAULT_PORT},
+ ssl => #{enable => false}
+ }.
start_apps(Apps) ->
lists:foreach(fun application:ensure_all_started/1, Apps).
diff --git a/apps/emqx_authz/test/emqx_authz_postgresql_SUITE.erl b/apps/emqx_authz/test/emqx_authz_postgresql_SUITE.erl
index 1785656aa..d4aaf7077 100644
--- a/apps/emqx_authz/test/emqx_authz_postgresql_SUITE.erl
+++ b/apps/emqx_authz/test/emqx_authz_postgresql_SUITE.erl
@@ -37,16 +37,17 @@ init_per_suite(Config) ->
case emqx_common_test_helpers:is_tcp_server_available(?PGSQL_HOST, ?PGSQL_DEFAULT_PORT) of
true ->
ok = emqx_common_test_helpers:start_apps(
- [emqx_conf, emqx_authz],
- fun set_special_configs/1
- ),
+ [emqx_conf, emqx_authz],
+ fun set_special_configs/1
+ ),
ok = start_apps([emqx_resource, emqx_connector]),
{ok, _} = emqx_resource:create_local(
- ?PGSQL_RESOURCE,
- ?RESOURCE_GROUP,
- emqx_connector_pgsql,
- pgsql_config(),
- #{}),
+ ?PGSQL_RESOURCE,
+ ?RESOURCE_GROUP,
+ emqx_connector_pgsql,
+ pgsql_config(),
+ #{}
+ ),
Config;
false ->
{skip, no_pgsql}
@@ -64,7 +65,6 @@ init_per_testcase(_TestCase, Config) ->
set_special_configs(emqx_authz) ->
ok = emqx_authz_test_lib:reset_authorizers();
-
set_special_configs(_) ->
ok.
@@ -73,12 +73,13 @@ set_special_configs(_) ->
%%------------------------------------------------------------------------------
t_topic_rules(_Config) ->
- ClientInfo = #{clientid => <<"clientid">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
ok = emqx_authz_test_lib:test_no_topic_rules(ClientInfo, fun setup_client_samples/2),
@@ -86,128 +87,195 @@ t_topic_rules(_Config) ->
ok = emqx_authz_test_lib:test_deny_topic_rules(ClientInfo, fun setup_client_samples/2).
-
t_lookups(_Config) ->
- ClientInfo = #{clientid => <<"clientid">>,
- cn => <<"cn">>,
- dn => <<"dn">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ cn => <<"cn">>,
+ dn => <<"dn">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
%% by clientid
ok = init_table(),
- ok = insert(<<"INSERT INTO acl(clientid, topic, permission, action)"
- "VALUES($1, $2, $3, $4)">>,
- [<<"clientid">>, <<"a">>, <<"allow">>, <<"subscribe">>]),
+ ok = insert(
+ <<
+ "INSERT INTO acl(clientid, topic, permission, action)"
+ "VALUES($1, $2, $3, $4)"
+ >>,
+ [<<"clientid">>, <<"a">>, <<"allow">>, <<"subscribe">>]
+ ),
ok = setup_config(
- #{<<"query">> => <<"SELECT permission, action, topic "
- "FROM acl WHERE clientid = ${clientid}">>}),
+ #{
+ <<"query">> => <<
+ "SELECT permission, action, topic "
+ "FROM acl WHERE clientid = ${clientid}"
+ >>
+ }
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]),
+ ClientInfo,
+ [
+ {allow, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ),
%% by peerhost
ok = init_table(),
- ok = insert(<<"INSERT INTO acl(peerhost, topic, permission, action)"
- "VALUES($1, $2, $3, $4)">>,
- [<<"127.0.0.1">>, <<"a">>, <<"allow">>, <<"subscribe">>]),
+ ok = insert(
+ <<
+ "INSERT INTO acl(peerhost, topic, permission, action)"
+ "VALUES($1, $2, $3, $4)"
+ >>,
+ [<<"127.0.0.1">>, <<"a">>, <<"allow">>, <<"subscribe">>]
+ ),
ok = setup_config(
- #{<<"query">> => <<"SELECT permission, action, topic "
- "FROM acl WHERE peerhost = ${peerhost}">>}),
+ #{
+ <<"query">> => <<
+ "SELECT permission, action, topic "
+ "FROM acl WHERE peerhost = ${peerhost}"
+ >>
+ }
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]),
+ ClientInfo,
+ [
+ {allow, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ),
%% by cn
ok = init_table(),
- ok = insert(<<"INSERT INTO acl(cn, topic, permission, action)"
- "VALUES($1, $2, $3, $4)">>,
- [<<"cn">>, <<"a">>, <<"allow">>, <<"subscribe">>]),
+ ok = insert(
+ <<
+ "INSERT INTO acl(cn, topic, permission, action)"
+ "VALUES($1, $2, $3, $4)"
+ >>,
+ [<<"cn">>, <<"a">>, <<"allow">>, <<"subscribe">>]
+ ),
ok = setup_config(
- #{<<"query">> => <<"SELECT permission, action, topic "
- "FROM acl WHERE cn = ${cert_common_name}">>}),
+ #{
+ <<"query">> => <<
+ "SELECT permission, action, topic "
+ "FROM acl WHERE cn = ${cert_common_name}"
+ >>
+ }
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]),
+ ClientInfo,
+ [
+ {allow, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ),
%% by dn
ok = init_table(),
- ok = insert(<<"INSERT INTO acl(dn, topic, permission, action)"
- "VALUES($1, $2, $3, $4)">>,
- [<<"dn">>, <<"a">>, <<"allow">>, <<"subscribe">>]),
+ ok = insert(
+ <<
+ "INSERT INTO acl(dn, topic, permission, action)"
+ "VALUES($1, $2, $3, $4)"
+ >>,
+ [<<"dn">>, <<"a">>, <<"allow">>, <<"subscribe">>]
+ ),
ok = setup_config(
- #{<<"query">> => <<"SELECT permission, action, topic "
- "FROM acl WHERE dn = ${cert_subject}">>}),
+ #{
+ <<"query">> => <<
+ "SELECT permission, action, topic "
+ "FROM acl WHERE dn = ${cert_subject}"
+ >>
+ }
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]).
+ ClientInfo,
+ [
+ {allow, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ).
t_pgsql_error(_Config) ->
- ClientInfo = #{clientid => <<"clientid">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
ok = setup_config(
- #{<<"query">> => <<"SELECT permission, action, topic "
- "FROM acl WHERE clientid = ${username}">>}),
+ #{
+ <<"query">> => <<
+ "SELECT permission, action, topic "
+ "FROM acl WHERE clientid = ${username}"
+ >>
+ }
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{deny, subscribe, <<"a">>}]).
-
+ ClientInfo,
+ [{deny, subscribe, <<"a">>}]
+ ).
t_create_invalid(_Config) ->
BadConfig = maps:merge(
- raw_pgsql_authz_config(),
- #{<<"server">> => <<"255.255.255.255:33333">>}),
+ raw_pgsql_authz_config(),
+ #{<<"server">> => <<"255.255.255.255:33333">>}
+ ),
{ok, _} = emqx_authz:update(?CMD_REPLACE, [BadConfig]),
[_] = emqx_authz:lookup().
t_nonbinary_values(_Config) ->
- ClientInfo = #{clientid => clientid,
- username => "username",
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
-
+ ClientInfo = #{
+ clientid => clientid,
+ username => "username",
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
ok = init_table(),
- ok = insert(<<"INSERT INTO acl(clientid, username, topic, permission, action)"
- "VALUES($1, $2, $3, $4, $5)">>,
- [<<"clientid">>, <<"username">>, <<"a">>, <<"allow">>, <<"subscribe">>]),
+ ok = insert(
+ <<
+ "INSERT INTO acl(clientid, username, topic, permission, action)"
+ "VALUES($1, $2, $3, $4, $5)"
+ >>,
+ [<<"clientid">>, <<"username">>, <<"a">>, <<"allow">>, <<"subscribe">>]
+ ),
ok = setup_config(
- #{<<"query">> => <<"SELECT permission, action, topic "
- "FROM acl WHERE clientid = ${clientid} AND username = ${username}">>}),
+ #{
+ <<"query">> => <<
+ "SELECT permission, action, topic "
+ "FROM acl WHERE clientid = ${clientid} AND username = ${username}"
+ >>
+ }
+ ),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]).
+ ClientInfo,
+ [
+ {allow, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ).
%%------------------------------------------------------------------------------
%% Helpers
@@ -222,34 +290,40 @@ raw_pgsql_authz_config() ->
<<"username">> => <<"root">>,
<<"password">> => <<"public">>,
- <<"query">> => <<"SELECT permission, action, topic "
- "FROM acl WHERE username = ${username}">>,
+ <<"query">> => <<
+ "SELECT permission, action, topic "
+ "FROM acl WHERE username = ${username}"
+ >>,
<<"server">> => pgsql_server()
}.
q(Sql) ->
emqx_resource:query(
- ?PGSQL_RESOURCE,
- {query, Sql}).
+ ?PGSQL_RESOURCE,
+ {query, Sql}
+ ).
insert(Sql, Params) ->
{ok, _} = emqx_resource:query(
- ?PGSQL_RESOURCE,
- {query, Sql, Params}),
+ ?PGSQL_RESOURCE,
+ {query, Sql, Params}
+ ),
ok.
init_table() ->
ok = drop_table(),
- {ok, _, _} = q("CREATE TABLE acl(
- username VARCHAR(255),
- clientid VARCHAR(255),
- peerhost VARCHAR(255),
- cn VARCHAR(255),
- dn VARCHAR(255),
- topic VARCHAR(255),
- permission VARCHAR(255),
- action VARCHAR(255))"),
+ {ok, _, _} = q(
+ "CREATE TABLE acl(\n"
+ " username VARCHAR(255),\n"
+ " clientid VARCHAR(255),\n"
+ " peerhost VARCHAR(255),\n"
+ " cn VARCHAR(255),\n"
+ " dn VARCHAR(255),\n"
+ " topic VARCHAR(255),\n"
+ " permission VARCHAR(255),\n"
+ " action VARCHAR(255))"
+ ),
ok.
drop_table() ->
@@ -260,37 +334,50 @@ setup_client_samples(ClientInfo, Samples) ->
#{username := Username} = ClientInfo,
ok = init_table(),
ok = lists:foreach(
- fun(#{topics := Topics, permission := Permission, action := Action}) ->
- lists:foreach(
- fun(Topic) ->
- insert(<<"INSERT INTO acl(username, topic, permission, action)"
- "VALUES($1, $2, $3, $4)">>,
- [Username, Topic, Permission, Action])
- end,
- Topics)
- end,
- Samples),
+ fun(#{topics := Topics, permission := Permission, action := Action}) ->
+ lists:foreach(
+ fun(Topic) ->
+ insert(
+ <<
+ "INSERT INTO acl(username, topic, permission, action)"
+ "VALUES($1, $2, $3, $4)"
+ >>,
+ [Username, Topic, Permission, Action]
+ )
+ end,
+ Topics
+ )
+ end,
+ Samples
+ ),
setup_config(
- #{<<"query">> => <<"SELECT permission, action, topic "
- "FROM acl WHERE username = ${username}">>}).
+ #{
+ <<"query">> => <<
+ "SELECT permission, action, topic "
+ "FROM acl WHERE username = ${username}"
+ >>
+ }
+ ).
setup_config(SpecialParams) ->
emqx_authz_test_lib:setup_config(
- raw_pgsql_authz_config(),
- SpecialParams).
+ raw_pgsql_authz_config(),
+ SpecialParams
+ ).
pgsql_server() ->
- iolist_to_binary(io_lib:format("~s",[?PGSQL_HOST])).
+ iolist_to_binary(io_lib:format("~s", [?PGSQL_HOST])).
pgsql_config() ->
- #{auto_reconnect => true,
- database => <<"mqtt">>,
- username => <<"root">>,
- password => <<"public">>,
- pool_size => 8,
- server => {?PGSQL_HOST, ?PGSQL_DEFAULT_PORT},
- ssl => #{enable => false}
- }.
+ #{
+ auto_reconnect => true,
+ database => <<"mqtt">>,
+ username => <<"root">>,
+ password => <<"public">>,
+ pool_size => 8,
+ server => {?PGSQL_HOST, ?PGSQL_DEFAULT_PORT},
+ ssl => #{enable => false}
+ }.
start_apps(Apps) ->
lists:foreach(fun application:ensure_all_started/1, Apps).
diff --git a/apps/emqx_authz/test/emqx_authz_redis_SUITE.erl b/apps/emqx_authz/test/emqx_authz_redis_SUITE.erl
index c0cdef2c9..286dbd286 100644
--- a/apps/emqx_authz/test/emqx_authz_redis_SUITE.erl
+++ b/apps/emqx_authz/test/emqx_authz_redis_SUITE.erl
@@ -38,16 +38,17 @@ init_per_suite(Config) ->
case emqx_common_test_helpers:is_tcp_server_available(?REDIS_HOST, ?REDIS_DEFAULT_PORT) of
true ->
ok = emqx_common_test_helpers:start_apps(
- [emqx_conf, emqx_authz],
- fun set_special_configs/1
- ),
+ [emqx_conf, emqx_authz],
+ fun set_special_configs/1
+ ),
ok = start_apps([emqx_resource, emqx_connector]),
{ok, _} = emqx_resource:create_local(
- ?REDIS_RESOURCE,
- ?RESOURCE_GROUP,
- emqx_connector_redis,
- redis_config(),
- #{}),
+ ?REDIS_RESOURCE,
+ ?RESOURCE_GROUP,
+ emqx_connector_redis,
+ redis_config(),
+ #{}
+ ),
Config;
false ->
{skip, no_redis}
@@ -65,109 +66,130 @@ init_per_testcase(_TestCase, Config) ->
set_special_configs(emqx_authz) ->
ok = emqx_authz_test_lib:reset_authorizers();
-
set_special_configs(_) ->
ok.
-
%%------------------------------------------------------------------------------
%% Tests
%%------------------------------------------------------------------------------
t_topic_rules(_Config) ->
- ClientInfo = #{clientid => <<"clientid">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
ok = emqx_authz_test_lib:test_no_topic_rules(ClientInfo, fun setup_client_samples/2),
ok = emqx_authz_test_lib:test_allow_topic_rules(ClientInfo, fun setup_client_samples/2).
-
t_lookups(_Config) ->
- ClientInfo = #{clientid => <<"client id">>,
- cn => <<"cn">>,
- dn => <<"dn">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"client id">>,
+ cn => <<"cn">>,
+ dn => <<"dn">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
- ByClientid = #{<<"mqtt_user:client id">> =>
- #{<<"a">> => <<"all">>}},
+ ByClientid = #{
+ <<"mqtt_user:client id">> =>
+ #{<<"a">> => <<"all">>}
+ },
ok = setup_sample(ByClientid),
ok = setup_config(#{<<"cmd">> => <<"HGETALL mqtt_user:${clientid}">>}),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]),
+ ClientInfo,
+ [
+ {allow, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ),
- ByPeerhost = #{<<"mqtt_user:127.0.0.1">> =>
- #{<<"a">> => <<"all">>}},
+ ByPeerhost = #{
+ <<"mqtt_user:127.0.0.1">> =>
+ #{<<"a">> => <<"all">>}
+ },
ok = setup_sample(ByPeerhost),
ok = setup_config(#{<<"cmd">> => <<"HGETALL mqtt_user:${peerhost}">>}),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]),
+ ClientInfo,
+ [
+ {allow, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ),
- ByCN = #{<<"mqtt_user:cn">> =>
- #{<<"a">> => <<"all">>}},
+ ByCN = #{
+ <<"mqtt_user:cn">> =>
+ #{<<"a">> => <<"all">>}
+ },
ok = setup_sample(ByCN),
ok = setup_config(#{<<"cmd">> => <<"HGETALL mqtt_user:${cert_common_name}">>}),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]),
+ ClientInfo,
+ [
+ {allow, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ),
-
- ByDN = #{<<"mqtt_user:dn">> =>
- #{<<"a">> => <<"all">>}},
+ ByDN = #{
+ <<"mqtt_user:dn">> =>
+ #{<<"a">> => <<"all">>}
+ },
ok = setup_sample(ByDN),
ok = setup_config(#{<<"cmd">> => <<"HGETALL mqtt_user:${cert_subject}">>}),
ok = emqx_authz_test_lib:test_samples(
- ClientInfo,
- [{allow, subscribe, <<"a">>},
- {deny, subscribe, <<"b">>}]).
+ ClientInfo,
+ [
+ {allow, subscribe, <<"a">>},
+ {deny, subscribe, <<"b">>}
+ ]
+ ).
t_create_invalid(_Config) ->
AuthzConfig = raw_redis_authz_config(),
InvalidConfigs =
- [maps:without([<<"server">>], AuthzConfig),
- AuthzConfig#{<<"server">> => <<"unknownhost:3333">>},
- AuthzConfig#{<<"password">> => <<"wrongpass">>},
- AuthzConfig#{<<"database">> => <<"5678">>}],
+ [
+ maps:without([<<"server">>], AuthzConfig),
+ AuthzConfig#{<<"server">> => <<"unknownhost:3333">>},
+ AuthzConfig#{<<"password">> => <<"wrongpass">>},
+ AuthzConfig#{<<"database">> => <<"5678">>}
+ ],
lists:foreach(
- fun(Config) ->
+ fun(Config) ->
{ok, _} = emqx_authz:update(?CMD_REPLACE, [Config]),
[_] = emqx_authz:lookup()
-
- end,
- InvalidConfigs).
+ end,
+ InvalidConfigs
+ ).
t_redis_error(_Config) ->
ok = setup_config(#{<<"cmd">> => <<"INVALID COMMAND">>}),
- ClientInfo = #{clientid => <<"clientid">>,
- username => <<"username">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo = #{
+ clientid => <<"clientid">>,
+ username => <<"username">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
deny = emqx_access_control:authorize(ClientInfo, subscribe, <<"a">>).
@@ -178,30 +200,36 @@ t_redis_error(_Config) ->
setup_sample(AuthzData) ->
{ok, _} = q(["FLUSHDB"]),
ok = lists:foreach(
- fun({Key, Values}) ->
- lists:foreach(
- fun({TopicFilter, Action}) ->
- q(["HSET", Key, TopicFilter, Action])
- end,
- maps:to_list(Values))
- end,
- maps:to_list(AuthzData)).
+ fun({Key, Values}) ->
+ lists:foreach(
+ fun({TopicFilter, Action}) ->
+ q(["HSET", Key, TopicFilter, Action])
+ end,
+ maps:to_list(Values)
+ )
+ end,
+ maps:to_list(AuthzData)
+ ).
setup_client_samples(ClientInfo, Samples) ->
#{username := Username} = ClientInfo,
Key = <<"mqtt_user:", Username/binary>>,
lists:foreach(
- fun(Sample) ->
- #{topics := Topics,
+ fun(Sample) ->
+ #{
+ topics := Topics,
permission := <<"allow">>,
- action := Action} = Sample,
- lists:foreach(
+ action := Action
+ } = Sample,
+ lists:foreach(
fun(Topic) ->
- q(["HSET", Key, Topic, Action])
+ q(["HSET", Key, Topic, Action])
end,
- Topics)
- end,
- Samples),
+ Topics
+ )
+ end,
+ Samples
+ ),
setup_config(#{}).
setup_config(SpecialParams) ->
@@ -221,22 +249,24 @@ raw_redis_authz_config() ->
}.
redis_server() ->
- iolist_to_binary(io_lib:format("~s",[?REDIS_HOST])).
+ iolist_to_binary(io_lib:format("~s", [?REDIS_HOST])).
q(Command) ->
emqx_resource:query(
- ?REDIS_RESOURCE,
- {cmd, Command}).
+ ?REDIS_RESOURCE,
+ {cmd, Command}
+ ).
redis_config() ->
- #{auto_reconnect => true,
- database => 1,
- pool_size => 8,
- redis_type => single,
- password => "public",
- server => {?REDIS_HOST, ?REDIS_DEFAULT_PORT},
- ssl => #{enable => false}
- }.
+ #{
+ auto_reconnect => true,
+ database => 1,
+ pool_size => 8,
+ redis_type => single,
+ password => "public",
+ server => {?REDIS_HOST, ?REDIS_DEFAULT_PORT},
+ ssl => #{enable => false}
+ }.
start_apps(Apps) ->
lists:foreach(fun application:ensure_all_started/1, Apps).
diff --git a/apps/emqx_authz/test/emqx_authz_rule_SUITE.erl b/apps/emqx_authz/test/emqx_authz_rule_SUITE.erl
index 62299621a..eb16c4e50 100644
--- a/apps/emqx_authz/test/emqx_authz_rule_SUITE.erl
+++ b/apps/emqx_authz/test/emqx_authz_rule_SUITE.erl
@@ -23,30 +23,38 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("emqx/include/emqx_placeholder.hrl").
--define(SOURCE1, {deny, all}).
--define(SOURCE2, {allow, {ipaddr, "127.0.0.1"}, all, [{eq, "#"}, {eq, "+"}]}).
+-define(SOURCE1, {deny, all}).
+-define(SOURCE2, {allow, {ipaddr, "127.0.0.1"}, all, [{eq, "#"}, {eq, "+"}]}).
-define(SOURCE3, {allow, {ipaddrs, ["127.0.0.1", "192.168.1.0/24"]}, subscribe, [?PH_S_CLIENTID]}).
-define(SOURCE4, {allow, {'and', [{client, "test"}, {user, "test"}]}, publish, ["topic/test"]}).
--define(SOURCE5, {allow, {'or',
- [{username, {re, "^test"}},
- {clientid, {re, "test?"}}]},
- publish, [?PH_S_USERNAME, ?PH_S_CLIENTID]}).
+-define(SOURCE5,
+ {allow,
+ {'or', [
+ {username, {re, "^test"}},
+ {clientid, {re, "test?"}}
+ ]},
+ publish, [?PH_S_USERNAME, ?PH_S_CLIENTID]}
+).
all() ->
emqx_common_test_helpers:all(?MODULE).
init_per_suite(Config) ->
ok = emqx_common_test_helpers:start_apps(
- [emqx_conf, emqx_authz],
- fun set_special_configs/1),
+ [emqx_conf, emqx_authz],
+ fun set_special_configs/1
+ ),
Config.
end_per_suite(_Config) ->
{ok, _} = emqx:update_config(
- [authorization],
- #{<<"no_match">> => <<"allow">>,
- <<"cache">> => #{<<"enable">> => <<"true">>},
- <<"sources">> => []}),
+ [authorization],
+ #{
+ <<"no_match">> => <<"allow">>,
+ <<"cache">> => #{<<"enable">> => <<"true">>},
+ <<"sources">> => []
+ }
+ ),
emqx_common_test_helpers:stop_apps([emqx_authz, emqx_conf]),
ok.
@@ -61,115 +69,242 @@ set_special_configs(_App) ->
t_compile(_) ->
?assertEqual({deny, all, all, [['#']]}, emqx_authz_rule:compile(?SOURCE1)),
- ?assertEqual({allow, {ipaddr, {{127,0,0,1}, {127,0,0,1}, 32}},
- all, [{eq, ['#']}, {eq, ['+']}]}, emqx_authz_rule:compile(?SOURCE2)),
+ ?assertEqual(
+ {allow, {ipaddr, {{127, 0, 0, 1}, {127, 0, 0, 1}, 32}}, all, [{eq, ['#']}, {eq, ['+']}]},
+ emqx_authz_rule:compile(?SOURCE2)
+ ),
- ?assertEqual({allow,
- {ipaddrs,[{{127,0,0,1},{127,0,0,1},32},
- {{192,168,1,0},{192,168,1,255},24}]},
- subscribe,
- [{pattern,[?PH_CLIENTID]}]
- }, emqx_authz_rule:compile(?SOURCE3)),
+ ?assertEqual(
+ {allow,
+ {ipaddrs, [
+ {{127, 0, 0, 1}, {127, 0, 0, 1}, 32},
+ {{192, 168, 1, 0}, {192, 168, 1, 255}, 24}
+ ]},
+ subscribe, [{pattern, [?PH_CLIENTID]}]},
+ emqx_authz_rule:compile(?SOURCE3)
+ ),
- ?assertMatch({allow,
- {'and', [{clientid, {eq, <<"test">>}}, {username, {eq, <<"test">>}}]},
- publish,
- [[<<"topic">>, <<"test">>]]
- }, emqx_authz_rule:compile(?SOURCE4)),
+ ?assertMatch(
+ {allow, {'and', [{clientid, {eq, <<"test">>}}, {username, {eq, <<"test">>}}]}, publish, [
+ [<<"topic">>, <<"test">>]
+ ]},
+ emqx_authz_rule:compile(?SOURCE4)
+ ),
- ?assertMatch({allow,
- {'or', [{username, {re_pattern, _, _, _, _}},
- {clientid, {re_pattern, _, _, _, _}}]},
- publish, [{pattern, [?PH_USERNAME]}, {pattern, [?PH_CLIENTID]}]
- }, emqx_authz_rule:compile(?SOURCE5)),
+ ?assertMatch(
+ {allow,
+ {'or', [
+ {username, {re_pattern, _, _, _, _}},
+ {clientid, {re_pattern, _, _, _, _}}
+ ]},
+ publish, [{pattern, [?PH_USERNAME]}, {pattern, [?PH_CLIENTID]}]},
+ emqx_authz_rule:compile(?SOURCE5)
+ ),
ok.
-
t_match(_) ->
- ClientInfo1 = #{clientid => <<"test">>,
- username => <<"test">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
- ClientInfo2 = #{clientid => <<"test">>,
- username => <<"test">>,
- peerhost => {192,168,1,10},
- zone => default,
- listener => {tcp, default}
- },
- ClientInfo3 = #{clientid => <<"test">>,
- username => <<"fake">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
- ClientInfo4 = #{clientid => <<"fake">>,
- username => <<"test">>,
- peerhost => {127,0,0,1},
- zone => default,
- listener => {tcp, default}
- },
+ ClientInfo1 = #{
+ clientid => <<"test">>,
+ username => <<"test">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
+ ClientInfo2 = #{
+ clientid => <<"test">>,
+ username => <<"test">>,
+ peerhost => {192, 168, 1, 10},
+ zone => default,
+ listener => {tcp, default}
+ },
+ ClientInfo3 = #{
+ clientid => <<"test">>,
+ username => <<"fake">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
+ ClientInfo4 = #{
+ clientid => <<"fake">>,
+ username => <<"test">>,
+ peerhost => {127, 0, 0, 1},
+ zone => default,
+ listener => {tcp, default}
+ },
- ?assertEqual({matched, deny},
- emqx_authz_rule:match(ClientInfo1, subscribe, <<"#">>,
- emqx_authz_rule:compile(?SOURCE1))),
- ?assertEqual({matched, deny},
- emqx_authz_rule:match(ClientInfo2, subscribe, <<"+">>,
- emqx_authz_rule:compile(?SOURCE1))),
- ?assertEqual({matched, deny},
- emqx_authz_rule:match(ClientInfo3, subscribe, <<"topic/test">>,
- emqx_authz_rule:compile(?SOURCE1))),
+ ?assertEqual(
+ {matched, deny},
+ emqx_authz_rule:match(
+ ClientInfo1,
+ subscribe,
+ <<"#">>,
+ emqx_authz_rule:compile(?SOURCE1)
+ )
+ ),
+ ?assertEqual(
+ {matched, deny},
+ emqx_authz_rule:match(
+ ClientInfo2,
+ subscribe,
+ <<"+">>,
+ emqx_authz_rule:compile(?SOURCE1)
+ )
+ ),
+ ?assertEqual(
+ {matched, deny},
+ emqx_authz_rule:match(
+ ClientInfo3,
+ subscribe,
+ <<"topic/test">>,
+ emqx_authz_rule:compile(?SOURCE1)
+ )
+ ),
- ?assertEqual({matched, allow},
- emqx_authz_rule:match(ClientInfo1, subscribe, <<"#">>,
- emqx_authz_rule:compile(?SOURCE2))),
- ?assertEqual(nomatch,
- emqx_authz_rule:match(ClientInfo1, subscribe, <<"topic/test">>,
- emqx_authz_rule:compile(?SOURCE2))),
- ?assertEqual(nomatch,
- emqx_authz_rule:match(ClientInfo2, subscribe, <<"#">>,
- emqx_authz_rule:compile(?SOURCE2))),
+ ?assertEqual(
+ {matched, allow},
+ emqx_authz_rule:match(
+ ClientInfo1,
+ subscribe,
+ <<"#">>,
+ emqx_authz_rule:compile(?SOURCE2)
+ )
+ ),
+ ?assertEqual(
+ nomatch,
+ emqx_authz_rule:match(
+ ClientInfo1,
+ subscribe,
+ <<"topic/test">>,
+ emqx_authz_rule:compile(?SOURCE2)
+ )
+ ),
+ ?assertEqual(
+ nomatch,
+ emqx_authz_rule:match(
+ ClientInfo2,
+ subscribe,
+ <<"#">>,
+ emqx_authz_rule:compile(?SOURCE2)
+ )
+ ),
- ?assertEqual({matched, allow},
- emqx_authz_rule:match(ClientInfo1, subscribe, <<"test">>,
- emqx_authz_rule:compile(?SOURCE3))),
- ?assertEqual({matched, allow},
- emqx_authz_rule:match(ClientInfo2, subscribe, <<"test">>,
- emqx_authz_rule:compile(?SOURCE3))),
- ?assertEqual(nomatch,
- emqx_authz_rule:match(ClientInfo2, subscribe, <<"topic/test">>,
- emqx_authz_rule:compile(?SOURCE3))),
+ ?assertEqual(
+ {matched, allow},
+ emqx_authz_rule:match(
+ ClientInfo1,
+ subscribe,
+ <<"test">>,
+ emqx_authz_rule:compile(?SOURCE3)
+ )
+ ),
+ ?assertEqual(
+ {matched, allow},
+ emqx_authz_rule:match(
+ ClientInfo2,
+ subscribe,
+ <<"test">>,
+ emqx_authz_rule:compile(?SOURCE3)
+ )
+ ),
+ ?assertEqual(
+ nomatch,
+ emqx_authz_rule:match(
+ ClientInfo2,
+ subscribe,
+ <<"topic/test">>,
+ emqx_authz_rule:compile(?SOURCE3)
+ )
+ ),
- ?assertEqual({matched, allow},
- emqx_authz_rule:match(ClientInfo1, publish, <<"topic/test">>,
- emqx_authz_rule:compile(?SOURCE4))),
- ?assertEqual({matched, allow},
- emqx_authz_rule:match(ClientInfo2, publish, <<"topic/test">>,
- emqx_authz_rule:compile(?SOURCE4))),
- ?assertEqual(nomatch,
- emqx_authz_rule:match(ClientInfo3, publish, <<"topic/test">>,
- emqx_authz_rule:compile(?SOURCE4))),
- ?assertEqual(nomatch,
- emqx_authz_rule:match(ClientInfo4, publish, <<"topic/test">>,
- emqx_authz_rule:compile(?SOURCE4))),
+ ?assertEqual(
+ {matched, allow},
+ emqx_authz_rule:match(
+ ClientInfo1,
+ publish,
+ <<"topic/test">>,
+ emqx_authz_rule:compile(?SOURCE4)
+ )
+ ),
+ ?assertEqual(
+ {matched, allow},
+ emqx_authz_rule:match(
+ ClientInfo2,
+ publish,
+ <<"topic/test">>,
+ emqx_authz_rule:compile(?SOURCE4)
+ )
+ ),
+ ?assertEqual(
+ nomatch,
+ emqx_authz_rule:match(
+ ClientInfo3,
+ publish,
+ <<"topic/test">>,
+ emqx_authz_rule:compile(?SOURCE4)
+ )
+ ),
+ ?assertEqual(
+ nomatch,
+ emqx_authz_rule:match(
+ ClientInfo4,
+ publish,
+ <<"topic/test">>,
+ emqx_authz_rule:compile(?SOURCE4)
+ )
+ ),
- ?assertEqual({matched, allow},
- emqx_authz_rule:match(ClientInfo1, publish, <<"test">>,
- emqx_authz_rule:compile(?SOURCE5))),
- ?assertEqual({matched, allow},
- emqx_authz_rule:match(ClientInfo2, publish, <<"test">>,
- emqx_authz_rule:compile(?SOURCE5))),
- ?assertEqual({matched, allow},
- emqx_authz_rule:match(ClientInfo3, publish, <<"test">>,
- emqx_authz_rule:compile(?SOURCE5))),
- ?assertEqual({matched, allow},
- emqx_authz_rule:match(ClientInfo3, publish, <<"fake">>,
- emqx_authz_rule:compile(?SOURCE5))),
- ?assertEqual({matched, allow},
- emqx_authz_rule:match(ClientInfo4, publish, <<"test">>,
- emqx_authz_rule:compile(?SOURCE5))),
- ?assertEqual({matched, allow},
- emqx_authz_rule:match(ClientInfo4, publish, <<"fake">>,
- emqx_authz_rule:compile(?SOURCE5))),
+ ?assertEqual(
+ {matched, allow},
+ emqx_authz_rule:match(
+ ClientInfo1,
+ publish,
+ <<"test">>,
+ emqx_authz_rule:compile(?SOURCE5)
+ )
+ ),
+ ?assertEqual(
+ {matched, allow},
+ emqx_authz_rule:match(
+ ClientInfo2,
+ publish,
+ <<"test">>,
+ emqx_authz_rule:compile(?SOURCE5)
+ )
+ ),
+ ?assertEqual(
+ {matched, allow},
+ emqx_authz_rule:match(
+ ClientInfo3,
+ publish,
+ <<"test">>,
+ emqx_authz_rule:compile(?SOURCE5)
+ )
+ ),
+ ?assertEqual(
+ {matched, allow},
+ emqx_authz_rule:match(
+ ClientInfo3,
+ publish,
+ <<"fake">>,
+ emqx_authz_rule:compile(?SOURCE5)
+ )
+ ),
+ ?assertEqual(
+ {matched, allow},
+ emqx_authz_rule:match(
+ ClientInfo4,
+ publish,
+ <<"test">>,
+ emqx_authz_rule:compile(?SOURCE5)
+ )
+ ),
+ ?assertEqual(
+ {matched, allow},
+ emqx_authz_rule:match(
+ ClientInfo4,
+ publish,
+ <<"fake">>,
+ emqx_authz_rule:compile(?SOURCE5)
+ )
+ ),
ok.
diff --git a/apps/emqx_authz/test/emqx_authz_test_lib.erl b/apps/emqx_authz/test/emqx_authz_test_lib.erl
index 6225d3a83..a2cc39b13 100644
--- a/apps/emqx_authz/test/emqx_authz_test_lib.erl
+++ b/apps/emqx_authz/test/emqx_authz_test_lib.erl
@@ -32,33 +32,40 @@ restore_authorizers() ->
reset_authorizers(Nomatch, ChacheEnabled) ->
{ok, _} = emqx:update_config(
- [authorization],
- #{<<"no_match">> => atom_to_binary(Nomatch),
- <<"cache">> => #{<<"enable">> => atom_to_binary(ChacheEnabled)},
- <<"sources">> => []}),
+ [authorization],
+ #{
+ <<"no_match">> => atom_to_binary(Nomatch),
+ <<"cache">> => #{<<"enable">> => atom_to_binary(ChacheEnabled)},
+ <<"sources">> => []
+ }
+ ),
ok.
setup_config(BaseConfig, SpecialParams) ->
Config = maps:merge(BaseConfig, SpecialParams),
case emqx_authz:update(?CMD_REPLACE, [Config]) of
- {ok, _} -> ok;
- {error, Reason} -> {error, Reason}
+ {ok, _} -> ok;
+ {error, Reason} -> {error, Reason}
end.
test_samples(ClientInfo, Samples) ->
lists:foreach(
- fun({Expected, Action, Topic}) ->
- ct:pal(
+ fun({Expected, Action, Topic}) ->
+ ct:pal(
"client_info: ~p, action: ~p, topic: ~p, expected: ~p",
- [ClientInfo, Action, Topic, Expected]),
- ?assertEqual(
- Expected,
- emqx_access_control:authorize(
- ClientInfo,
- Action,
- Topic))
- end,
- Samples).
+ [ClientInfo, Action, Topic, Expected]
+ ),
+ ?assertEqual(
+ Expected,
+ emqx_access_control:authorize(
+ ClientInfo,
+ Action,
+ Topic
+ )
+ )
+ end,
+ Samples
+ ).
test_no_topic_rules(ClientInfo, SetupSamples) ->
%% No rules
@@ -67,43 +74,52 @@ test_no_topic_rules(ClientInfo, SetupSamples) ->
ok = SetupSamples(ClientInfo, []),
ok = test_samples(
- ClientInfo,
- [{deny, subscribe, <<"#">>},
+ ClientInfo,
+ [
+ {deny, subscribe, <<"#">>},
{deny, subscribe, <<"subs">>},
- {deny, publish, <<"pub">>}]).
+ {deny, publish, <<"pub">>}
+ ]
+ ).
test_allow_topic_rules(ClientInfo, SetupSamples) ->
- Samples = [#{
- topics => [<<"eq testpub1/${username}">>,
- <<"testpub2/${clientid}">>,
- <<"testpub3/#">>],
- permission => <<"allow">>,
- action => <<"publish">>
- },
- #{
- topics => [<<"eq testsub1/${username}">>,
- <<"testsub2/${clientid}">>,
- <<"testsub3/#">>],
- permission => <<"allow">>,
- action => <<"subscribe">>
- },
+ Samples = [
+ #{
+ topics => [
+ <<"eq testpub1/${username}">>,
+ <<"testpub2/${clientid}">>,
+ <<"testpub3/#">>
+ ],
+ permission => <<"allow">>,
+ action => <<"publish">>
+ },
+ #{
+ topics => [
+ <<"eq testsub1/${username}">>,
+ <<"testsub2/${clientid}">>,
+ <<"testsub3/#">>
+ ],
+ permission => <<"allow">>,
+ action => <<"subscribe">>
+ },
- #{
- topics => [<<"eq testall1/${username}">>,
- <<"testall2/${clientid}">>,
- <<"testall3/#">>],
- permission => <<"allow">>,
- action => <<"all">>
- }
- ],
+ #{
+ topics => [
+ <<"eq testall1/${username}">>,
+ <<"testall2/${clientid}">>,
+ <<"testall3/#">>
+ ],
+ permission => <<"allow">>,
+ action => <<"all">>
+ }
+ ],
ok = reset_authorizers(deny, false),
ok = SetupSamples(ClientInfo, Samples),
ok = test_samples(
- ClientInfo,
- [
-
+ ClientInfo,
+ [
%% Publish rules
{deny, publish, <<"testpub1/username">>},
@@ -114,7 +130,6 @@ test_allow_topic_rules(ClientInfo, SetupSamples) ->
{deny, publish, <<"testpub2/username">>},
{deny, publish, <<"testpub1/clientid">>},
-
{deny, subscribe, <<"testpub1/username">>},
{deny, subscribe, <<"testpub2/clientid">>},
{deny, subscribe, <<"testpub3/foobar">>},
@@ -154,41 +169,47 @@ test_allow_topic_rules(ClientInfo, SetupSamples) ->
{deny, publish, <<"testall2/username">>},
{deny, publish, <<"testall1/clientid">>},
{deny, publish, <<"testall4/foobar">>}
- ]).
+ ]
+ ).
test_deny_topic_rules(ClientInfo, SetupSamples) ->
Samples = [
- #{
- topics => [<<"eq testpub1/${username}">>,
- <<"testpub2/${clientid}">>,
- <<"testpub3/#">>],
- permission => <<"deny">>,
- action => <<"publish">>
- },
- #{
- topics => [<<"eq testsub1/${username}">>,
- <<"testsub2/${clientid}">>,
- <<"testsub3/#">>],
- permission => <<"deny">>,
- action => <<"subscribe">>
- },
+ #{
+ topics => [
+ <<"eq testpub1/${username}">>,
+ <<"testpub2/${clientid}">>,
+ <<"testpub3/#">>
+ ],
+ permission => <<"deny">>,
+ action => <<"publish">>
+ },
+ #{
+ topics => [
+ <<"eq testsub1/${username}">>,
+ <<"testsub2/${clientid}">>,
+ <<"testsub3/#">>
+ ],
+ permission => <<"deny">>,
+ action => <<"subscribe">>
+ },
- #{
- topics => [<<"eq testall1/${username}">>,
- <<"testall2/${clientid}">>,
- <<"testall3/#">>],
- permission => <<"deny">>,
- action => <<"all">>
- }
- ],
+ #{
+ topics => [
+ <<"eq testall1/${username}">>,
+ <<"testall2/${clientid}">>,
+ <<"testall3/#">>
+ ],
+ permission => <<"deny">>,
+ action => <<"all">>
+ }
+ ],
ok = reset_authorizers(allow, false),
ok = SetupSamples(ClientInfo, Samples),
ok = test_samples(
- ClientInfo,
- [
-
+ ClientInfo,
+ [
%% Publish rules
{allow, publish, <<"testpub1/username">>},
@@ -199,7 +220,6 @@ test_deny_topic_rules(ClientInfo, SetupSamples) ->
{allow, publish, <<"testpub2/username">>},
{allow, publish, <<"testpub1/clientid">>},
-
{allow, subscribe, <<"testpub1/username">>},
{allow, subscribe, <<"testpub2/clientid">>},
{allow, subscribe, <<"testpub3/foobar">>},
@@ -239,4 +259,5 @@ test_deny_topic_rules(ClientInfo, SetupSamples) ->
{allow, publish, <<"testall2/username">>},
{allow, publish, <<"testall1/clientid">>},
{allow, publish, <<"testall4/foobar">>}
- ]).
+ ]
+ ).