support 'and', 'or'
This commit is contained in:
parent
d09721301e
commit
8491467bbb
|
@ -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/#"]}.
|
||||||
|
|
|
@ -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,17 +48,22 @@
|
||||||
|
|
||||||
-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) ->
|
||||||
all;
|
all;
|
||||||
compile(who, {ipaddr, CIDR}) ->
|
compile(who, {ipaddr, CIDR}) ->
|
||||||
{Start, End} = esockd_access:range(CIDR),
|
{Start, End} = esockd_access:range(CIDR),
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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">>, '#'] ]},
|
||||||
|
@ -69,10 +77,15 @@ match_test() ->
|
||||||
?assertMatch({matched, allow}, match(User, <<"clients/testClient">>,
|
?assertMatch({matched, allow}, match(User, <<"clients/testClient">>,
|
||||||
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.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue