From c034cbf6de312d3023e3f8d8a12133e2133085ff Mon Sep 17 00:00:00 2001 From: firest Date: Tue, 31 Jan 2023 17:48:43 +0800 Subject: [PATCH 1/2] feat(authz): allow the placeholder to be anywhere in the topic for authz rules --- apps/emqx_authz/src/emqx_authz_rule.erl | 35 ++++++------------- .../emqx_authz/test/emqx_authz_rule_SUITE.erl | 34 ++++++++++++++++-- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/apps/emqx_authz/src/emqx_authz_rule.erl b/apps/emqx_authz/src/emqx_authz_rule.erl index 4aa0983e6..306ca9433 100644 --- a/apps/emqx_authz/src/emqx_authz_rule.erl +++ b/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]). diff --git a/apps/emqx_authz/test/emqx_authz_rule_SUITE.erl b/apps/emqx_authz/test/emqx_authz_rule_SUITE.erl index 77f8617ee..76e5677ce 100644 --- a/apps/emqx_authz/test/emqx_authz_rule_SUITE.erl +++ b/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. From 9f4c36ecbc4e72f04937bd7c234bf7ffd351b22e Mon Sep 17 00:00:00 2001 From: firest Date: Tue, 31 Jan 2023 18:04:45 +0800 Subject: [PATCH 2/2] chore: bump version && update changes --- apps/emqx_authz/src/emqx_authz.app.src | 2 +- changes/v5.0.16/feat-9871.en.md | 3 +++ changes/v5.0.16/feat-9871.zh.md | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 changes/v5.0.16/feat-9871.en.md create mode 100644 changes/v5.0.16/feat-9871.zh.md diff --git a/apps/emqx_authz/src/emqx_authz.app.src b/apps/emqx_authz/src/emqx_authz.app.src index 6a4b721e9..3fea50147 100644 --- a/apps/emqx_authz/src/emqx_authz.app.src +++ b/apps/emqx_authz/src/emqx_authz.app.src @@ -1,7 +1,7 @@ %% -*- mode: erlang -*- {application, emqx_authz, [ {description, "An OTP application"}, - {vsn, "0.1.12"}, + {vsn, "0.1.13"}, {registered, []}, {mod, {emqx_authz_app, []}}, {applications, [ diff --git a/changes/v5.0.16/feat-9871.en.md b/changes/v5.0.16/feat-9871.en.md new file mode 100644 index 000000000..b907aa3f1 --- /dev/null +++ b/changes/v5.0.16/feat-9871.en.md @@ -0,0 +1,3 @@ +Allow the placeholder to be anywhere in the topic for `authz` rules. +e.g: +`{allow, {username, "who"}, publish, ["t/foo${username}boo/${clientid}xxx"]}.` diff --git a/changes/v5.0.16/feat-9871.zh.md b/changes/v5.0.16/feat-9871.zh.md new file mode 100644 index 000000000..ecd526a93 --- /dev/null +++ b/changes/v5.0.16/feat-9871.zh.md @@ -0,0 +1,3 @@ +允许占位符出现在 `authz` 规则中的主题里的任意位置。 +例如: +`{allow, {username, "who"}, publish, ["t/foo${username}boo/${clientid}xxx"]}.`