From 4bb1e9c9646af0bf3182d615365fb2428bd38ad8 Mon Sep 17 00:00:00 2001 From: zhanghongtong Date: Fri, 6 Aug 2021 18:04:03 +0800 Subject: [PATCH] feat(authz): more better update function Signed-off-by: zhanghongtong --- apps/emqx_authz/src/emqx_authz.erl | 87 ++++++-- apps/emqx_authz/src/emqx_authz_api.erl | 117 ++++++++++- apps/emqx_authz/test/emqx_authz_SUITE.erl | 142 ++++++++------ apps/emqx_authz/test/emqx_authz_api_SUITE.erl | 185 ++++++++++-------- .../test/emqx_authz_mongo_SUITE.erl | 1 + .../test/emqx_authz_mysql_SUITE.erl | 1 + .../test/emqx_authz_pgsql_SUITE.erl | 1 + .../test/emqx_authz_redis_SUITE.erl | 1 + 8 files changed, 369 insertions(+), 166 deletions(-) diff --git a/apps/emqx_authz/src/emqx_authz.erl b/apps/emqx_authz/src/emqx_authz.erl index 2599fd161..da4ef9347 100644 --- a/apps/emqx_authz/src/emqx_authz.erl +++ b/apps/emqx_authz/src/emqx_authz.erl @@ -20,11 +20,16 @@ -include("emqx_authz.hrl"). -include_lib("emqx/include/logger.hrl"). +-ifdef(TEST). +-compile(export_all). +-compile(nowarn_export_all). +-endif. -export([ register_metrics/0 , init/0 , init_rule/1 , lookup/0 + , lookup/1 , update/2 , authorize/5 , match/4 @@ -45,8 +50,13 @@ init() -> ok = emqx_hooks:add('client.authorize', {?MODULE, authorize, [NRules]}, -1). lookup() -> - {_M, _F, A}= find_action_in_hooks(), + {_M, _F, [A]}= find_action_in_hooks(), A. +lookup(Id) -> + case find_rule_by_id(Id, lookup()) of + {error, Reason} -> {error, Reason}; + {_, Rule} -> Rule + end. update(Cmd, Rules) -> emqx_config:update(emqx_authz_schema, ?CONF_KEY_PATH, {Cmd, Rules}). @@ -56,6 +66,13 @@ pre_config_update({head, Rules}, OldConf) when is_list(Rules), is_list(OldConf) Rules ++ OldConf; pre_config_update({tail, Rules}, OldConf) when is_list(Rules), is_list(OldConf) -> OldConf ++ Rules; +pre_config_update({{replace_once, Id}, Rule}, OldConf) when is_map(Rule), is_list(OldConf) -> + {Index, _} = case find_rule_by_id(Id, lookup()) of + {error, Reason} -> error(Reason); + R -> R + end, + {OldConf1, OldConf2} = lists:split(Index, OldConf), + lists:droplast(OldConf1) ++ [Rule] ++ OldConf2; pre_config_update({_, Rules}, _OldConf) when is_list(Rules)-> %% overwrite the entire config! Rules. @@ -63,19 +80,38 @@ pre_config_update({_, Rules}, _OldConf) when is_list(Rules)-> post_config_update(_, undefined, _OldConf) -> ok; post_config_update({head, Rules}, _NewRules, _OldConf) -> - InitedRules = [init_rule(Rule) || Rule <- check_rules(Rules)], - ok = emqx_hooks:put('client.authorize', {?MODULE, authorize, [lists:append(InitedRules, lookup())]}, -1), + InitedRules = [init_rule(R) || R <- check_rules(Rules)], + ok = emqx_hooks:put('client.authorize', {?MODULE, authorize, [InitedRules ++ lookup()]}, -1), ok = emqx_authz_cache:drain_cache(); + post_config_update({tail, Rules}, _NewRules, _OldConf) -> - InitedRules = [init_rule(Rule) || Rule <- check_rules(Rules)], - emqx_hooks:put('client.authorize', {?MODULE, authorize, [lists:append(InitedRules, lookup())]}, -1), + InitedRules = [init_rule(R) || R <- check_rules(Rules)], + emqx_hooks:put('client.authorize', {?MODULE, authorize, [lookup() ++ InitedRules]}, -1), ok = emqx_authz_cache:drain_cache(); + +post_config_update({{replace_once, Id}, Rule}, _NewRules, _OldConf) when is_map(Rule) -> + OldInitedRules = lookup(), + {Index, OldRule} = case find_rule_by_id(Id, OldInitedRules) of + {error, Reason} -> error(Reason); + R -> R + end, + case maps:get(type, OldRule, undefined) of + undefined -> ok; + _ -> + #{annotations := #{id := Id}} = OldRule, + ok = emqx_resource:remove(Id) + end, + {OldRules1, OldRules2 } = lists:split(Index, OldInitedRules), + InitedRules = [init_rule(R#{annotations => #{id => Id}}) || R <- check_rules([Rule])], + ok = emqx_hooks:put('client.authorize', {?MODULE, authorize, [lists:droplast(OldRules1) ++ InitedRules ++ OldRules2]}, -1), + ok = emqx_authz_cache:drain_cache(); + post_config_update(_, NewRules, _OldConf) -> %% overwrite the entire config! OldInitedRules = lookup(), InitedRules = [init_rule(Rule) || Rule <- NewRules], ok = emqx_hooks:put('client.authorize', {?MODULE, authorize, [InitedRules]}, -1), - lists:foreach(fun (#{type := _Type, enable := true, metadata := #{id := Id}}) -> + lists:foreach(fun (#{type := _Type, enable := true, annotations := #{id := Id}}) -> ok = emqx_resource:remove(Id); (_) -> ok end, OldInitedRules), @@ -91,6 +127,14 @@ check_rules(RawRules) -> #{authorization := #{rules := Rules}} = hocon_schema:richmap_to_map(CheckConf), Rules. +find_rule_by_id(Id, Rules) -> find_rule_by_id(Id, Rules, 1). +find_rule_by_id(_RuleId, [], _N) -> {error, not_found_rule}; +find_rule_by_id(RuleId, [ Rule = #{annotations := #{id := Id}} | Tail], N) -> + case RuleId =:= Id of + true -> {N, Rule}; + false -> find_rule_by_id(RuleId, Tail, N + 1) + end. + find_action_in_hooks() -> Callbacks = emqx_hooks:lookup('client.authorize'), [Action] = [Action || {callback,{?MODULE, authorize, _} = Action, _, _} <- Callbacks ], @@ -99,6 +143,19 @@ find_action_in_hooks() -> gen_id(Type) -> iolist_to_binary([io_lib:format("~s_~s",[?APP, Type]), "_", integer_to_list(erlang:system_time())]). +create_resource(#{type := DB, + config := Config, + annotations := #{id := ResourceID}}) -> + case emqx_resource:update( + ResourceID, + list_to_existing_atom(io_lib:format("~s_~s",[emqx_connector, DB])), + Config, + []) + of + {ok, _} -> ResourceID; + {error, already_created} -> ResourceID; + {error, Reason} -> {error, Reason} + end; create_resource(#{type := DB, config := Config}) -> ResourceID = gen_id(DB), @@ -116,13 +173,19 @@ create_resource(#{type := DB, init_rule(#{topics := Topics, action := Action, permission := Permission, - principal := Principal - } = Rule) when ?ALLOW_DENY(Permission), ?PUBSUB(Action), is_list(Topics) -> + principal := Principal, + annotations := #{id := Id} + } = Rule) when ?ALLOW_DENY(Permission), ?PUBSUB(Action), is_list(Topics) -> Rule#{annotations => - #{id => gen_id(simple), + #{id => Id, principal => compile_principal(Principal), topics => [compile_topic(Topic) || Topic <- Topics]} }; +init_rule(#{topics := Topics, + action := Action, + permission := Permission + } = Rule) when ?ALLOW_DENY(Permission), ?PUBSUB(Action), is_list(Topics) -> + init_rule(Rule#{annotations =>#{id => gen_id(simple)}}); init_rule(#{principal := Principal, enable := true, @@ -132,7 +195,7 @@ init_rule(#{principal := Principal, NConfig = maps:merge(Config, #{base_url => maps:remove(query, Url)}), case create_resource(Rule#{config := NConfig}) of {error, Reason} -> error({load_config_error, Reason}); - Id -> Rule#{annotations => + Id -> Rule#{annotations => #{id => Id, principal => compile_principal(Principal) } @@ -146,7 +209,7 @@ init_rule(#{principal := Principal, DB =:= mongo -> case create_resource(Rule) of {error, Reason} -> error({load_config_error, Reason}); - Id -> Rule#{annotations => + Id -> Rule#{annotations => #{id => Id, principal => compile_principal(Principal) } @@ -162,7 +225,7 @@ init_rule(#{principal := Principal, Mod = list_to_existing_atom(io_lib:format("~s_~s",[?APP, DB])), case create_resource(Rule) of {error, Reason} -> error({load_config_error, Reason}); - Id -> Rule#{annotations => + Id -> Rule#{annotations => #{id => Id, principal => compile_principal(Principal), sql => Mod:parse_query(SQL) diff --git a/apps/emqx_authz/src/emqx_authz_api.erl b/apps/emqx_authz/src/emqx_authz_api.erl index ad56d2573..d69294eae 100644 --- a/apps/emqx_authz/src/emqx_authz_api.erl +++ b/apps/emqx_authz/src/emqx_authz_api.erl @@ -20,13 +20,17 @@ -include("emqx_authz.hrl"). +-define(EXAMPLE_RETURNED_RULE1, + #{principal => <<"all">>, + permission => <<"allow">>, + action => <<"all">>, + topics => [<<"#">>], + annotations => #{id => 1} + }). + + -define(EXAMPLE_RETURNED_RULES, - #{rules => [ #{principal => <<"all">>, - permission => <<"allow">>, - action => <<"all">>, - topics => [<<"#">>], - metadata => #{id => 1} - } + #{rules => [?EXAMPLE_RETURNED_RULE1 ] }). @@ -37,10 +41,12 @@ -export([ api_spec/0 , authorization/2 + , authorization_once/2 ]). api_spec() -> - {[ authorization_api() + {[ authorization_api(), + authorization_api2() ], definitions()}. definitions() -> emqx_authz_api_schema:definitions(). @@ -99,13 +105,79 @@ authorization_api() -> }, {"/authorization", Metadata, authorization}. +authorization_api2() -> + Metadata = #{ + get => #{ + description => "List authorization rules", + parameters => [ + #{ + name => id, + in => path, + schema => #{ + type => string + }, + required => true + } + ], + responses => #{ + <<"200">> => #{ + description => <<"OK">>, + content => #{ + 'application/json' => #{ + schema => minirest:ref(<<"returned_rules">>), + examples => #{ + rules => #{ + summary => <<"Rules">>, + value => jsx:encode(?EXAMPLE_RETURNED_RULE1) + } + } + } + } + }, + <<"404">> => #{description => <<"Not Found">>} + } + }, + put => #{ + description => "Update rule", + parameters => [ + #{ + name => id, + in => path, + schema => #{ + type => string + }, + required => true + } + ], + requestBody => #{ + content => #{ + 'application/json' => #{ + schema => minirest:ref(<<"rules">>), + examples => #{ + simple_rule => #{ + summary => <<"Rules">>, + value => jsx:encode(?EXAMPLE_RULE1) + } + } + } + } + }, + responses => #{ + <<"201">> => #{description => <<"Created">>}, + <<"400">> => #{description => <<"Bad Request">>} + } + } + }, + {"/authorization/:id", Metadata, authorization_once}. + + authorization(get, _Request) -> - Rules = lists:foldl(fun (#{type := _Type, enable := true, metadata := #{id := Id} = MataData} = Rule, AccIn) -> + Rules = lists:foldl(fun (#{type := _Type, enable := true, annotations := #{id := Id} = Annotations} = Rule, AccIn) -> NRule = case emqx_resource:health_check(Id) of ok -> - Rule#{metadata => MataData#{status => healthy}}; + Rule#{annotations => Annotations#{status => healthy}}; _ -> - Rule#{metadata => MataData#{status => unhealthy}} + Rule#{annotations => Annotations#{status => unhealthy}} end, lists:append(AccIn, [NRule]); (Rule, AccIn) -> @@ -120,4 +192,29 @@ authorization(post, Request) -> {error, Reason} -> {400, #{messgae => atom_to_binary(Reason)}} end. +authorization_once(get, Request) -> + Id = cowboy_req:binding(id, Request), + case emqx_authz:lookup(Id) of + {error, Reason} -> {404, #{messgae => atom_to_binary(Reason)}}; + Rule -> + case maps:get(type, Rule, undefined) of + undefined -> {200, Rule}; + _ -> + case emqx_resource:health_check(Id) of + ok -> + {200, Rule#{annotations => #{status => healthy}}}; + _ -> + {200, Rule#{annotations => #{status => unhealthy}}} + end + + end + end; +authorization_once(put, Request) -> + RuleId = cowboy_req:binding(id, Request), + {ok, Body, _} = cowboy_req:read_body(Request), + RawConfig = jsx:decode(Body, [return_maps]), + case emqx_authz:update({replace_once, RuleId}, RawConfig) of + ok -> {200}; + {error, Reason} -> {400, #{messgae => atom_to_binary(Reason)}} + end. diff --git a/apps/emqx_authz/test/emqx_authz_SUITE.erl b/apps/emqx_authz/test/emqx_authz_SUITE.erl index dd3f38519..36ed1107d 100644 --- a/apps/emqx_authz/test/emqx_authz_SUITE.erl +++ b/apps/emqx_authz/test/emqx_authz_SUITE.erl @@ -22,6 +22,8 @@ -include_lib("eunit/include/eunit.hrl"). -include_lib("common_test/include/ct.hrl"). +-define(CONF_DEFAULT, <<"authorization: {rules: []}">>). + all() -> emqx_ct:all(?MODULE). @@ -29,82 +31,104 @@ groups() -> []. init_per_suite(Config) -> + ok = emqx_config:init_load(emqx_authz_schema, ?CONF_DEFAULT), ok = emqx_ct_helpers:start_apps([emqx_authz]), ok = emqx_config:update([zones, default, authorization, cache, enable], false), ok = emqx_config:update([zones, default, authorization, enable], true), - emqx_authz:update(replace, []), Config. end_per_suite(_Config) -> - emqx_ct_helpers:stop_apps([emqx_authz]). + ok = emqx_authz:update(replace, []), + emqx_ct_helpers:stop_apps([emqx_authz]), + ok. --define(RULE1, #{principal => all, - topics => [<<"#">>], - action => all, - permission => deny} +-define(RULE1, #{<<"principal">> => <<"all">>, + <<"topics">> => [<<"#">>], + <<"action">> => <<"all">>, + <<"permission">> => <<"deny">>} ). --define(RULE2, #{principal => - #{ipaddress => <<"127.0.0.1">>}, - topics => - [#{eq => <<"#">>}, - #{eq => <<"+">>} +-define(RULE2, #{<<"principal">> => + #{<<"ipaddress">> => <<"127.0.0.1">>}, + <<"topics">> => + [#{<<"eq">> => <<"#">>}, + #{<<"eq">> => <<"+">>} ] , - action => all, - permission => allow} + <<"action">> => <<"all">>, + <<"permission">> => <<"allow">>} ). --define(RULE3,#{principal => - #{'and' => [#{username => "^test?"}, - #{clientid => "^test?"} +-define(RULE3,#{<<"principal">> => + #{<<"and">> => [#{<<"username">> => <<"^test?">>}, + #{<<"clientid">> => <<"^test?">>} ]}, - topics => [<<"test">>], - action => publish, - permission => allow} + <<"topics">> => [<<"test">>], + <<"action">> => <<"publish">>, + <<"permission">> => <<"allow">>} ). --define(RULE4,#{principal => - #{'or' => [#{username => <<"^test">>}, - #{clientid => <<"test?">>} - ]}, - topics => [<<"%u">>,<<"%c">>], - action => publish, - permission => deny} +-define(RULE4,#{<<"principal">> => + #{<<"or">> => [#{<<"username">> => <<"^test">>}, + #{<<"clientid">> => <<"test?">>} + ]}, + <<"topics">> => [<<"%u">>,<<"%c">>], + <<"action">> => <<"publish">>, + <<"permission">> => <<"deny">>} ). - %%------------------------------------------------------------------------------ %% Testcases %%------------------------------------------------------------------------------ -t_init_rule(_) -> - ?assertMatch(#{annotations := #{id := _ID, - principal := all, - topics := [['#']]} - }, emqx_authz:init_rule(?RULE1)), - ?assertMatch(#{annotations := #{principal := - #{ipaddress := {{127,0,0,1},{127,0,0,1},32}}, - topics := [#{eq := ['#']}, - #{eq := ['+']}], - id := _ID} - }, emqx_authz:init_rule(?RULE2)), - ?assertMatch(#{annotations := - #{principal := + +t_update_rule(_) -> + ok = emqx_authz:update(replace, [?RULE2]), + ok = emqx_authz:update(head, [?RULE1]), + ok = emqx_authz:update(tail, [?RULE3]), + + Lists1 = emqx_authz:check_rules([?RULE1, ?RULE2, ?RULE3]), + ?assertMatch(Lists1, emqx_config:get([authorization, rules], [])), + + [#{annotations := #{id := Id1, + principal := all, + topics := [['#']]} + }, + #{annotations := #{id := Id2, + principal := #{ipaddress := {{127,0,0,1},{127,0,0,1},32}}, + topics := [#{eq := ['#']}, #{eq := ['+']}]} + }, + #{annotations := #{id := Id3, + principal := #{'and' := [#{username := {re_pattern, _, _, _, _}}, #{clientid := {re_pattern, _, _, _, _}} ] }, - topics := [[<<"test">>]], - id := _ID} - }, emqx_authz:init_rule(?RULE3)), - ?assertMatch(#{annotations := - #{principal := - #{'or' := [#{username := {re_pattern, _, _, _, _}}, - #{clientid := {re_pattern, _, _, _, _}} - ] - }, - topics := [#{pattern := [<<"%u">>]}, - #{pattern := [<<"%c">>]} - ], - id := _ID} - }, emqx_authz:init_rule(?RULE4)), - ok. + topics := [[<<"test">>]]} + } + ] = emqx_authz:lookup(), + + ok = emqx_authz:update({replace_once, Id3}, ?RULE4), + Lists2 = emqx_authz:check_rules([?RULE1, ?RULE2, ?RULE4]), + ?assertMatch(Lists2, emqx_config:get([authorization, rules], [])), + + [#{annotations := #{id := Id1, + principal := all, + topics := [['#']]} + }, + #{annotations := #{id := Id2, + principal := #{ipaddress := {{127,0,0,1},{127,0,0,1},32}}, + topics := [#{eq := ['#']}, + #{eq := ['+']}]} + }, + #{annotations := #{id := Id3, + principal := + #{'or' := [#{username := {re_pattern, _, _, _, _}}, + #{clientid := {re_pattern, _, _, _, _}} + ] + }, + topics := [#{pattern := [<<"%u">>]}, + #{pattern := [<<"%c">>]} + ]} + } + ] = emqx_authz:lookup(), + + ok = emqx_authz:update(replace, []). t_authz(_) -> ClientInfo1 = #{clientid => <<"test">>, @@ -132,10 +156,10 @@ t_authz(_) -> listener => mqtt_tcp }, - Rules1 = [emqx_authz:init_rule(Rule) || Rule <- [?RULE1, ?RULE2]], - Rules2 = [emqx_authz:init_rule(Rule) || Rule <- [?RULE2, ?RULE1]], - Rules3 = [emqx_authz:init_rule(Rule) || Rule <- [?RULE3, ?RULE4]], - Rules4 = [emqx_authz:init_rule(Rule) || Rule <- [?RULE4, ?RULE1]], + Rules1 = [emqx_authz:init_rule(Rule) || Rule <- emqx_authz:check_rules([?RULE1, ?RULE2])], + Rules2 = [emqx_authz:init_rule(Rule) || Rule <- emqx_authz:check_rules([?RULE2, ?RULE1])], + Rules3 = [emqx_authz:init_rule(Rule) || Rule <- emqx_authz:check_rules([?RULE3, ?RULE4])], + Rules4 = [emqx_authz:init_rule(Rule) || Rule <- emqx_authz:check_rules([?RULE4, ?RULE1])], ?assertEqual({stop, deny}, emqx_authz:authorize(ClientInfo1, subscribe, <<"#">>, deny, [])), diff --git a/apps/emqx_authz/test/emqx_authz_api_SUITE.erl b/apps/emqx_authz/test/emqx_authz_api_SUITE.erl index 0bb1b2132..ec713445f 100644 --- a/apps/emqx_authz/test/emqx_authz_api_SUITE.erl +++ b/apps/emqx_authz/test/emqx_authz_api_SUITE.erl @@ -18,33 +18,26 @@ -compile(nowarn_export_all). -compile(export_all). -% -include("emqx_authz.hrl"). -% -include_lib("eunit/include/eunit.hrl"). -% -include_lib("common_test/include/ct.hrl"). +-include("emqx_authz.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). -% -import(emqx_ct_http, [ request_api/3 -% , request_api/5 -% , get_http_data/1 -% , create_default_app/0 -% , delete_default_app/0 -% , default_auth_header/0 -% ]). +-import(emqx_ct_http, [ request_api/3 + , request_api/5 + , get_http_data/1 + , create_default_app/0 + , delete_default_app/0 + , default_auth_header/0 + ]). -% -define(HOST, "http://127.0.0.1:8081/"). -% -define(API_VERSION, "v4"). -% -define(BASE_PATH, "api"). +-define(HOST, "http://127.0.0.1:8081/"). +-define(API_VERSION, "v5"). +-define(BASE_PATH, "api/authorization"). --define(CONF_DEFAULT, <<""" -authorization:{ - rules: [ - ] -} -""">>). +-define(CONF_DEFAULT, <<"authorization: {rules: []}">>). all() -> -%% TODO: V5 API -%% emqx_ct:all(?MODULE). - [t_api_unit_test]. + emqx_ct:all(?MODULE). groups() -> []. @@ -52,12 +45,15 @@ groups() -> init_per_suite(Config) -> ok = emqx_config:init_load(emqx_authz_schema, ?CONF_DEFAULT), ok = emqx_ct_helpers:start_apps([emqx_authz]), + ok = emqx_config:update([zones, default, authorization, cache, enable], false), + ok = emqx_config:update([zones, default, authorization, enable], true), - %create_default_app(), + create_default_app(), Config. end_per_suite(_Config) -> ok = emqx_authz:update(replace, []), + delete_default_app(), emqx_ct_helpers:stop_apps([emqx_authz]), ok. @@ -77,29 +73,12 @@ end_per_suite(_Config) -> % set_special_configs(_App) -> % ok. -% %%------------------------------------------------------------------------------ -% %% Testcases -% %%------------------------------------------------------------------------------ +%%------------------------------------------------------------------------------ +%% Testcases +%%------------------------------------------------------------------------------ -t_api_unit_test(_Config) -> - %% TODO: Decode from JSON or HOCON, instead of hand-crafting decode result - Rule1 = #{<<"principal">> => - #{<<"and">> => [#{<<"username">> => <<"^test?">>}, - #{<<"clientid">> => <<"^test?">>} - ]}, - <<"action">> => <<"subscribe">>, - <<"topics">> => [<<"%u">>], - <<"permission">> => <<"allow">> - }, - ok = emqx_authz_api:push_authz(#{}, Rule1), - [#{action := subscribe, - permission := allow, - principal := - #{'and' := [#{username := <<"^test?">>}, - #{clientid := <<"^test?">>}]}, - topics := [<<"%u">>]}] = emqx_config:get([authorization, rules]). - -% t_api(_Config) -> +% t_api_unit_test(_Config) -> +% %% TODO: Decode from JSON or HOCON, instead of hand-crafting decode result % Rule1 = #{<<"principal">> => % #{<<"and">> => [#{<<"username">> => <<"^test?">>}, % #{<<"clientid">> => <<"^test?">>} @@ -108,53 +87,89 @@ t_api_unit_test(_Config) -> % <<"topics">> => [<<"%u">>], % <<"permission">> => <<"allow">> % }, -% {ok, _} = request_http_rest_add(["authz/push"], #{rules => [Rule1]}), -% {ok, Result1} = request_http_rest_lookup(["authz"]), -% ?assertMatch([Rule1 | _ ], get_http_data(Result1)), +% ok = emqx_authz_api:push_authz(#{}, Rule1), +% [#{action := subscribe, +% permission := allow, +% principal := +% #{'and' := [#{username := <<"^test?">>}, +% #{clientid := <<"^test?">>}]}, +% topics := [<<"%u">>]}] = emqx_config:get([authorization, rules]). -% Rule2 = #{<<"principal">> => #{<<"ipaddress">> => <<"127.0.0.1">>}, -% <<"action">> => <<"publish">>, -% <<"topics">> => [#{<<"eq">> => <<"#">>}, -% #{<<"eq">> => <<"+">>} -% ], -% <<"permission">> => <<"deny">> -% }, -% {ok, _} = request_http_rest_add(["authz/append"], #{rules => [Rule2]}), -% {ok, Result2} = request_http_rest_lookup(["authz"]), -% ?assertEqual(Rule2#{<<"principal">> => #{<<"ipaddress">> => "127.0.0.1"}}, -% lists:last(get_http_data(Result2))), +t_post(_) -> + Rules1 = request(get, uri(), []), + ct:print("============~p~n",[Rules1]), + ok. -% {ok, _} = request_http_rest_update(["authz"], #{rules => []}), -% {ok, Result3} = request_http_rest_lookup(["authz"]), -% ?assertEqual([], get_http_data(Result3)), -% ok. +t_api(_Config) -> + Rule1 = #{<<"principal">> => + #{<<"and">> => [#{<<"username">> => <<"^test?">>}, + #{<<"clientid">> => <<"^test?">>} + ]}, + <<"action">> => <<"subscribe">>, + <<"topics">> => [<<"%u">>], + <<"permission">> => <<"allow">> + }, + {ok, _} = request_http_rest_add(["authz/push"], #{rules => [Rule1]}), + {ok, Result1} = request_http_rest_lookup(["authz"]), + ?assertMatch([Rule1 | _ ], get_http_data(Result1)), -% %%-------------------------------------------------------------------- -% %% HTTP Request -% %%-------------------------------------------------------------------- + Rule2 = #{<<"principal">> => #{<<"ipaddress">> => <<"127.0.0.1">>}, + <<"action">> => <<"publish">>, + <<"topics">> => [#{<<"eq">> => <<"#">>}, + #{<<"eq">> => <<"+">>} + ], + <<"permission">> => <<"deny">> + }, + {ok, _} = request_http_rest_add(["authz/append"], #{rules => [Rule2]}), + {ok, Result2} = request_http_rest_lookup(["authz"]), + ?assertEqual(Rule2#{<<"principal">> => #{<<"ipaddress">> => "127.0.0.1"}}, + lists:last(get_http_data(Result2))), -% request_http_rest_list(Path) -> -% request_api(get, uri(Path), default_auth_header()). + {ok, _} = request_http_rest_update(["authz"], #{rules => []}), + {ok, Result3} = request_http_rest_lookup(["authz"]), + ?assertEqual([], get_http_data(Result3)), + ok. -% request_http_rest_lookup(Path) -> -% request_api(get, uri([Path]), default_auth_header()). +%%-------------------------------------------------------------------- +%% HTTP Request +%%-------------------------------------------------------------------- -% request_http_rest_add(Path, Params) -> -% request_api(post, uri(Path), [], default_auth_header(), Params). +request(Method, Url, Body) -> + Request = case Body of + [] -> {Url, [{"username", "admin"}, {"password", "public"}]}; + _ -> {Url, [{"username", "admin"}, {"password", "public"}], "application/json", Body} + end, + case httpc:request(Method, Request, [], [{body_format, binary}]) of + {error, socket_closed_remotely} -> + {error, socket_closed_remotely}; + {ok, {{"HTTP/1.1", Code, _}, _Headers, Return} } -> + {ok, Code, Return}; + {ok, {Reason, _, _}} -> + {error, Reason} + end. -% request_http_rest_update(Path, Params) -> -% request_api(put, uri([Path]), [], default_auth_header(), Params). +request_http_rest_list(Path) -> + request_api(get, uri(Path), default_auth_header()). -% request_http_rest_delete(Login) -> -% request_api(delete, uri([Login]), default_auth_header()). +request_http_rest_lookup(Path) -> + request_api(get, uri([Path]), default_auth_header()). -% uri() -> uri([]). -% uri(Parts) when is_list(Parts) -> -% NParts = [b2l(E) || E <- Parts], -% ?HOST ++ filename:join([?BASE_PATH, ?API_VERSION | NParts]). +request_http_rest_add(Path, Params) -> + request_api(post, uri(Path), [], default_auth_header(), Params). -% %% @private -% b2l(B) when is_binary(B) -> -% binary_to_list(B); -% b2l(L) when is_list(L) -> -% L. +request_http_rest_update(Path, Params) -> + request_api(put, uri([Path]), [], default_auth_header(), Params). + +request_http_rest_delete(Login) -> + request_api(delete, uri([Login]), default_auth_header()). + +uri() -> uri([]). +uri(Parts) when is_list(Parts) -> + NParts = [b2l(E) || E <- Parts], + ?HOST ++ filename:join([?BASE_PATH, ?API_VERSION | NParts]). + +%% @private +b2l(B) when is_binary(B) -> + binary_to_list(B); +b2l(L) when is_list(L) -> + L. diff --git a/apps/emqx_authz/test/emqx_authz_mongo_SUITE.erl b/apps/emqx_authz/test/emqx_authz_mongo_SUITE.erl index 9ba6aa843..67f9a3bfe 100644 --- a/apps/emqx_authz/test/emqx_authz_mongo_SUITE.erl +++ b/apps/emqx_authz/test/emqx_authz_mongo_SUITE.erl @@ -31,6 +31,7 @@ groups() -> init_per_suite(Config) -> meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]), meck:expect(emqx_resource, create, fun(_, _, _) -> {ok, meck_data} end), + meck:expect(emqx_resource, remove, fun(_) -> ok end ), ok = emqx_ct_helpers:start_apps([emqx_authz]), ok = emqx_config:update([zones, default, authorization, cache, enable], false), diff --git a/apps/emqx_authz/test/emqx_authz_mysql_SUITE.erl b/apps/emqx_authz/test/emqx_authz_mysql_SUITE.erl index 54aa7d8fc..a1120684e 100644 --- a/apps/emqx_authz/test/emqx_authz_mysql_SUITE.erl +++ b/apps/emqx_authz/test/emqx_authz_mysql_SUITE.erl @@ -31,6 +31,7 @@ groups() -> init_per_suite(Config) -> meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]), meck:expect(emqx_resource, create, fun(_, _, _) -> {ok, meck_data} end ), + meck:expect(emqx_resource, remove, fun(_) -> ok end ), ok = emqx_ct_helpers:start_apps([emqx_authz]), diff --git a/apps/emqx_authz/test/emqx_authz_pgsql_SUITE.erl b/apps/emqx_authz/test/emqx_authz_pgsql_SUITE.erl index 66a6581a8..61a719474 100644 --- a/apps/emqx_authz/test/emqx_authz_pgsql_SUITE.erl +++ b/apps/emqx_authz/test/emqx_authz_pgsql_SUITE.erl @@ -31,6 +31,7 @@ groups() -> init_per_suite(Config) -> meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]), meck:expect(emqx_resource, create, fun(_, _, _) -> {ok, meck_data} end ), + meck:expect(emqx_resource, remove, fun(_) -> ok end ), ok = emqx_ct_helpers:start_apps([emqx_authz]), diff --git a/apps/emqx_authz/test/emqx_authz_redis_SUITE.erl b/apps/emqx_authz/test/emqx_authz_redis_SUITE.erl index 0eb42bdb8..4a1765589 100644 --- a/apps/emqx_authz/test/emqx_authz_redis_SUITE.erl +++ b/apps/emqx_authz/test/emqx_authz_redis_SUITE.erl @@ -31,6 +31,7 @@ groups() -> init_per_suite(Config) -> meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]), meck:expect(emqx_resource, create, fun(_, _, _) -> {ok, meck_data} end ), + meck:expect(emqx_resource, remove, fun(_) -> ok end ), ok = emqx_ct_helpers:start_apps([emqx_authz]),