diff options
Diffstat (limited to 'src/acl.erl')
-rw-r--r-- | src/acl.erl | 367 |
1 files changed, 203 insertions, 164 deletions
diff --git a/src/acl.erl b/src/acl.erl index b5f64141b..77c55e79d 100644 --- a/src/acl.erl +++ b/src/acl.erl @@ -25,225 +25,264 @@ %%%---------------------------------------------------------------------- -module(acl). + -author('alexey@process-one.net'). --export([start/0, - to_record/3, - add/3, - add_list/3, - match_rule/3, - % for debugging only - match_acl/3]). +-export([start/0, to_record/3, add/3, add_list/3, + match_rule/3, match_acl/3]). -include("ejabberd.hrl"). +-include("jlib.hrl"). -record(acl, {aclname, aclspec}). +-type regexp() :: binary(). +-type glob() :: binary(). +-type aclname() :: {atom(), binary() | global}. +-type aclspec() :: all | none | + {user, binary()} | + {user, binary(), binary()} | + {server, binary()} | + {resource, binary()} | + {user_regexp, regexp()} | + {shared_group, binary()} | + {shared_group, binary(), binary()} | + {user_regexp, regexp(), binary()} | + {server_regexp, regexp()} | + {resource_regexp, regexp()} | + {node_regexp, regexp(), regexp()} | + {user_glob, glob()} | + {user_glob, glob(), binary()} | + {server_glob, glob()} | + {resource_glob, glob()} | + {node_glob, glob(), glob()}. + +-type acl() :: #acl{aclname :: aclname(), + aclspec :: aclspec()}. + +-export_type([acl/0]). + start() -> mnesia:create_table(acl, - [{disc_copies, [node()]}, - {type, bag}, + [{disc_copies, [node()]}, {type, bag}, {attributes, record_info(fields, acl)}]), mnesia:add_table_copy(acl, node(), ram_copies), + update_table(), ok. +-spec to_record(binary(), atom(), aclspec()) -> acl(). + to_record(Host, ACLName, ACLSpec) -> - #acl{aclname = {ACLName, Host}, aclspec = normalize_spec(ACLSpec)}. + #acl{aclname = {ACLName, Host}, + aclspec = normalize_spec(ACLSpec)}. + +-spec add(binary(), aclname(), aclspec()) -> {atomic, ok} | {aborted, any()}. add(Host, ACLName, ACLSpec) -> - F = fun() -> + F = fun () -> mnesia:write(#acl{aclname = {ACLName, Host}, aclspec = normalize_spec(ACLSpec)}) end, mnesia:transaction(F). +-spec add_list(binary(), [acl()], boolean()) -> false | ok. + add_list(Host, ACLs, Clear) -> - F = fun() -> - if - Clear -> - Ks = mnesia:select( - acl, [{{acl, {'$1', Host}, '$2'}, [], ['$1']}]), - lists:foreach(fun(K) -> - mnesia:delete({acl, {K, Host}}) - end, Ks); - true -> - ok + F = fun () -> + if Clear -> + Ks = mnesia:select(acl, + [{{acl, {'$1', Host}, '$2'}, [], + ['$1']}]), + lists:foreach(fun (K) -> mnesia:delete({acl, {K, Host}}) + end, + Ks); + true -> ok end, - lists:foreach(fun(ACL) -> + lists:foreach(fun (ACL) -> case ACL of - #acl{aclname = ACLName, - aclspec = ACLSpec} -> - mnesia:write( - #acl{aclname = {ACLName, Host}, - aclspec = normalize_spec(ACLSpec)}) + #acl{aclname = ACLName, + aclspec = ACLSpec} -> + mnesia:write(#acl{aclname = + {ACLName, + Host}, + aclspec = + normalize_spec(ACLSpec)}) end - end, ACLs) + end, + ACLs) end, case mnesia:transaction(F) of - {atomic, _} -> - ok; - _ -> - false + {atomic, _} -> ok; + _ -> false end. -normalize(A) -> - jlib:nodeprep(A). -normalize_spec({A, B}) -> - {A, normalize(B)}; +normalize(A) -> jlib:nodeprep(iolist_to_binary(A)). + +normalize_spec({A, B}) -> {A, normalize(B)}; normalize_spec({A, B, C}) -> {A, normalize(B), normalize(C)}; -normalize_spec(all) -> - all; -normalize_spec(none) -> - none. - +normalize_spec(all) -> all; +normalize_spec(none) -> none. +-spec match_rule(global | binary(), atom(), jid() | ljid()) -> any(). match_rule(global, Rule, JID) -> case Rule of - all -> allow; - none -> deny; - _ -> - case ejabberd_config:get_global_option({access, Rule, global}) of - undefined -> - deny; - GACLs -> - match_acls(GACLs, JID, global) - end + all -> allow; + none -> deny; + _ -> + case ejabberd_config:get_global_option( + {access, Rule, global}, fun(V) -> V end) + of + undefined -> deny; + GACLs -> match_acls(GACLs, JID, global) + end end; - match_rule(Host, Rule, JID) -> case Rule of - all -> allow; - none -> deny; - _ -> - case ejabberd_config:get_global_option({access, Rule, global}) of - undefined -> - case ejabberd_config:get_global_option({access, Rule, Host}) of - undefined -> - deny; - ACLs -> - match_acls(ACLs, JID, Host) - end; - GACLs -> - case ejabberd_config:get_global_option({access, Rule, Host}) of - undefined -> - match_acls(GACLs, JID, Host); - ACLs -> - case lists:reverse(GACLs) of - [{allow, all} | Rest] -> - match_acls( - lists:reverse(Rest) ++ ACLs ++ - [{allow, all}], - JID, Host); - _ -> - match_acls(GACLs ++ ACLs, JID, Host) - end - end - end + all -> allow; + none -> deny; + _ -> + case ejabberd_config:get_global_option( + {access, Rule, global}, fun(V) -> V end) + of + undefined -> + case ejabberd_config:get_global_option( + {access, Rule, Host}, fun(V) -> V end) + of + undefined -> deny; + ACLs -> match_acls(ACLs, JID, Host) + end; + GACLs -> + case ejabberd_config:get_global_option( + {access, Rule, Host}, fun(V) -> V end) + of + undefined -> match_acls(GACLs, JID, Host); + ACLs -> + case lists:reverse(GACLs) of + [{allow, all} | Rest] -> + match_acls(lists:reverse(Rest) ++ + ACLs ++ [{allow, all}], + JID, Host); + _ -> match_acls(GACLs ++ ACLs, JID, Host) + end + end + end end. -match_acls([], _, _Host) -> - deny; +match_acls([], _, _Host) -> deny; match_acls([{Access, ACL} | ACLs], JID, Host) -> case match_acl(ACL, JID, Host) of - true -> - Access; - _ -> - match_acls(ACLs, JID, Host) + true -> Access; + _ -> match_acls(ACLs, JID, Host) end. +-spec match_acl(atom(), jid() | ljid(), binary()) -> boolean(). + match_acl(ACL, JID, Host) -> case ACL of - all -> true; - none -> false; - _ -> - {User, Server, Resource} = jlib:jid_tolower(JID), - lists:any(fun(#acl{aclspec = Spec}) -> - case Spec of - all -> - true; - {user, U} -> - (U == User) - andalso - ((Host == Server) orelse - ((Host == global) andalso - lists:member(Server, ?MYHOSTS))); - {user, U, S} -> - (U == User) andalso (S == Server); - {server, S} -> - S == Server; - {resource, R} -> - R == Resource; - {user_regexp, UR} -> - ((Host == Server) orelse - ((Host == global) andalso - lists:member(Server, ?MYHOSTS))) - andalso is_regexp_match(User, UR); - {shared_group, G} -> - Mod = loaded_shared_roster_module(Host), - Mod:is_user_in_group({User, Server}, G, Host); - {shared_group, G, H} -> - Mod = loaded_shared_roster_module(H), - Mod:is_user_in_group({User, Server}, G, H); - {user_regexp, UR, S} -> - (S == Server) andalso - is_regexp_match(User, UR); - {server_regexp, SR} -> - is_regexp_match(Server, SR); - {resource_regexp, RR} -> - is_regexp_match(Resource, RR); - {node_regexp, UR, SR} -> - is_regexp_match(Server, SR) andalso - is_regexp_match(User, UR); - {user_glob, UR} -> - ((Host == Server) orelse - ((Host == global) andalso - lists:member(Server, ?MYHOSTS))) - andalso - is_glob_match(User, UR); - {user_glob, UR, S} -> - (S == Server) andalso - is_glob_match(User, UR); - {server_glob, SR} -> - is_glob_match(Server, SR); - {resource_glob, RR} -> - is_glob_match(Resource, RR); - {node_glob, UR, SR} -> - is_glob_match(Server, SR) andalso - is_glob_match(User, UR); - WrongSpec -> - ?ERROR_MSG( - "Wrong ACL expression: ~p~n" - "Check your config file and reload it with the override_acls option enabled", - [WrongSpec]), - false - end - end, - ets:lookup(acl, {ACL, global}) ++ + all -> true; + none -> false; + _ -> + {User, Server, Resource} = jlib:jid_tolower(JID), + lists:any(fun (#acl{aclspec = Spec}) -> + case Spec of + all -> true; + {user, U} -> + U == User andalso + (Host == Server orelse + Host == global andalso + lists:member(Server, ?MYHOSTS)); + {user, U, S} -> U == User andalso S == Server; + {server, S} -> S == Server; + {resource, R} -> R == Resource; + {user_regexp, UR} -> + (Host == Server orelse + Host == global andalso + lists:member(Server, ?MYHOSTS)) + andalso is_regexp_match(User, UR); + {shared_group, G} -> + Mod = loaded_shared_roster_module(Host), + Mod:is_user_in_group({User, Server}, G, Host); + {shared_group, G, H} -> + Mod = loaded_shared_roster_module(H), + Mod:is_user_in_group({User, Server}, G, H); + {user_regexp, UR, S} -> + S == Server andalso is_regexp_match(User, UR); + {server_regexp, SR} -> + is_regexp_match(Server, SR); + {resource_regexp, RR} -> + is_regexp_match(Resource, RR); + {node_regexp, UR, SR} -> + is_regexp_match(Server, SR) andalso + is_regexp_match(User, UR); + {user_glob, UR} -> + (Host == Server orelse + Host == global andalso + lists:member(Server, ?MYHOSTS)) + andalso is_glob_match(User, UR); + {user_glob, UR, S} -> + S == Server andalso is_glob_match(User, UR); + {server_glob, SR} -> is_glob_match(Server, SR); + {resource_glob, RR} -> + is_glob_match(Resource, RR); + {node_glob, UR, SR} -> + is_glob_match(Server, SR) andalso + is_glob_match(User, UR); + WrongSpec -> + ?ERROR_MSG("Wrong ACL expression: ~p~nCheck your " + "config file and reload it with the override_a" + "cls option enabled", + [WrongSpec]), + false + end + end, + ets:lookup(acl, {ACL, global}) ++ ets:lookup(acl, {ACL, Host})) end. is_regexp_match(String, RegExp) -> case ejabberd_regexp:run(String, RegExp) of - nomatch -> - false; - match -> - true; - {error, ErrDesc} -> - ?ERROR_MSG( - "Wrong regexp ~p in ACL: ~p", - [RegExp, ErrDesc]), - false + nomatch -> false; + match -> true; + {error, ErrDesc} -> + ?ERROR_MSG("Wrong regexp ~p in ACL: ~p", + [RegExp, ErrDesc]), + false end. is_glob_match(String, Glob) -> - is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)). + is_regexp_match(String, + ejabberd_regexp:sh_to_awk(Glob)). loaded_shared_roster_module(Host) -> case gen_mod:is_loaded(Host, mod_shared_roster_ldap) of - true -> - mod_shared_roster_ldap; - false -> - mod_shared_roster + true -> mod_shared_roster_ldap; + false -> mod_shared_roster + end. + +update_table() -> + Fields = record_info(fields, acl), + case mnesia:table_info(acl, attributes) of + Fields -> + ejabberd_config:convert_table_to_binary( + acl, Fields, bag, + fun(#acl{aclspec = Spec}) when is_tuple(Spec) -> + element(2, Spec); + (_) -> + '$next' + end, + fun(#acl{aclname = {ACLName, Host}, + aclspec = Spec} = R) -> + NewHost = if Host == global -> + Host; + true -> + iolist_to_binary(Host) + end, + R#acl{aclname = {ACLName, NewHost}, + aclspec = normalize_spec(Spec)} + end); + _ -> + ?INFO_MSG("Recreating acl table", []), + mnesia:transform_table(acl, ignore, Fields) end. |