support 'and', 'or'

This commit is contained in:
Feng 2015-11-04 21:42:17 +08:00
parent d09721301e
commit 8491467bbb
3 changed files with 54 additions and 25 deletions

View File

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

View File

@ -24,7 +24,6 @@
%%% %%%
%%% @end %%% @end
%%%----------------------------------------------------------------------------- %%%-----------------------------------------------------------------------------
-module(emqttd_access_rule). -module(emqttd_access_rule).
-author("Feng Lee <feng@emqtt.io>"). -author("Feng Lee <feng@emqtt.io>").
@ -49,14 +48,19 @@
-export([compile/1, match/3]). -export([compile/1, match/3]).
-define(ALLOW_DENY(A), ((A =:= allow) orelse (A =:= deny))).
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% @doc Compile access rule %% @doc Compile access rule
%% @end %% @end
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
compile({A, all}) when (A =:= allow) orelse (A =:= deny) -> compile({A, all}) when ?ALLOW_DENY(A) ->
{A, all}; {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]}. {A, compile(who, Who), Access, [compile(topic, Topic) || Topic <- TopicFilters]}.
compile(who, all) -> compile(who, all) ->
@ -72,6 +76,10 @@ compile(who, {user, all}) ->
{user, all}; {user, all};
compile(who, {user, Username}) -> compile(who, {user, Username}) ->
{user, bin(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}) -> compile(topic, {eq, Topic}) ->
{eq, emqttd_topic:words(bin(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}}) -> match_who(#mqtt_client{peername = {IP, _}}, {ipaddr, {_CDIR, Start, End}}) ->
I = esockd_access:atoi(IP), I = esockd_access:atoi(IP),
I >= Start andalso I =< End; 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) -> match_who(_Client, _Who) ->
false. false.

View File

@ -35,6 +35,14 @@
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
compile_test() -> 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">>, '#'], ['#'] ]}, ?assertMatch({allow, {ipaddr, {"127.0.0.1", _I, _I}}, subscribe, [ [<<"$SYS">>, '#'], ['#'] ]},
compile({allow, {ipaddr, "127.0.0.1"}, subscribe, ["$SYS/#", "#"]})), compile({allow, {ipaddr, "127.0.0.1"}, subscribe, ["$SYS/#", "#"]})),
?assertMatch({allow, {user, <<"testuser">>}, subscribe, [ [<<"a">>, <<"b">>, <<"c">>], [<<"d">>, <<"e">>, <<"f">>, '#'] ]}, ?assertMatch({allow, {user, <<"testuser">>}, subscribe, [ [<<"a">>, <<"b">>, <<"c">>], [<<"d">>, <<"e">>, <<"f">>, '#'] ]},
@ -70,9 +78,14 @@ match_test() ->
compile({allow, all, pubsub, ["clients/$c"]}))), compile({allow, all, pubsub, ["clients/$c"]}))),
?assertMatch({matched, allow}, match(#mqtt_client{username = <<"user2">>}, <<"users/user2/abc/def">>, ?assertMatch({matched, allow}, match(#mqtt_client{username = <<"user2">>}, <<"users/user2/abc/def">>,
compile({allow, all, subscribe, ["users/$u/#"]}))), compile({allow, all, subscribe, ["users/$u/#"]}))),
?assertMatch({matched, deny}, ?assertMatch({matched, deny}, match(User, <<"d/e/f">>,
match(User, <<"d/e/f">>, compile({deny, all, subscribe, ["$SYS/#", "#"]}))),
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. -endif.