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

feat(authz): allow the placeholder to be anywhere in the topic for authz rules

firest 3 лет назад
Родитель
Сommit
c034cbf6de
2 измененных файлов с 43 добавлено и 26 удалено
  1. 11 24
      apps/emqx_authz/src/emqx_authz_rule.erl
  2. 32 2
      apps/emqx_authz/test/emqx_authz_rule_SUITE.erl

+ 11 - 24
apps/emqx_authz/src/emqx_authz_rule.erl

@@ -100,15 +100,17 @@ compile_topic(<<"eq ", Topic/binary>>) ->
 compile_topic({eq, Topic}) ->
     {eq, emqx_topic:words(bin(Topic))};
 compile_topic(Topic) ->
-    Words = emqx_topic:words(bin(Topic)),
-    case pattern(Words) of
-        true -> {pattern, Words};
-        false -> Words
+    TopicBin = bin(Topic),
+    case
+        emqx_placeholder:preproc_tmpl(
+            TopicBin,
+            #{placeholders => [?PH_USERNAME, ?PH_CLIENTID]}
+        )
+    of
+        [{str, _}] -> emqx_topic:words(TopicBin);
+        Tokens -> {pattern, Tokens}
     end.
 
-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)
@@ -202,8 +204,8 @@ match_who(_, _) ->
 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
+    TopicFilter = emqx_placeholder:proc_tmpl(PatternFilter, ClientInfo),
+    match_topic(emqx_topic:words(Topic), emqx_topic:words(TopicFilter)) orelse
         match_topics(ClientInfo, Topic, Filters);
 match_topics(ClientInfo, Topic, [TopicFilter | Filters]) ->
     match_topic(emqx_topic:words(Topic), TopicFilter) orelse
@@ -213,18 +215,3 @@ match_topic(Topic, {'eq', TopicFilter}) ->
     Topic =:= TopicFilter;
 match_topic(Topic, TopicFilter) ->
     emqx_topic:match(Topic, TopicFilter).
-
-feed_var(ClientInfo, Pattern) ->
-    feed_var(ClientInfo, Pattern, []).
-feed_var(_ClientInfo, [], Acc) ->
-    lists:reverse(Acc);
-feed_var(ClientInfo = #{clientid := undefined}, [?PH_CLIENTID | Words], Acc) ->
-    feed_var(ClientInfo, Words, [?PH_CLIENTID | Acc]);
-feed_var(ClientInfo = #{clientid := ClientId}, [?PH_CLIENTID | Words], Acc) ->
-    feed_var(ClientInfo, Words, [ClientId | Acc]);
-feed_var(ClientInfo = #{username := undefined}, [?PH_USERNAME | Words], Acc) ->
-    feed_var(ClientInfo, Words, [?PH_USERNAME | Acc]);
-feed_var(ClientInfo = #{username := Username}, [?PH_USERNAME | Words], Acc) ->
-    feed_var(ClientInfo, Words, [Username | Acc]);
-feed_var(ClientInfo, [W | Words], Acc) ->
-    feed_var(ClientInfo, Words, [W | Acc]).

+ 32 - 2
apps/emqx_authz/test/emqx_authz_rule_SUITE.erl

@@ -35,6 +35,7 @@
         ]},
         publish, [?PH_S_USERNAME, ?PH_S_CLIENTID]}
 ).
+-define(SOURCE6, {allow, {username, "test"}, publish, ["t/foo${username}boo"]}).
 
 all() ->
     emqx_common_test_helpers:all(?MODULE).
@@ -80,7 +81,7 @@ t_compile(_) ->
                 {{127, 0, 0, 1}, {127, 0, 0, 1}, 32},
                 {{192, 168, 1, 0}, {192, 168, 1, 255}, 24}
             ]},
-            subscribe, [{pattern, [?PH_CLIENTID]}]},
+            subscribe, [{pattern, [{var, {var, <<"clientid">>}}]}]},
         emqx_authz_rule:compile(?SOURCE3)
     ),
 
@@ -97,9 +98,18 @@ t_compile(_) ->
                 {username, {re_pattern, _, _, _, _}},
                 {clientid, {re_pattern, _, _, _, _}}
             ]},
-            publish, [{pattern, [?PH_USERNAME]}, {pattern, [?PH_CLIENTID]}]},
+            publish, [
+                {pattern, [{var, {var, <<"username">>}}]}, {pattern, [{var, {var, <<"clientid">>}}]}
+            ]},
         emqx_authz_rule:compile(?SOURCE5)
     ),
+
+    ?assertEqual(
+        {allow, {username, {eq, <<"test">>}}, publish, [
+            {pattern, [{str, <<"t/foo">>}, {var, {var, <<"username">>}}, {str, <<"boo">>}]}
+        ]},
+        emqx_authz_rule:compile(?SOURCE6)
+    ),
     ok.
 
 t_match(_) ->
@@ -307,4 +317,24 @@ t_match(_) ->
             emqx_authz_rule:compile(?SOURCE5)
         )
     ),
+
+    ?assertEqual(
+        nomatch,
+        emqx_authz_rule:match(
+            ClientInfo1,
+            publish,
+            <<"t/foo${username}boo">>,
+            emqx_authz_rule:compile(?SOURCE6)
+        )
+    ),
+
+    ?assertEqual(
+        {matched, allow},
+        emqx_authz_rule:match(
+            ClientInfo4,
+            publish,
+            <<"t/footestboo">>,
+            emqx_authz_rule:compile(?SOURCE6)
+        )
+    ),
     ok.