Parcourir la source

chore(authz): test Mongo backend with real Mongo

Ilya Averyanov il y a 4 ans
Parent
commit
28c319b6c4

+ 2 - 2
apps/emqx_authz/src/emqx_authz.erl

@@ -217,10 +217,10 @@ do_post_update({{?CMD_DELETE, Type}, _Source}, _NewSources) ->
     ok = ensure_resource_deleted(OldSource),
     ok = emqx_hooks:put('client.authorize', {?MODULE, authorize, [Front ++ Rear]}, -1),
     ok = emqx_authz_cache:drain_cache();
-do_post_update(_, NewSources) ->
+do_post_update({?CMD_REPLACE, Sources}, _NewSources) ->
     %% overwrite the entire config!
     OldInitedSources = lookup(),
-    InitedSources = init_sources(NewSources),
+    InitedSources = init_sources(check_sources(Sources)),
     ok = emqx_hooks:put('client.authorize', {?MODULE, authorize, [InitedSources]}, -1),
     lists:foreach(fun ensure_resource_deleted/1, OldInitedSources),
     ok = emqx_authz_cache:drain_cache().

+ 216 - 90
apps/emqx_authz/test/emqx_authz_mongodb_SUITE.erl

@@ -23,112 +23,238 @@
 -include_lib("common_test/include/ct.hrl").
 -include_lib("emqx/include/emqx_placeholder.hrl").
 
+-define(MONGO_HOST, "mongo").
+-define(MONGO_PORT, 27017).
+-define(MONGO_CLIENT, 'emqx_authz_mongo_SUITE_client').
+
 all() ->
     emqx_common_test_helpers:all(?MODULE).
 
 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_common_test_helpers:start_apps(
-           [emqx_conf, emqx_authz],
-           fun set_special_configs/1
-          ),
-
-    Rules = [#{<<"type">> => <<"mongodb">>,
-               <<"mongo_type">> => <<"single">>,
-               <<"server">> => <<"127.0.0.1:27017">>,
-               <<"pool_size">> => 1,
-               <<"database">> => <<"mqtt">>,
-               <<"ssl">> => #{<<"enable">> => false},
-               <<"collection">> => <<"fake">>,
-               <<"selector">> => #{<<"a">> => <<"b">>}
-              }],
-    {ok, _} = emqx_authz:update(replace, Rules),
+init_per_testcase(_TestCase, Config) ->
+    {ok, _} = mc_worker_api:connect(mongo_config()),
     Config.
 
+end_per_testcase(_TestCase, _Config) ->
+    ok = reset_samples(),
+    ok = mc_worker_api:disconnect(?MONGO_CLIENT).
+
+init_per_suite(Config) ->
+    case emqx_authz_test_lib:is_tcp_server_available(?MONGO_HOST, ?MONGO_PORT) of
+        true ->
+            ok = emqx_common_test_helpers:start_apps(
+                   [emqx_conf, emqx_authz],
+                   fun set_special_configs/1
+                  ),
+            ok = start_apps([emqx_resource, emqx_connector]),
+            Config;
+        false ->
+            {skip, no_mongo}
+    end.
+
 end_per_suite(_Config) ->
-    {ok, _} = emqx:update_config(
-                [authorization],
-                #{<<"no_match">> => <<"allow">>,
-                  <<"cache">> => #{<<"enable">> => <<"true">>},
-                  <<"sources">> => []}),
-    emqx_common_test_helpers:stop_apps([emqx_authz, emqx_conf]),
-    meck:unload(emqx_resource),
-    ok.
+    ok = emqx_authz_test_lib:reset_authorizers(),
+    ok = stop_apps([emqx_resource, emqx_connector]),
+    ok = emqx_common_test_helpers:stop_apps([emqx_authz]).
 
 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], []),
-    ok;
-set_special_configs(_App) ->
+    ok = emqx_authz_test_lib:reset_authorizers();
+
+set_special_configs(_) ->
     ok.
 
--define(SOURCE1,[#{<<"topics">> => [<<"#">>],
-                 <<"permission">> => <<"deny">>,
-                 <<"action">> => <<"all">>}]).
--define(SOURCE2,[#{<<"topics">> => [<<"eq #">>],
-                 <<"permission">> => <<"allow">>,
-                 <<"action">> => <<"all">>}]).
--define(SOURCE3,[#{<<"topics">> => [<<"test/", ?PH_CLIENTID/binary>>],
-                 <<"permission">> => <<"allow">>,
-                 <<"action">> => <<"subscribe">>}]).
--define(SOURCE4,[#{<<"topics">> => [<<"test/", ?PH_USERNAME/binary>>],
+%%------------------------------------------------------------------------------
+%% Testcases
+%%------------------------------------------------------------------------------
+
+
+t_topic_rules(_Config) ->
+    ClientInfo = #{clientid => <<"clientid">>,
+                   username => <<"username">>,
+                   peerhost => {127,0,0,1},
+                   zone => default,
+                   listener => {tcp, default}
+                  },
+
+    %% No rules
+
+    ok = setup_samples([]),
+    ok = setup_config(#{}),
+
+    ok = emqx_authz_test_lib:test_samples(
+           ClientInfo,
+           [{deny, subscribe, <<"#">>},
+            {deny, subscribe, <<"subs">>},
+            {deny, publish, <<"pub">>}]),
+
+    %% Publish rules
+
+    Samples0 = populate_records(
+                 [#{<<"topics">> => [<<"eq testpub1/${username}">>]},
+                  #{<<"topics">> => [<<"testpub2/${clientid}">>, <<"testpub3/#">>]}],
+                 #{<<"permission">> => <<"allow">>,
+                   <<"action">> => <<"publish">>,
+                   <<"username">> => <<"username">>}),
+
+    ok = setup_samples(Samples0),
+    ok = setup_config(#{}),
+
+    ok = emqx_authz_test_lib:test_samples(
+           ClientInfo,
+           [{deny, publish, <<"testpub1/username">>},
+            {allow, publish, <<"testpub1/${username}">>},
+            {allow, publish, <<"testpub2/clientid">>},
+            {allow, publish, <<"testpub3/foobar">>},
+
+            {deny, publish, <<"testpub2/username">>},
+            {deny, publish, <<"testpub1/clientid">>},
+
+
+            {deny, subscribe, <<"testpub1/username">>},
+            {deny, subscribe, <<"testpub2/clientid">>},
+            {deny, subscribe, <<"testpub3/foobar">>}]),
+
+    %% Subscribe rules
+
+    Samples1 = populate_records(
+                 [#{<<"topics">> => [<<"eq testsub1/${username}">>]},
+                  #{<<"topics">> => [<<"testsub2/${clientid}">>, <<"testsub3/#">>]}],
+                 #{<<"permission">> => <<"allow">>,
+                   <<"action">> => <<"subscribe">>,
+                   <<"username">> => <<"username">>}),
+
+    ok = setup_samples(Samples1),
+    ok = setup_config(#{}),
+
+    ok = emqx_authz_test_lib:test_samples(
+           ClientInfo,
+           [{deny, subscribe, <<"testsub1/username">>},
+            {allow, subscribe, <<"testsub1/${username}">>},
+            {allow, subscribe, <<"testsub2/clientid">>},
+            {allow, subscribe, <<"testsub3/foobar">>},
+            {allow, subscribe, <<"testsub3/+/foobar">>},
+            {allow, subscribe, <<"testsub3/#">>},
+
+            {deny, subscribe, <<"testsub2/username">>},
+            {deny, subscribe, <<"testsub1/clientid">>},
+            {deny, subscribe, <<"testsub4/foobar">>},
+            {deny, publish, <<"testsub1/username">>},
+            {deny, publish, <<"testsub2/clientid">>},
+            {deny, publish, <<"testsub3/foobar">>}]),
+
+    %% All rules
+
+    Samples2 = populate_records(
+                 [#{<<"topics">> => [<<"eq testall1/${username}">>]},
+                  #{<<"topics">> => [<<"testall2/${clientid}">>, <<"testall3/#">>]}],
+                 #{<<"permission">> => <<"allow">>,
+                   <<"action">> => <<"all">>,
+                   <<"username">> => <<"username">>}),
+
+    ok = setup_samples(Samples2),
+    ok = setup_config(#{}),
+
+    ok = emqx_authz_test_lib:test_samples(
+           ClientInfo,
+           [{deny, subscribe, <<"testall1/username">>},
+            {allow, subscribe, <<"testall1/${username}">>},
+            {allow, subscribe, <<"testall2/clientid">>},
+            {allow, subscribe, <<"testall3/foobar">>},
+            {allow, subscribe, <<"testall3/+/foobar">>},
+            {allow, subscribe, <<"testall3/#">>},
+            {deny, publish, <<"testall1/username">>},
+            {allow, publish, <<"testall1/${username}">>},
+            {allow, publish, <<"testall2/clientid">>},
+            {allow, publish, <<"testall3/foobar">>},
+
+            {deny, subscribe, <<"testall2/username">>},
+            {deny, subscribe, <<"testall1/clientid">>},
+            {deny, subscribe, <<"testall4/foobar">>},
+            {deny, publish, <<"testall2/username">>},
+            {deny, publish, <<"testall1/clientid">>},
+            {deny, publish, <<"testall4/foobar">>}]).
+
+
+t_complex_selector(_) ->
+    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">>}]).
+                 <<"action">> => <<"publish">>,
+                 <<"topics">> => [<<"t">>]
+                }],
+
+    ok = setup_samples(Samples),
+    ok = setup_config(
+           #{<<"selector">> => #{<<"x">> => #{<<"u">> => <<"${username}">>,
+                                              <<"c">> => [#{<<"c">> => <<"${clientid}">>}],
+                                              <<"y">> => 1}
+                                }
+            }),
+
+    ok = emqx_authz_test_lib:test_samples(
+           ClientInfo,
+           [{allow, publish, <<"t">>}]).
 
 %%------------------------------------------------------------------------------
-%% Testcases
+%% Helpers
 %%------------------------------------------------------------------------------
 
-t_authz(_) ->
-    ClientInfo1 = #{clientid => <<"test">>,
-                    username => <<"test">>,
-                    peerhost => {127,0,0,1},
-                    zone => default,
-                    listener => {tcp, default}
-                   },
-    ClientInfo2 = #{clientid => <<"test_clientid">>,
-                    username => <<"test_username">>,
-                    peerhost => {192,168,0,10},
-                    zone => default,
-                    listener => {tcp, default}
-                   },
-    ClientInfo3 = #{clientid => <<"test_clientid">>,
-                    username => <<"fake_username">>,
-                    peerhost => {127,0,0,1},
-                    zone => default,
-                    listener => {tcp, default}
-                   },
-
-    meck:expect(emqx_resource, query, fun(_, _) -> [] end),
-    ?assertEqual(deny, emqx_access_control:authorize(ClientInfo1, subscribe, <<"#">>)), % nomatch
-    ?assertEqual(deny, emqx_access_control:authorize(ClientInfo1, publish, <<"#">>)), % nomatch
-
-    meck:expect(emqx_resource, query, fun(_, _) -> ?SOURCE1 ++ ?SOURCE2 end),
-    ?assertEqual(deny, emqx_access_control:authorize(ClientInfo1, subscribe, <<"+">>)),
-    ?assertEqual(deny, emqx_access_control:authorize(ClientInfo1, publish, <<"+">>)),
-
-    meck:expect(emqx_resource, query, fun(_, _) -> ?SOURCE2 ++ ?SOURCE1 end),
-    ?assertEqual(allow, emqx_access_control:authorize(ClientInfo1, subscribe, <<"#">>)),
-    ?assertEqual(deny, emqx_access_control:authorize(ClientInfo1, subscribe, <<"+">>)),
-
-    meck:expect(emqx_resource, query, fun(_, _) -> ?SOURCE3 ++ ?SOURCE4 end),
-    ?assertEqual(allow, emqx_access_control:authorize(
-                          ClientInfo2, subscribe, <<"test/test_clientid">>)),
-    ?assertEqual(deny,  emqx_access_control:authorize(
-                          ClientInfo2, publish,   <<"test/test_clientid">>)),
-    ?assertEqual(deny,  emqx_access_control:authorize(
-                          ClientInfo2, subscribe, <<"test/test_username">>)),
-    ?assertEqual(allow, emqx_access_control:authorize(
-                          ClientInfo2, publish,   <<"test/test_username">>)),
-    ?assertEqual(deny,  emqx_access_control:authorize(
-                          ClientInfo3, subscribe, <<"test">>)), % nomatch
-    ?assertEqual(deny,  emqx_access_control:authorize(
-                          ClientInfo3, publish,   <<"test">>)), % nomatch
+populate_records(AclRecords, AdditionalData) ->
+    [maps:merge(Record, AdditionalData) || Record <- AclRecords].
+
+setup_samples(AclRecords) ->
+    ok = reset_samples(),
+    {{true, _}, _} = mc_worker_api:insert(?MONGO_CLIENT, <<"acl">>, AclRecords),
+    ok.
+
+reset_samples() ->
+    {true, _} = mc_worker_api:delete(?MONGO_CLIENT, <<"acl">>, #{}),
     ok.
+
+setup_config(SpecialParams) ->
+    emqx_authz_test_lib:setup_config(
+      raw_mongo_authz_config(),
+      SpecialParams).
+
+raw_mongo_authz_config() ->
+    #{
+        <<"type">> => <<"mongodb">>,
+        <<"enable">> => <<"true">>,
+
+        <<"mongo_type">> => <<"single">>,
+        <<"database">> => <<"mqtt">>,
+        <<"collection">> => <<"acl">>,
+        <<"server">> => mongo_server(),
+
+        <<"selector">> => #{<<"username">> => <<"${username}">>}
+    }.
+
+mongo_server() ->
+    iolist_to_binary(
+      io_lib:format(
+        "~s:~b",
+        [?MONGO_HOST, ?MONGO_PORT])).
+
+mongo_config() ->
+    [
+     {database, <<"mqtt">>},
+     {host, ?MONGO_HOST},
+     {port, ?MONGO_PORT},
+     {register, ?MONGO_CLIENT}
+    ].
+
+start_apps(Apps) ->
+    lists:foreach(fun application:ensure_all_started/1, Apps).
+
+stop_apps(Apps) ->
+    lists:foreach(fun application:stop/1, Apps).

+ 8 - 24
apps/emqx_authz/test/emqx_authz_redis_SUITE.erl

@@ -81,7 +81,7 @@ t_topic_rules(_Config) ->
     ok = setup_sample(#{}),
     ok = setup_config(#{}),
 
-    ok = test_samples(
+    ok = emqx_authz_test_lib:test_samples(
            ClientInfo,
            [{deny, subscribe, <<"#">>},
             {deny, subscribe, <<"subs">>},
@@ -98,7 +98,7 @@ t_topic_rules(_Config) ->
     ok = setup_sample(Sample0),
     ok = setup_config(#{}),
 
-    ok = test_samples(
+    ok = emqx_authz_test_lib:test_samples(
            ClientInfo,
            [{allow, publish, <<"testpub1/username">>},
             {allow, publish, <<"testpub2/clientid">>},
@@ -123,7 +123,7 @@ t_topic_rules(_Config) ->
     ok = setup_sample(Sample1),
     ok = setup_config(#{}),
 
-    ok = test_samples(
+    ok = emqx_authz_test_lib:test_samples(
            ClientInfo,
            [{allow, subscribe, <<"testsub1/username">>},
             {allow, subscribe, <<"testsub2/clientid">>},
@@ -149,7 +149,7 @@ t_topic_rules(_Config) ->
     ok = setup_sample(Sample2),
     ok = setup_config(#{}),
 
-    ok = test_samples(
+    ok = emqx_authz_test_lib:test_samples(
            ClientInfo,
            [{allow, subscribe, <<"testall1/username">>},
             {allow, subscribe, <<"testall2/clientid">>},
@@ -183,7 +183,7 @@ t_lookups(_Config) ->
     ok = setup_sample(ByClientid),
     ok = setup_config(#{<<"cmd">> => <<"HGETALL mqtt_user:${clientid}">>}),
 
-    ok = test_samples(
+    ok = emqx_authz_test_lib:test_samples(
            ClientInfo,
            [{allow, subscribe, <<"a">>},
             {deny, subscribe, <<"b">>}]),
@@ -194,7 +194,7 @@ t_lookups(_Config) ->
     ok = setup_sample(ByPeerhost),
     ok = setup_config(#{<<"cmd">> => <<"HGETALL mqtt_user:${peerhost}">>}),
 
-    ok = test_samples(
+    ok = emqx_authz_test_lib:test_samples(
            ClientInfo,
            [{allow, subscribe, <<"a">>},
             {deny, subscribe, <<"b">>}]),
@@ -205,7 +205,7 @@ t_lookups(_Config) ->
     ok = setup_sample(ByCN),
     ok = setup_config(#{<<"cmd">> => <<"HGETALL mqtt_user:${cert_common_name}">>}),
 
-    ok = test_samples(
+    ok = emqx_authz_test_lib:test_samples(
            ClientInfo,
            [{allow, subscribe, <<"a">>},
             {deny, subscribe, <<"b">>}]),
@@ -217,7 +217,7 @@ t_lookups(_Config) ->
     ok = setup_sample(ByDN),
     ok = setup_config(#{<<"cmd">> => <<"HGETALL mqtt_user:${cert_subject}">>}),
 
-    ok = test_samples(
+    ok = emqx_authz_test_lib:test_samples(
            ClientInfo,
            [{allow, subscribe, <<"a">>},
             {deny, subscribe, <<"b">>}]).
@@ -255,22 +255,6 @@ t_redis_error(_Config) ->
 %% Helpers
 %%------------------------------------------------------------------------------
 
-test_samples(ClientInfo, Samples) ->
-    lists:foreach(
-      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).
-
-
 setup_sample(AuthzData) ->
     {ok, _} = q(["FLUSHDB"]),
     ok = lists:foreach(

+ 22 - 0
apps/emqx_authz/test/emqx_authz_test_lib.erl

@@ -17,6 +17,7 @@
 -module(emqx_authz_test_lib).
 
 -include("emqx_authz.hrl").
+-include_lib("eunit/include/eunit.hrl").
 
 -compile(nowarn_export_all).
 -compile(export_all).
@@ -34,6 +35,27 @@ reset_authorizers(Nomatch, ChacheEnabled) ->
                   <<"sources">> => []}),
     ok.
 
+setup_config(BaseConfig, SpecialParams) ->
+    ok = reset_authorizers(deny, false),
+    Config = maps:merge(BaseConfig, SpecialParams),
+    {ok, _} = emqx_authz:update(?CMD_REPLACE, [Config]),
+    ok.
+
+test_samples(ClientInfo, Samples) ->
+    lists:foreach(
+      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).
+
 is_tcp_server_available(Host, Port) ->
     case gen_tcp:connect(Host, Port, [], ?DEFAULT_CHECK_AVAIL_TIMEOUT) of
         {ok, Socket} ->