Просмотр исходного кода

feat(authz): more better update function

Signed-off-by: zhanghongtong <rory-z@outlook.com>
zhanghongtong 4 лет назад
Родитель
Сommit
4bb1e9c964

+ 75 - 12
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)

+ 107 - 10
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.
 

+ 83 - 59
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, [])),

+ 110 - 95
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").
-
-% -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(CONF_DEFAULT, <<"""
-authorization:{
-    rules: [
-    ]
-}
-""">>).
+-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
+                      ]).
+
+-define(HOST, "http://127.0.0.1:8081/").
+-define(API_VERSION, "v5").
+-define(BASE_PATH, "api/authorization").
+
+-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)),
-
-%     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))),
-
-%     {ok, _} = request_http_rest_update(["authz"], #{rules => []}),
-%     {ok, Result3} = request_http_rest_lookup(["authz"]),
-%     ?assertEqual([], get_http_data(Result3)),
-%     ok.
-
-% %%--------------------------------------------------------------------
-% %% HTTP Request
-% %%--------------------------------------------------------------------
-
-% request_http_rest_list(Path) ->
-%     request_api(get, uri(Path), default_auth_header()).
-
-% request_http_rest_lookup(Path) ->
-%     request_api(get, uri([Path]), default_auth_header()).
-
-% request_http_rest_add(Path, Params) ->
-%     request_api(post, uri(Path), [], default_auth_header(), Params).
-
-% request_http_rest_update(Path, Params) ->
-%     request_api(put, uri([Path]), [], default_auth_header(), Params).
+%     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_post(_) ->
+    Rules1 = request(get, uri(), []),
+    ct:print("============~p~n",[Rules1]),
+    ok.
 
-% request_http_rest_delete(Login) ->
-%     request_api(delete, uri([Login]), default_auth_header()).
+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)),
+
+    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))),
+
+    {ok, _} = request_http_rest_update(["authz"], #{rules => []}),
+    {ok, Result3} = request_http_rest_lookup(["authz"]),
+    ?assertEqual([], get_http_data(Result3)),
+    ok.
 
-% uri() -> uri([]).
-% uri(Parts) when is_list(Parts) ->
-%     NParts = [b2l(E) || E <- Parts],
-%     ?HOST ++ filename:join([?BASE_PATH, ?API_VERSION | NParts]).
+%%--------------------------------------------------------------------
+%% HTTP Request
+%%--------------------------------------------------------------------
 
-% %% @private
-% b2l(B) when is_binary(B) ->
-%     binary_to_list(B);
-% b2l(L) when is_list(L) ->
-%     L.
+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_list(Path) ->
+    request_api(get, uri(Path), default_auth_header()).
+
+request_http_rest_lookup(Path) ->
+    request_api(get, uri([Path]), default_auth_header()).
+
+request_http_rest_add(Path, Params) ->
+    request_api(post, uri(Path), [], default_auth_header(), Params).
+
+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.

+ 1 - 0
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),

+ 1 - 0
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]),
 

+ 1 - 0
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]),
 

+ 1 - 0
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]),