Feng 10 лет назад
Родитель
Сommit
8491467bbb
3 измененных файлов с 54 добавлено и 25 удалено
  1. 17 17
      rel/files/acl.config
  2. 20 4
      src/emqttd_access_rule.erl
  3. 17 4
      test/emqttd_access_rule_tests.erl

+ 17 - 17
rel/files/acl.config

@@ -1,21 +1,21 @@
 %%%-----------------------------------------------------------------------------
-%% 
-%% [ACL](https://github.com/emqtt/emqttd/wiki/ACL)
-%% 
-%% -type who() :: all | binary() |
-%%               {ipaddr, esockd_access:cidr()} |
-%%               {client, binary()} |
-%%               {user, binary()}.
-%%
-%% -type access() :: subscribe | publish | pubsub.
-%%
-%% -type topic() :: binary().
-%%
-%% -type rule() :: {allow, all} |
-%%                {allow, who(), access(), list(topic())} |
-%%                {deny, all} |
-%%                {deny, who(), access(), list(topic())}.
-%%
+%%%
+%%% [ACL](https://github.com/emqtt/emqttd/wiki/ACL)
+%%%
+%%% -type who() :: all | binary() |
+%%%                {ipaddr, esockd_access:cidr()} |
+%%%                {client, binary()} |
+%%%                {user, binary()}.
+%%%
+%%% -type access() :: subscribe | publish | pubsub.
+%%%
+%%% -type topic() :: binary().
+%%%
+%%% -type rule() :: {allow, all} |
+%%%                 {allow, who(), access(), list(topic())} |
+%%%                 {deny, all} |
+%%%                 {deny, who(), access(), list(topic())}.
+%%%
 %%%-----------------------------------------------------------------------------
 
 {allow, {user, "dashboard"}, subscribe, ["$SYS/#"]}.

+ 20 - 4
src/emqttd_access_rule.erl

@@ -24,7 +24,6 @@
 %%%
 %%% @end
 %%%-----------------------------------------------------------------------------
-
 -module(emqttd_access_rule).
 
 -author("Feng Lee <feng@emqtt.io>").
@@ -49,17 +48,22 @@
 
 -export([compile/1, match/3]).
 
+-define(ALLOW_DENY(A), ((A =:= allow) orelse (A =:= deny))).
+
 %%------------------------------------------------------------------------------
 %% @doc Compile access rule
 %% @end
 %%------------------------------------------------------------------------------
-compile({A, all}) when (A =:= allow) orelse (A =:= deny) ->
+compile({A, all}) when ?ALLOW_DENY(A) ->
     {A, all};
 
-compile({A, Who, Access, TopicFilters}) when (A =:= allow) orelse (A =:= deny) ->
+compile({A, Who, Access, Topic}) when ?ALLOW_DENY(A) andalso is_binary(Topic) ->
+    {A, compile(who, Who), Access, [compile(topic, Topic)]};
+
+compile({A, Who, Access, TopicFilters}) when ?ALLOW_DENY(A) ->
     {A, compile(who, Who), Access, [compile(topic, Topic) || Topic <- TopicFilters]}.
 
-compile(who, all) -> 
+compile(who, all) ->
     all;
 compile(who, {ipaddr, CIDR}) ->
     {Start, End} = esockd_access:range(CIDR),
@@ -72,6 +76,10 @@ compile(who, {user, all}) ->
     {user, all};
 compile(who, {user, Username}) ->
     {user, bin(Username)};
+compile(who, {'and', Conds}) when is_list(Conds) ->
+    {'and', [compile(who, Cond) || Cond <- Conds]};
+compile(who, {'or', Conds}) when is_list(Conds) ->
+    {'or', [compile(who, Cond) || Cond <- Conds]};
 
 compile(topic, {eq, Topic}) ->
     {eq, emqttd_topic:words(bin(Topic))};
@@ -120,6 +128,14 @@ match_who(#mqtt_client{peername = undefined}, {ipaddr, _Tup}) ->
 match_who(#mqtt_client{peername = {IP, _}}, {ipaddr, {_CDIR, Start, End}}) ->
     I = esockd_access:atoi(IP),
     I >= Start andalso I =< End;
+match_who(Client, {'and', Conds}) when is_list(Conds) ->
+    lists:foldl(fun(Who, Allow) ->
+                  match_who(Client, Who) andalso Allow
+                end, true, Conds);
+match_who(Client, {'or', Conds}) when is_list(Conds) ->
+    lists:foldl(fun(Who, Allow) ->
+                  match_who(Client, Who) orelse Allow
+                end, false, Conds);
 match_who(_Client, _Who) ->
     false.
 

+ 17 - 4
test/emqttd_access_rule_tests.erl

@@ -35,6 +35,14 @@
 -include_lib("eunit/include/eunit.hrl").
 
 compile_test() ->
+
+    ?assertMatch({allow, {'and', [{ipaddr, {"127.0.0.1", _I, _I}},
+                                      {user, <<"user">>}]}, subscribe, [ [<<"$SYS">>, '#'], ['#'] ]},
+                 compile({allow, {'and', [{ipaddr, "127.0.0.1"}, {user, <<"user">>}]}, subscribe, ["$SYS/#", "#"]})),
+    ?assertMatch({allow, {'or', [{ipaddr, {"127.0.0.1", _I, _I}},
+                                 {user, <<"user">>}]}, subscribe, [ [<<"$SYS">>, '#'], ['#'] ]},
+                 compile({allow, {'or', [{ipaddr, "127.0.0.1"}, {user, <<"user">>}]}, subscribe, ["$SYS/#", "#"]})),
+
     ?assertMatch({allow, {ipaddr, {"127.0.0.1", _I, _I}}, subscribe, [ [<<"$SYS">>, '#'], ['#'] ]},
                  compile({allow, {ipaddr, "127.0.0.1"}, subscribe, ["$SYS/#", "#"]})),
     ?assertMatch({allow, {user, <<"testuser">>}, subscribe, [ [<<"a">>, <<"b">>, <<"c">>], [<<"d">>, <<"e">>, <<"f">>, '#'] ]},
@@ -69,10 +77,15 @@ match_test() ->
     ?assertMatch({matched, allow}, match(User, <<"clients/testClient">>,
                                                        compile({allow, all, pubsub, ["clients/$c"]}))),
     ?assertMatch({matched, allow}, match(#mqtt_client{username = <<"user2">>}, <<"users/user2/abc/def">>,
-                                                                  compile({allow, all, subscribe, ["users/$u/#"]}))),
-    ?assertMatch({matched, deny}, 
-                 match(User, <<"d/e/f">>,
-                                     compile({deny, all, subscribe, ["$SYS/#", "#"]}))).
+                                         compile({allow, all, subscribe, ["users/$u/#"]}))),
+    ?assertMatch({matched, deny}, match(User, <<"d/e/f">>,
+                                        compile({deny, all, subscribe, ["$SYS/#", "#"]}))),
+    Rule = compile({allow, {'and', [{ipaddr, "127.0.0.1"}, {user, <<"WrongUser">>}]}, publish, <<"Topic">>}),
+    ?assertMatch(nomatch, match(User, <<"Topic">>, Rule)),
+    AndRule = compile({allow, {'and', [{ipaddr, "127.0.0.1"}, {user, <<"TestUser">>}]}, publish, <<"Topic">>}),
+    ?assertMatch({matched, allow}, match(User, <<"Topic">>, AndRule)),
+    OrRule = compile({allow, {'or', [{ipaddr, "127.0.0.1"}, {user, <<"WrongUser">>}]}, publish, ["Topic"]}),
+    ?assertMatch({matched, allow}, match(User, <<"Topic">>, OrRule)).
 
 -endif.