diff --git a/apps/emqx_authz/include/emqx_authz.hrl b/apps/emqx_authz/include/emqx_authz.hrl index b11915d14..de5dca73e 100644 --- a/apps/emqx_authz/include/emqx_authz.hrl +++ b/apps/emqx_authz/include/emqx_authz.hrl @@ -1,22 +1,3 @@ --type(ipaddress() :: {ipaddr, esockd_cidr:cidr_string()} | - {ipaddrs, list(esockd_cidr:cidr_string())}). - --type(username() :: {username, binary()}). - --type(clientid() :: {clientid, binary()}). - --type(who() :: ipaddress() | username() | clientid() | - {'and', [ipaddress() | username() | clientid()]} | - {'or', [ipaddress() | username() | clientid()]} | - all). - --type(action() :: subscribe | publish | all). - --type(permission() :: allow | deny). - --type(rule() :: {permission(), who(), action(), list(emqx_types:topic())}). --type(rules() :: [rule()]). - -define(APP, emqx_authz). -define(ALLOW_DENY(A), ((A =:= allow) orelse (A =:= <<"allow">>) orelse @@ -36,6 +17,9 @@ -define(ACL_TABLE_USERNAME, 1). -define(ACL_TABLE_CLIENTID, 2). +-type(action() :: subscribe | publish | all). +-type(permission() :: allow | deny). + -record(emqx_acl, { who :: ?ACL_TABLE_ALL| {?ACL_TABLE_USERNAME, binary()} | {?ACL_TABLE_CLIENTID, binary()}, rules :: [ {permission(), action(), emqx_topic:topic()} ] diff --git a/apps/emqx_authz/src/emqx_authz.erl b/apps/emqx_authz/src/emqx_authz.erl index e2bf18a70..af16c3892 100644 --- a/apps/emqx_authz/src/emqx_authz.erl +++ b/apps/emqx_authz/src/emqx_authz.erl @@ -333,8 +333,6 @@ find_action_in_hooks() -> authz_module('built-in-database') -> emqx_authz_mnesia; -authz_module(file) -> - emqx_authz_rule; authz_module(Type) -> list_to_existing_atom("emqx_authz_" ++ atom_to_list(Type)). diff --git a/apps/emqx_authz/src/emqx_authz_file.erl b/apps/emqx_authz/src/emqx_authz_file.erl new file mode 100644 index 000000000..ba4f9c2b7 --- /dev/null +++ b/apps/emqx_authz/src/emqx_authz_file.erl @@ -0,0 +1,61 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_authz_file). + +-include("emqx_authz.hrl"). +-include_lib("emqx/include/logger.hrl"). + +-behaviour(emqx_authz). + +-ifdef(TEST). +-compile(export_all). +-compile(nowarn_export_all). +-endif. + +%% APIs +-export([ description/0 + , init/1 + , destroy/1 + , dry_run/1 + , authorize/4 + ]). + +description() -> + "AuthZ with static rules". + +init(#{path := Path} = Source) -> + Rules = case file:consult(Path) of + {ok, Terms} -> + [emqx_authz_rule:compile(Term) || Term <- Terms]; + {error, eacces} -> + ?SLOG(alert, #{msg => "insufficient_permissions_to_read_file", path => Path}), + error(eaccess); + {error, enoent} -> + ?SLOG(alert, #{msg => "file_does_not_exist", path => Path}), + error(enoent); + {error, Reason} -> + ?SLOG(alert, #{msg => "failed_to_read_file", path => Path, reason => Reason}), + error(Reason) + end, + Source#{annotations => #{rules => Rules}}. + +destroy(_Source) -> ok. + +dry_run(_Source) -> ok. + +authorize(Client, PubSub, Topic, #{annotations := #{rules := Rules}}) -> + emqx_authz_rule:matches(Client, PubSub, Topic, Rules). diff --git a/apps/emqx_authz/src/emqx_authz_rule.erl b/apps/emqx_authz/src/emqx_authz_rule.erl index ade364788..952d6b7a5 100644 --- a/apps/emqx_authz/src/emqx_authz_rule.erl +++ b/apps/emqx_authz/src/emqx_authz_rule.erl @@ -20,49 +20,33 @@ -include_lib("emqx/include/logger.hrl"). -include_lib("emqx/include/emqx_placeholder.hrl"). --behaviour(emqx_authz). - -ifdef(TEST). -compile(export_all). -compile(nowarn_export_all). -endif. %% APIs --export([ description/0 - , init/1 - , destroy/1 - , dry_run/1 - , authorize/4 +-export([ match/4 + , matches/4 + , compile/1 ]). +-type(ipaddress() :: {ipaddr, esockd_cidr:cidr_string()} | + {ipaddrs, list(esockd_cidr:cidr_string())}). + +-type(username() :: {username, binary()}). + +-type(clientid() :: {clientid, binary()}). + +-type(who() :: ipaddress() | username() | clientid() | + {'and', [ipaddress() | username() | clientid()]} | + {'or', [ipaddress() | username() | clientid()]} | + all). + +-type(rule() :: {permission(), who(), action(), list(emqx_types:topic())}). + -export_type([rule/0]). -description() -> - "AuthZ with static rules". - -init(#{path := Path} = Source) -> - Rules = case file:consult(Path) of - {ok, Terms} -> - [compile(Term) || Term <- Terms]; - {error, eacces} -> - ?SLOG(alert, #{msg => "insufficient_permissions_to_read_file", path => Path}), - error(eaccess); - {error, enoent} -> - ?SLOG(alert, #{msg => "file_does_not_exist", path => Path}), - error(enoent); - {error, Reason} -> - ?SLOG(alert, #{msg => "failed_to_read_file", path => Path, reason => Reason}), - error(Reason) - end, - Source#{annotations => #{rules => Rules}}. - -destroy(_Source) -> ok. - -dry_run(_Source) -> ok. - -authorize(Client, PubSub, Topic, #{annotations := #{rules := Rules}}) -> - matches(Client, PubSub, Topic, Rules). - compile({Permission, all}) when ?ALLOW_DENY(Permission) -> {Permission, all, all, [compile_topic(<<"#">>)]}; compile({Permission, Who, Action, TopicFilters})