sync with air
This commit is contained in:
parent
f049824e9f
commit
1148451a46
|
@ -24,23 +24,23 @@
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqttd_acl_rule).
|
-module(emqttd_access_rule).
|
||||||
|
|
||||||
-include("emqttd.hrl").
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
-type pubsub() :: subscribe | publish | pubsub.
|
|
||||||
|
|
||||||
-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 rule() :: {allow, all} |
|
-type access() :: subscribe | publish | pubsub.
|
||||||
{allow, who(), pubsub(), list(binary())} |
|
|
||||||
{deny, all} |
|
|
||||||
{deny, who(), pubsub(), list(binary())}.
|
|
||||||
|
|
||||||
-export_type([pubsub/0]).
|
-type topic() :: binary().
|
||||||
|
|
||||||
|
-type rule() :: {allow, all} |
|
||||||
|
{allow, who(), access(), list(topic())} |
|
||||||
|
{deny, all} |
|
||||||
|
{deny, who(), access(), list(topic())}.
|
||||||
|
|
||||||
-export([compile/1, match/3]).
|
-export([compile/1, match/3]).
|
||||||
|
|
|
@ -22,11 +22,6 @@
|
||||||
%%% @doc
|
%%% @doc
|
||||||
%%% emqttd ACL.
|
%%% emqttd ACL.
|
||||||
%%%
|
%%%
|
||||||
%%% Two types of authorization:
|
|
||||||
%%%
|
|
||||||
%%% subscribe topic
|
|
||||||
%%% publish to topic
|
|
||||||
%%%
|
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqttd_acl).
|
-module(emqttd_acl).
|
||||||
|
@ -35,13 +30,6 @@
|
||||||
|
|
||||||
-include("emqttd.hrl").
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
-callback check_acl(PubSub, User, Topic) -> {ok, allow | deny} | ignore | {error, any()} when
|
|
||||||
PubSub :: pubsub(),
|
|
||||||
User :: mqtt_user(),
|
|
||||||
Topic :: binary().
|
|
||||||
|
|
||||||
-callback reload_acl() -> ok | {error, any()}.
|
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
|
@ -49,12 +37,35 @@
|
||||||
%% API Function Exports
|
%% API Function Exports
|
||||||
-export([start_link/1, check/3, reload/0, register_mod/1, unregister_mod/1]).
|
-export([start_link/1, check/3, reload/0, register_mod/1, unregister_mod/1]).
|
||||||
|
|
||||||
%% ACL Callback
|
%% gen_server callbacks
|
||||||
-export([check_acl/3, reload_acl/0]).
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
|
terminate/2, code_change/3]).
|
||||||
|
|
||||||
-define(ACL_TAB, mqtt_acl).
|
-define(ACL_TABLE, mqtt_acl).
|
||||||
|
|
||||||
-record(state, {acl_file, raw_rules = []}).
|
%%%=============================================================================
|
||||||
|
%%% ACL behavihour
|
||||||
|
%%%=============================================================================
|
||||||
|
|
||||||
|
-ifdef(use_specs).
|
||||||
|
|
||||||
|
-callback check_acl(PubSub, User, Topic) -> {ok, allow | deny} | ignore | {error, any()} when
|
||||||
|
PubSub :: publish | subscribe,
|
||||||
|
User :: mqtt_user(),
|
||||||
|
Topic :: binary().
|
||||||
|
|
||||||
|
-callback reload_acl() -> ok | {error, any()}.
|
||||||
|
|
||||||
|
-else.
|
||||||
|
|
||||||
|
-export([behaviour_info/1]).
|
||||||
|
|
||||||
|
behaviour_info(callbacks) ->
|
||||||
|
[{check_acl, 3}, {reload_acl, 0}, {description, 0}];
|
||||||
|
behaviour_info(_Other) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
|
-endif.
|
||||||
|
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
%%% API
|
%%% API
|
||||||
|
@ -71,91 +82,85 @@
|
||||||
start_link(AclOpts) ->
|
start_link(AclOpts) ->
|
||||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [AclOpts], []).
|
gen_server:start_link({local, ?SERVER}, ?MODULE, [AclOpts], []).
|
||||||
|
|
||||||
-spec check(PubSub, User, Topic) -> allow | deny when
|
%%------------------------------------------------------------------------------
|
||||||
PubSub :: pubsub(),
|
%% @doc
|
||||||
|
%% Check ACL.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec check(PubSub, User, Topic) -> {ok, allow | deny} | {error, any()} when
|
||||||
|
PubSub :: publish | subscribe,
|
||||||
User :: mqtt_user(),
|
User :: mqtt_user(),
|
||||||
Topic :: binary().
|
Topic :: binary().
|
||||||
check(PubSub, User, Topic) ->
|
check(PubSub, User, Topic) when PubSub =:= publish orelse PubSub =:= subscribe ->
|
||||||
case ets:lookup(?ACL_TAB, acl_mods) of
|
case ets:lookup(?ACL_TABLE, acl_mods) of
|
||||||
[] -> {error, "No ACL mod!"};
|
[] -> {error, "No ACL mods!"};
|
||||||
[{_, Mods}] -> check(PubSub, User, Topic, Mods)
|
[{_, Mods}] -> check(PubSub, User, Topic, Mods)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
check(_PubSub, _User, _Topic, []) ->
|
check(_PubSub, _User, _Topic, []) ->
|
||||||
{error, "All ACL mods ignored!"};
|
{error, "All ACL mods ignored!"};
|
||||||
|
|
||||||
check(PubSub, User, Topic, [Mod|Mods]) ->
|
check(PubSub, User, Topic, [Mod|Mods]) ->
|
||||||
case Mod:check_acl(PubSub, User, Topic) of
|
case Mod:check_acl(PubSub, User, Topic) of
|
||||||
{ok, AllowDeny} -> AllowDeny;
|
{ok, AllowDeny} -> {ok, AllowDeny};
|
||||||
ignore -> check(PubSub, User, Topic, Mods)
|
ignore -> check(PubSub, User, Topic, Mods)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%TODO:
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Reload ACL.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
reload() ->
|
reload() ->
|
||||||
case ets:lookup(?ACL_TAB, acl_mods) of
|
case ets:lookup(?ACL_TABLE, acl_mods) of
|
||||||
[] -> {error, "No ACL mod!"};
|
[] -> {error, "No ACL mod!"};
|
||||||
[{_, Mods}] -> [M:reload() || M <- Mods]
|
[{_, Mods}] -> [M:reload() || M <- Mods]
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Register ACL Module.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec register_mod(Mod :: atom()) -> ok | {error, any()}.
|
||||||
register_mod(Mod) ->
|
register_mod(Mod) ->
|
||||||
gen_server:call(?MODULE, {register_mod, Mod}).
|
gen_server:call(?SERVER, {register_mod, Mod}).
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% @doc
|
||||||
|
%% Unregister ACL Module.
|
||||||
|
%%
|
||||||
|
%% @end
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec unregister_mod(Mod :: atom()) -> ok | {error, any()}.
|
||||||
unregister_mod(Mod) ->
|
unregister_mod(Mod) ->
|
||||||
gen_server:call(?MODULE, {unregister_mod, Mod}).
|
gen_server:call(?SERVER, {unregister_mod, Mod}).
|
||||||
|
|
||||||
|
%%%=============================================================================
|
||||||
%% ------------------------------------------------------------------
|
%%% gen_server callbacks.
|
||||||
%% gen_server Function Definitions
|
%%%=============================================================================
|
||||||
%% ------------------------------------------------------------------
|
init([_AclOpts]) ->
|
||||||
init([AclOpts]) ->
|
ets:new(?ACL_TABLE, [set, protected, named_table]),
|
||||||
ets:new(?ACL_TAB, [set, protected, named_table]),
|
{ok, state}.
|
||||||
ets:insert(?ACL_TAB, {acl_mods, [?MODULE]}),
|
|
||||||
AclFile = proplists:get_value(file, AclOpts),
|
|
||||||
load_rules(#state{acl_file = AclFile}).
|
|
||||||
|
|
||||||
load_rules(State = #state{acl_file = AclFile}) ->
|
|
||||||
{ok, Terms} = file:consult(AclFile),
|
|
||||||
Rules = [compile(Term) || Term <- Terms],
|
|
||||||
lists:foreach(fun(PubSub) ->
|
|
||||||
ets:insert(?ACL_TAB, {PubSub,
|
|
||||||
lists:filter(fun(Rule) -> filter(PubSub, Rule) end, Rules)})
|
|
||||||
end, [publish, subscribe]),
|
|
||||||
{ok, State#state{raw_rules = Terms}}.
|
|
||||||
|
|
||||||
filter(_PubSub, {allow, all}) ->
|
|
||||||
true;
|
|
||||||
filter(_PubSub, {deny, all}) ->
|
|
||||||
true;
|
|
||||||
filter(publish, {_AllowDeny, _Who, publish, _Topics}) ->
|
|
||||||
true;
|
|
||||||
filter(_PubSub, {_AllowDeny, _Who, pubsub, _Topics}) ->
|
|
||||||
true;
|
|
||||||
filter(subscribe, {_AllowDeny, _Who, subscribe, _Topics}) ->
|
|
||||||
true;
|
|
||||||
filter(_PubSub, {_AllowDeny, _Who, _, _Topics}) ->
|
|
||||||
false.
|
|
||||||
|
|
||||||
handle_call(reload, _From, State) ->
|
|
||||||
case catch load_rules(State) of
|
|
||||||
{ok, NewState} ->
|
|
||||||
{reply, ok, NewState};
|
|
||||||
{'EXIT', Error} ->
|
|
||||||
{reply, {error, Error}, State}
|
|
||||||
end;
|
|
||||||
|
|
||||||
handle_call({register_mod, Mod}, _From, State) ->
|
handle_call({register_mod, Mod}, _From, State) ->
|
||||||
[{_, Mods}] = ets:lookup(?ACL_TAB, acl_mods),
|
Mods = acl_mods(),
|
||||||
case lists:member(Mod, Mods) of
|
case lists:member(Mod, Mods) of
|
||||||
true ->
|
true ->
|
||||||
{reply, {error, registered}, State};
|
{reply, {error, registered}, State};
|
||||||
false ->
|
false ->
|
||||||
ets:insert(?ACL_TAB, {acl_mods, [Mod|Mods]}),
|
ets:insert(?ACL_TABLE, {acl_mods, [Mod | Mods]}),
|
||||||
{reply, ok, State}
|
{reply, ok, State}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
handle_call({unregister_mod, Mod}, _From, State) ->
|
handle_call({unregister_mod, Mod}, _From, State) ->
|
||||||
[{_, Mods}] = ets:lookup(?ACL_TAB, acl_mods),
|
Mods = acl_mods(),
|
||||||
case lists:member(Mod, Mods) of
|
case lists:member(Mod, Mods) of
|
||||||
true ->
|
true ->
|
||||||
ets:insert(?ACL_TAB, lists:delete(Mod, Mods)),
|
ets:insert(?ACL_TABLE, lists:delete(Mod, Mods)),
|
||||||
{reply, ok, State};
|
{reply, ok, State};
|
||||||
false ->
|
false ->
|
||||||
{reply, {error, not_found}, State}
|
{reply, {error, not_found}, State}
|
||||||
|
@ -180,5 +185,9 @@ code_change(_OldVsn, State, _Extra) ->
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
|
acl_mods() ->
|
||||||
|
case ets:lookup(?ACL_TABLE, acl_mods) of
|
||||||
|
[] -> [];
|
||||||
|
[{_, Mods}] -> Mods
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,10 @@
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
terminate/2, code_change/3]).
|
terminate/2, code_change/3]).
|
||||||
|
|
||||||
|
-define(ACL_RULE_TABLE, mqtt_acl_rule).
|
||||||
|
|
||||||
|
-record(state, {acl_file, raw_rules = []}).
|
||||||
|
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
%%% API
|
%%% API
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
|
@ -59,7 +63,7 @@ start_link(AclOpts) ->
|
||||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [AclOpts], []).
|
gen_server:start_link({local, ?SERVER}, ?MODULE, [AclOpts], []).
|
||||||
|
|
||||||
-spec check_acl(PubSub, User, Topic) -> {ok, allow} | {ok, deny} | ignore | {error, any()} when
|
-spec check_acl(PubSub, User, Topic) -> {ok, allow} | {ok, deny} | ignore | {error, any()} when
|
||||||
PubSub :: pubsub(),
|
PubSub :: publish | subscribe,
|
||||||
User :: mqtt_user(),
|
User :: mqtt_user(),
|
||||||
Topic :: binary().
|
Topic :: binary().
|
||||||
check_acl(PubSub, User, Topic) ->
|
check_acl(PubSub, User, Topic) ->
|
||||||
|
@ -74,7 +78,7 @@ reload_acl() ->
|
||||||
gen_server:call(?SERVER, reload).
|
gen_server:call(?SERVER, reload).
|
||||||
|
|
||||||
lookup(PubSub) ->
|
lookup(PubSub) ->
|
||||||
case ets:lookup(?ACL_TAB, PubSub) of
|
case ets:lookup(emqttd_acl:table(), PubSub) of
|
||||||
[] -> [];
|
[] -> [];
|
||||||
[{PubSub, Rules}] -> Rules
|
[{PubSub, Rules}] -> Rules
|
||||||
end.
|
end.
|
||||||
|
@ -83,19 +87,16 @@ match(_User, _Topic, []) ->
|
||||||
nomatch;
|
nomatch;
|
||||||
|
|
||||||
match(User, Topic, [Rule|Rules]) ->
|
match(User, Topic, [Rule|Rules]) ->
|
||||||
case match_rule(User, Topic, Rule) of
|
case emqttd_acl_rule:match(User, Topic, Rule) of
|
||||||
nomatch -> match(User, Topic, Rules);
|
nomatch -> match(User, Topic, Rules);
|
||||||
{matched, AllowDeny} -> {matched, AllowDeny}
|
{matched, AllowDeny} -> {matched, AllowDeny}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%% ------------------------------------------------------------------
|
%% ------------------------------------------------------------------
|
||||||
%% gen_server Function Definitions
|
%% gen_server Function Definitions
|
||||||
%% ------------------------------------------------------------------
|
%% ------------------------------------------------------------------
|
||||||
init([AclOpts]) ->
|
init([AclOpts]) ->
|
||||||
ets:new(?ACL_TAB, [set, protected, named_table]),
|
ets:insert(emqttd_acl:table(), {acl_mods, [?MODULE]}),
|
||||||
ets:insert(?ACL_TAB, {acl_mods, [?MODULE]}),
|
|
||||||
AclFile = proplists:get_value(file, AclOpts),
|
AclFile = proplists:get_value(file, AclOpts),
|
||||||
load_rules(#state{acl_file = AclFile}).
|
load_rules(#state{acl_file = AclFile}).
|
||||||
|
|
||||||
|
@ -161,3 +162,4 @@ handle_info(_Info, State) ->
|
||||||
|
|
||||||
terminate(_Reason, _State) ->
|
terminate(_Reason, _State) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue