From 1148451a46054d50a646c435b2b9131636ee5ed9 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Mon, 6 Apr 2015 10:15:54 +0800 Subject: [PATCH] sync with air --- ...td_acl_rule.erl => emqttd_access_rule.erl} | 16 +- apps/emqttd/src/emqttd_acl.erl | 153 +++++++++--------- apps/emqttd/src/emqttd_acl_internal.erl | 16 +- 3 files changed, 98 insertions(+), 87 deletions(-) rename apps/emqttd/src/{emqttd_acl_rule.erl => emqttd_access_rule.erl} (96%) diff --git a/apps/emqttd/src/emqttd_acl_rule.erl b/apps/emqttd/src/emqttd_access_rule.erl similarity index 96% rename from apps/emqttd/src/emqttd_acl_rule.erl rename to apps/emqttd/src/emqttd_access_rule.erl index bbc119b2b..1c6dfcdb6 100644 --- a/apps/emqttd/src/emqttd_acl_rule.erl +++ b/apps/emqttd/src/emqttd_access_rule.erl @@ -24,23 +24,23 @@ %%% %%% @end %%%----------------------------------------------------------------------------- --module(emqttd_acl_rule). +-module(emqttd_access_rule). -include("emqttd.hrl"). --type pubsub() :: subscribe | publish | pubsub. - -type who() :: all | binary() | {ipaddr, esockd_access:cidr()} | {client, binary()} | {user, binary()}. --type rule() :: {allow, all} | - {allow, who(), pubsub(), list(binary())} | - {deny, all} | - {deny, who(), pubsub(), list(binary())}. +-type access() :: subscribe | publish | pubsub. --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]). diff --git a/apps/emqttd/src/emqttd_acl.erl b/apps/emqttd/src/emqttd_acl.erl index 1b57ee239..a0c142351 100644 --- a/apps/emqttd/src/emqttd_acl.erl +++ b/apps/emqttd/src/emqttd_acl.erl @@ -22,11 +22,6 @@ %%% @doc %%% emqttd ACL. %%% -%%% Two types of authorization: -%%% -%%% subscribe topic -%%% publish to topic -%%% %%% @end %%%----------------------------------------------------------------------------- -module(emqttd_acl). @@ -35,13 +30,6 @@ -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). -define(SERVER, ?MODULE). @@ -49,12 +37,35 @@ %% API Function Exports -export([start_link/1, check/3, reload/0, register_mod/1, unregister_mod/1]). -%% ACL Callback --export([check_acl/3, reload_acl/0]). +%% gen_server callbacks +-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 @@ -71,91 +82,85 @@ start_link(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(), Topic :: binary(). -check(PubSub, User, Topic) -> - case ets:lookup(?ACL_TAB, acl_mods) of - [] -> {error, "No ACL mod!"}; +check(PubSub, User, Topic) when PubSub =:= publish orelse PubSub =:= subscribe -> + case ets:lookup(?ACL_TABLE, acl_mods) of + [] -> {error, "No ACL mods!"}; [{_, Mods}] -> check(PubSub, User, Topic, Mods) end. + check(_PubSub, _User, _Topic, []) -> {error, "All ACL mods ignored!"}; + check(PubSub, User, Topic, [Mod|Mods]) -> case Mod:check_acl(PubSub, User, Topic) of - {ok, AllowDeny} -> AllowDeny; + {ok, AllowDeny} -> {ok, AllowDeny}; ignore -> check(PubSub, User, Topic, Mods) end. -%%TODO: +%%------------------------------------------------------------------------------ +%% @doc +%% Reload ACL. +%% +%% @end +%%------------------------------------------------------------------------------ reload() -> - case ets:lookup(?ACL_TAB, acl_mods) of + case ets:lookup(?ACL_TABLE, acl_mods) of [] -> {error, "No ACL mod!"}; [{_, Mods}] -> [M:reload() || M <- Mods] end. +%%------------------------------------------------------------------------------ +%% @doc +%% Register ACL Module. +%% +%% @end +%%------------------------------------------------------------------------------ +-spec register_mod(Mod :: atom()) -> ok | {error, any()}. 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) -> - gen_server:call(?MODULE, {unregister_mod, Mod}). + gen_server:call(?SERVER, {unregister_mod, Mod}). - -%% ------------------------------------------------------------------ -%% gen_server Function Definitions -%% ------------------------------------------------------------------ -init([AclOpts]) -> - ets:new(?ACL_TAB, [set, protected, named_table]), - 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; +%%%============================================================================= +%%% gen_server callbacks. +%%%============================================================================= +init([_AclOpts]) -> + ets:new(?ACL_TABLE, [set, protected, named_table]), + {ok, state}. handle_call({register_mod, Mod}, _From, State) -> - [{_, Mods}] = ets:lookup(?ACL_TAB, acl_mods), + Mods = acl_mods(), case lists:member(Mod, Mods) of - true -> + true -> {reply, {error, registered}, State}; - false -> - ets:insert(?ACL_TAB, {acl_mods, [Mod|Mods]}), + false -> + ets:insert(?ACL_TABLE, {acl_mods, [Mod | Mods]}), {reply, ok, State} end; handle_call({unregister_mod, Mod}, _From, State) -> - [{_, Mods}] = ets:lookup(?ACL_TAB, acl_mods), + Mods = acl_mods(), case lists:member(Mod, Mods) of true -> - ets:insert(?ACL_TAB, lists:delete(Mod, Mods)), + ets:insert(?ACL_TABLE, lists:delete(Mod, Mods)), {reply, ok, State}; false -> {reply, {error, not_found}, State} @@ -180,5 +185,9 @@ code_change(_OldVsn, State, _Extra) -> %%%============================================================================= %%% Internal functions %%%============================================================================= - +acl_mods() -> + case ets:lookup(?ACL_TABLE, acl_mods) of + [] -> []; + [{_, Mods}] -> Mods + end. diff --git a/apps/emqttd/src/emqttd_acl_internal.erl b/apps/emqttd/src/emqttd_acl_internal.erl index 801663ec3..722f61a6e 100644 --- a/apps/emqttd/src/emqttd_acl_internal.erl +++ b/apps/emqttd/src/emqttd_acl_internal.erl @@ -43,6 +43,10 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +-define(ACL_RULE_TABLE, mqtt_acl_rule). + +-record(state, {acl_file, raw_rules = []}). + %%%============================================================================= %%% API %%%============================================================================= @@ -59,7 +63,7 @@ start_link(AclOpts) -> gen_server:start_link({local, ?SERVER}, ?MODULE, [AclOpts], []). -spec check_acl(PubSub, User, Topic) -> {ok, allow} | {ok, deny} | ignore | {error, any()} when - PubSub :: pubsub(), + PubSub :: publish | subscribe, User :: mqtt_user(), Topic :: binary(). check_acl(PubSub, User, Topic) -> @@ -74,7 +78,7 @@ reload_acl() -> gen_server:call(?SERVER, reload). lookup(PubSub) -> - case ets:lookup(?ACL_TAB, PubSub) of + case ets:lookup(emqttd_acl:table(), PubSub) of [] -> []; [{PubSub, Rules}] -> Rules end. @@ -83,19 +87,16 @@ match(_User, _Topic, []) -> nomatch; 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); {matched, AllowDeny} -> {matched, AllowDeny} end. - - %% ------------------------------------------------------------------ %% gen_server Function Definitions %% ------------------------------------------------------------------ init([AclOpts]) -> - ets:new(?ACL_TAB, [set, protected, named_table]), - ets:insert(?ACL_TAB, {acl_mods, [?MODULE]}), + ets:insert(emqttd_acl:table(), {acl_mods, [?MODULE]}), AclFile = proplists:get_value(file, AclOpts), load_rules(#state{acl_file = AclFile}). @@ -161,3 +162,4 @@ handle_info(_Info, State) -> terminate(_Reason, _State) -> ok. +