diff --git a/rel/files/acl.config b/rel/files/acl.config index 3359b2b04..9b1d512a6 100644 --- a/rel/files/acl.config +++ b/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/#"]}. diff --git a/src/emqttd_access_rule.erl b/src/emqttd_access_rule.erl index fab9461b9..30ed8f87b 100644 --- a/src/emqttd_access_rule.erl +++ b/src/emqttd_access_rule.erl @@ -24,7 +24,6 @@ %%% %%% @end %%%----------------------------------------------------------------------------- - -module(emqttd_access_rule). -author("Feng Lee "). @@ -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. diff --git a/test/emqttd_access_rule_tests.erl b/test/emqttd_access_rule_tests.erl index 142beeaeb..f46f23ce4 100644 --- a/test/emqttd_access_rule_tests.erl +++ b/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.