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)
%%
%% -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/#"]}.

View File

@ -24,7 +24,6 @@
%%%
%%% @end
%%%-----------------------------------------------------------------------------
-module(emqttd_access_rule).
-author("Feng Lee <feng@emqtt.io>").
@ -49,14 +48,19 @@
-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) ->
@ -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.

View File

@ -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">>, '#'] ]},
@ -70,9 +78,14 @@ match_test() ->
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/#", "#"]}))).
?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.