-
Notifications
You must be signed in to change notification settings - Fork 428
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
23 changed files
with
242 additions
and
297 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,76 +26,42 @@ | |
-module(acl). | ||
-author('[email protected]'). | ||
|
||
-export([match_rule/3, | ||
match_rule/4, | ||
match_rule_for_host_type/4, | ||
match_rule_for_host_type/5]). | ||
|
||
-export([prepare_spec/1]). | ||
|
||
-ignore_xref([add/3, delete/3, match_rule/4]). | ||
-export([match_rule/4, match_rule/5]). | ||
|
||
-include("jlib.hrl"). | ||
-include("mongoose.hrl"). | ||
|
||
-export_type([rule/0, domain_or_global/0, host_type_or_global/0]). | ||
-export_type([rule/0]). | ||
|
||
-type rule() :: 'all' | 'none' | atom(). | ||
-type domain_or_global() :: jid:lserver() | global. | ||
-type host_type_or_global() :: mongooseim:host_type() | global. | ||
-type aclspec() :: #{match => all | none} | #{jid_part() => acl_pattern()}. | ||
-type jid_part() :: user | server | resource. | ||
-type acl_pattern() :: {value | regexp | glob, binary()}. | ||
-type rule() :: atom(). | ||
-type acl_spec() :: #{match := all | none | valid_domain} | | ||
#{acl_spec_key() => binary()}. | ||
-type acl_spec_key() :: user | user_regexp | user_glob | ||
| server | server_regexp | server_glob | ||
| resource | resource_regexp | resource_glob. | ||
|
||
-type acl_result() :: allow | deny | term(). | ||
|
||
%% legacy API, use match_rule_for_host_type instead | ||
-spec match_rule(Domain :: domain_or_global(), | ||
Rule :: rule(), | ||
JID :: jid:jid()) -> acl_result(). | ||
match_rule(Domain, Rule, JID) -> | ||
match_rule(Domain, Rule, JID, deny). | ||
|
||
-spec match_rule_for_host_type(HostType :: host_type_or_global(), | ||
Domain :: domain_or_global(), | ||
Rule :: rule(), | ||
JID :: jid:jid()) -> acl_result(). | ||
match_rule_for_host_type(HostType, Domain, Rule, JID) -> | ||
match_rule_for_host_type(HostType, Domain, Rule, JID, deny). | ||
|
||
%% legacy API, use match_rule_for_host_type instead | ||
-spec match_rule(Domain :: domain_or_global(), | ||
Rule :: rule(), | ||
JID :: jid:jid(), | ||
Default :: acl_result()) -> acl_result(). | ||
match_rule(Domain, Rule, JID, Default) -> | ||
%% We don't want to cast Domain to HostType here. | ||
%% Developers should start using match_rule_for_host_type explicitly. | ||
match_rule_for_host_type(Domain, Domain, Rule, JID, Default). | ||
|
||
%% HostType determines which rules and ACLs are checked: | ||
%% - 'global' - only global ones | ||
%% - a specific host type - both global and per-host-type ones | ||
%% Domain is only used for validating the user's domain name: | ||
%% - 'global' - any domain hosted by the server is accepted | ||
%% - a specific domain name - only the provided name is accepted | ||
-spec match_rule_for_host_type(HostType :: host_type_or_global(), | ||
Domain :: domain_or_global(), | ||
Rule :: rule(), | ||
JID :: jid:jid(), | ||
Default :: acl_result()) -> acl_result(). | ||
match_rule_for_host_type(_HostType, _, all, _, _Default) -> | ||
-spec match_rule(mongooseim:host_type_or_global(), jid:lserver(), rule(), jid:jid()) -> | ||
acl_result(). | ||
match_rule(_HostType, _Domain, all, _JID) -> | ||
allow; | ||
match_rule_for_host_type(_HostType, _, none, _, _Default) -> | ||
match_rule(_HostType, _Domain, none, _JID) -> | ||
deny; | ||
match_rule_for_host_type(global, Domain, Rule, JID, Default) -> | ||
match_rule(HostType, Domain, Rule, JID) -> | ||
match_rule(HostType, Domain, Rule, JID, deny). | ||
|
||
-spec match_rule(mongooseim:host_type_or_global(), jid:lserver(), rule(), jid:jid(), | ||
Default :: acl_result()) -> | ||
acl_result(). | ||
match_rule(global, Domain, Rule, JID, Default) -> | ||
case mongoose_config:lookup_opt({access, Rule, global}) of | ||
{error, not_found} -> | ||
Default; | ||
{ok, GACLs} -> | ||
match_acls(GACLs, JID, global, Domain) | ||
{ok, ACLs} -> | ||
match_acls(ACLs, JID, global, Domain) | ||
end; | ||
match_rule_for_host_type(HostType, Domain, Rule, JID, Default) -> | ||
match_rule(HostType, Domain, Rule, JID, Default) -> | ||
case {mongoose_config:lookup_opt({access, Rule, global}), | ||
mongoose_config:lookup_opt({access, Rule, HostType})} of | ||
{{error, not_found}, {error, not_found}} -> | ||
|
@@ -117,10 +83,8 @@ merge_acls(Global, HostLocal) -> | |
Global ++ HostLocal | ||
end. | ||
|
||
-spec match_acls(ACLs :: [{any(), rule()}], | ||
JID :: jid:jid(), | ||
HostType :: host_type_or_global(), | ||
Domain :: domain_or_global()) -> deny | term(). | ||
-spec match_acls(ACLs :: [{any(), rule()}], jid:jid(), mongooseim:host_type(), jid:lserver()) -> | ||
acl_result(). | ||
match_acls([], _, _HostType, _Domain) -> | ||
deny; | ||
match_acls([{Value, Rule} | ACLs], JID, HostType, Domain) -> | ||
|
@@ -131,10 +95,7 @@ match_acls([{Value, Rule} | ACLs], JID, HostType, Domain) -> | |
match_acls(ACLs, JID, HostType, Domain) | ||
end. | ||
|
||
-spec match_acl(Rule :: rule(), | ||
JID :: jid:jid(), | ||
HostType :: host_type_or_global(), | ||
Domain :: domain_or_global()) -> boolean(). | ||
-spec match_acl(rule(), jid:jid(), mongooseim:host_type_or_global(), jid:lserver()) -> boolean(). | ||
match_acl(all, _JID, _HostType, _Domain) -> | ||
true; | ||
match_acl(none, _JID, _HostType, _Domain) -> | ||
|
@@ -144,71 +105,36 @@ match_acl(Rule, JID, HostType, Domain) -> | |
global -> get_acl_specs(Rule, global); | ||
_ -> get_acl_specs(Rule, HostType) ++ get_acl_specs(Rule, global) | ||
end, | ||
Pred = fun(ACLSpec) -> match(ACLSpec, JID) end, | ||
is_server_valid(Domain, JID#jid.lserver) andalso lists:any(Pred, AllSpecs). | ||
Pred = fun(ACLSpec) -> match(ACLSpec, Domain, JID) end, | ||
lists:any(Pred, AllSpecs). | ||
|
||
-spec get_acl_specs(rule(), host_type_or_global()) -> [aclspec()]. | ||
-spec get_acl_specs(rule(), mongooseim:host_type()) -> [acl_spec()]. | ||
get_acl_specs(Rule, HostType) -> | ||
mongoose_config:get_opt({acl, Rule, HostType}, []). | ||
|
||
-spec is_server_valid(domain_or_global(), jid:lserver()) -> boolean(). | ||
is_server_valid(Domain, Domain) -> | ||
true; | ||
is_server_valid(global, JIDServer) -> | ||
case mongoose_domain_api:get_domain_host_type(JIDServer) of | ||
{ok, _HostType} -> | ||
true; | ||
_ -> | ||
false | ||
end; | ||
is_server_valid(_Domain, _JIDServer) -> | ||
false. | ||
|
||
-spec match(aclspec(), jid:jid()) -> boolean(). | ||
match(#{match := Match}, _JID) -> | ||
Match =:= all; | ||
match(M, JID) -> | ||
lists:all(fun({K, V}) -> check(K, V, JID) end, maps:to_list(M)). | ||
|
||
-spec check(jid_part(), acl_pattern(), jid:jid()) -> boolean(). | ||
check(user, Spec, #jid{luser = LUser}) -> check(Spec, LUser); | ||
check(server, Spec, #jid{lserver = LServer}) -> check(Spec, LServer); | ||
check(resource, Spec, #jid{lresource = LResource}) -> check(Spec, LResource). | ||
|
||
-spec check(acl_pattern(), binary()) -> boolean(). | ||
check({regexp, Regexp}, Value) -> is_regexp_match(Value, Regexp); | ||
check({glob, Glob}, Value) -> is_glob_match(Value, Glob); | ||
check({value, ExpValue}, Value) -> ExpValue =:= Value. | ||
|
||
-spec prepare_spec(map()) -> aclspec(). | ||
prepare_spec(M) -> | ||
KVs = [prepare_check(K, V) || {K, V} <- maps:to_list(M)], | ||
case conflicts(proplists:get_keys(KVs)) of | ||
[] -> maps:from_list(KVs); | ||
Conflicts -> | ||
error(#{what => wrong_acl_expression, | ||
text => <<"Wrong ACL expression in the configuration file">>, | ||
wrong_spec => M, | ||
conflicts => Conflicts}) | ||
end. | ||
|
||
-spec conflicts([atom()]) -> [atom()]. | ||
conflicts(Keys) -> | ||
case lists:member(match, Keys) of | ||
true -> Keys -- [match]; | ||
false -> Keys -- [user, server, resource] | ||
end. | ||
|
||
prepare_check(user, User) -> {user, {value, User}}; | ||
prepare_check(server, Server) -> {server, {value, Server}}; | ||
prepare_check(resource, Resource) -> {resource, {value, Resource}}; | ||
prepare_check(user_regexp, Regexp) -> {user, {regexp, Regexp}}; | ||
prepare_check(server_regexp, Regexp) -> {server, {regexp, Regexp}}; | ||
prepare_check(resource_regexp, Regexp) -> {resource, {regexp, Regexp}}; | ||
prepare_check(user_glob, Glob) -> {user, {glob, Glob}}; | ||
prepare_check(server_glob, Glob) -> {server, {glob, Glob}}; | ||
prepare_check(resource_glob, Glob) -> {resource, {glob, Glob}}; | ||
prepare_check(match, Value) -> {match, Value}. | ||
%% @doc Check if all conditions from ACLSpec are satisfied by JID | ||
-spec match(acl_spec(), jid:lserver(), jid:jid()) -> boolean(). | ||
match(ACLSpec, Domain, JID) -> | ||
match_step(maps:next(maps:iterator(ACLSpec)), Domain, JID). | ||
|
||
match_step({K, V, I}, Domain, JID) -> | ||
check(K, V, Domain, JID) andalso match_step(maps:next(I), Domain, JID); | ||
match_step(none, _Domain, _JID) -> | ||
true. | ||
|
||
-spec check(acl_spec_key(), binary(), jid:lserver(), jid:jid()) -> boolean(). | ||
check(match, all, _, _) -> true; | ||
check(match, none, _, _) -> false; | ||
check(match, current_domain, Domain, JID) -> JID#jid.lserver =:= Domain; | ||
check(user, User, _, JID) -> JID#jid.luser =:= User; | ||
check(user_regexp, Regexp, _, JID) -> is_regexp_match(JID#jid.luser, Regexp); | ||
check(user_glob, Glob, _, JID) -> is_glob_match(JID#jid.luser, Glob); | ||
check(server, Server, _, JID) -> JID#jid.lserver =:= Server; | ||
check(server_regexp, Regexp, _, JID) -> is_regexp_match(JID#jid.lserver, Regexp); | ||
check(server_glob, Glob, _, JID) -> is_glob_match(JID#jid.lserver, Glob); | ||
check(resource, Resource, _, JID) -> JID#jid.lresource =:= Resource; | ||
check(resource_regexp, Regexp, _, JID) -> is_regexp_match(JID#jid.lresource, Regexp); | ||
check(resource_glob, Glob, _, JID) -> is_glob_match(JID#jid.lresource, Glob). | ||
|
||
-spec is_regexp_match(binary(), RegExp :: iodata()) -> boolean(). | ||
is_regexp_match(String, RegExp) -> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.