25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-12-20 17:27:00 +01:00

* src/acl.erl: New ACL: shared_group (thanks to Maxim Ryazanov)

* doc/guide.tex: Likewise

* src/mod_shared_roster.erl: Push new group members when
registered or manually added to group: EJAB-730 EJAB-731 EJAB-732
EJAB-767 EJAB-794. When user is added to group, push it to other
members, and other members to it. When user is removed from group,
push deletion to other members, and other members to it. When user
is registered, push him to members of group @all@. When user is
deleted, push deletion to members of group @all@. Document several
functions in mod_shared_roster.

* src/ejabberd_auth.erl: Rename hook user_registered to
register_user, for name consistency with the widely used hook
remove_user. Run hook register_user in ejabberd_auth, so it's run
when account is created with any method. Run hook remove_user in
ejabberd_auth, so it's run when account is deleted with any
method.
* src/ejabberd_auth_internal.erl: Likewise
* src/ejabberd_auth_ldap.erl: Likewise
* src/ejabberd_auth_odbc.erl: Likewise
* src/ejabberd_auth_pam.erl: Likewise
* src/mod_register.erl: Likewise

SVN Revision: 1752
This commit is contained in:
Badlop 2008-12-23 19:15:33 +00:00
parent 35b1e2885e
commit 7808dc11af
10 changed files with 181 additions and 31 deletions

View File

@ -1,5 +1,29 @@
2008-12-23 Badlop <badlop@process-one.net>
* src/acl.erl: New ACL: shared_group (thanks to Maxim Ryazanov)
* doc/guide.tex: Likewise
* src/mod_shared_roster.erl: Push new group members when
registered or manually added to group: EJAB-730 EJAB-731 EJAB-732
EJAB-767 EJAB-794. When user is added to group, push it to other
members, and other members to it. When user is removed from group,
push deletion to other members, and other members to it. When user
is registered, push him to members of group @all@. When user is
deleted, push deletion to members of group @all@. Document several
functions in mod_shared_roster.
* src/ejabberd_auth.erl: Rename hook user_registered to
register_user, for name consistency with the widely used hook
remove_user. Run hook register_user in ejabberd_auth, so it's run
when account is created with any method. Run hook remove_user in
ejabberd_auth, so it's run when account is deleted with any
method.
* src/ejabberd_auth_internal.erl: Likewise
* src/ejabberd_auth_ldap.erl: Likewise
* src/ejabberd_auth_odbc.erl: Likewise
* src/ejabberd_auth_pam.erl: Likewise
* src/mod_register.erl: Likewise
* src/jlib.erl: Implementation of XEP-0059 Result Set
Management (thanks to Eric Cestari)(EJAB-807)
* src/jlib.hrl: Likewise

View File

@ -1239,6 +1239,14 @@ declarations of ACLs in the configuration file have the following syntax:
\begin{verbatim}
{acl, mucklres, {resource, "muckl"}}.
\end{verbatim}
\titem{\{shared\_group, <groupname>\}} Matches any member of a Shared Roster Group with name \term{<groupname>} in the virtual host. Example:
\begin{verbatim}
{acl, techgroupmembers, {shared_group, "techteam"}}.
\end{verbatim}
\titem{\{shared\_group, <groupname>, <server>\}} Matches any member of a Shared Roster Group with name \term{<groupname>} in the virtual host \term{<server>}. Example:
\begin{verbatim}
{acl, techgroupmembers, {shared_group, "techteam", "example.org"}}.
\end{verbatim}
\titem{\{user\_regexp, <regexp>\}} Matches any local user with a name that
matches \term{<regexp>} on local virtual hosts. Example:
\begin{verbatim}

View File

@ -180,6 +180,10 @@ match_acl(ACL, JID, Host) ->
((Host == global) andalso
lists:member(Server, ?MYHOSTS)))
andalso is_regexp_match(User, UR);
{shared_group, G} ->
mod_shared_roster:is_user_in_group({User, Server}, G, Host);
{shared_group, G, H} ->
mod_shared_roster:is_user_in_group({User, Server}, G, H);
{user_regexp, UR, S} ->
(S == Server) andalso
is_regexp_match(User, UR);

View File

@ -139,6 +139,7 @@ set_password(User, Server, Password) ->
Res
end, {error, not_allowed}, auth_modules(Server)).
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, not_allowed}
try_register(_User, _Server, "") ->
%% We do not allow empty password
{error, not_allowed};
@ -149,12 +150,19 @@ try_register(User, Server, Password) ->
false ->
case lists:member(jlib:nameprep(Server), ?MYHOSTS) of
true ->
lists:foldl(
Res = lists:foldl(
fun(_M, {atomic, ok} = Res) ->
Res;
(M, _) ->
M:try_register(User, Server, Password)
end, {error, not_allowed}, auth_modules(Server));
end, {error, not_allowed}, auth_modules(Server)),
case Res of
{atomic, ok} ->
ejabberd_hooks:run(register_user, Server,
[User, Server]),
{atomic, ok};
_ -> Res
end;
false ->
{error, not_allowed}
end
@ -251,17 +259,37 @@ is_user_exists_in_other_modules(Module, User, Server) ->
M:is_user_exists(User, Server)
end, auth_modules(Server)--[Module]).
%% @spec (User, Server) -> ok | error | {error, not_allowed}
%% Remove user.
%% Note: it may return ok even if there was some problem removing the user.
remove_user(User, Server) ->
lists:foreach(
R = lists:foreach(
fun(M) ->
M:remove_user(User, Server)
end, auth_modules(Server)).
end, auth_modules(Server)),
case R of
ok -> ejabberd_hooks:run(remove_user, jlib:nameprep(Server), [User, Server]);
_ -> none
end,
R.
%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request | error
%% Try to remove user if the provided password is correct.
%% The removal is attempted in each auth method provided:
%% when one returns 'ok' the loop stops;
%% if no method returns 'ok' then it returns the error message indicated by the last method attempted.
remove_user(User, Server, Password) ->
lists:foreach(
fun(M) ->
R = lists:foldl(
fun(_M, ok = Res) ->
Res;
(M, _) ->
M:remove_user(User, Server, Password)
end, auth_modules(Server)).
end, error, auth_modules(Server)),
case R of
ok -> ejabberd_hooks:run(remove_user, jlib:nameprep(Server), [User, Server]);
_ -> none
end,
R.
%%%----------------------------------------------------------------------

View File

@ -112,6 +112,7 @@ set_password(User, Server, Password) ->
ok
end.
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid} | {aborted, Reason}
try_register(User, Server, Password) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
@ -237,6 +238,9 @@ is_user_exists(User, Server) ->
false
end.
%% @spec (User, Server) -> ok
%% Remove user.
%% Note: it returns ok even if there was some problem removing the user.
remove_user(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
@ -245,8 +249,10 @@ remove_user(User, Server) ->
mnesia:delete({passwd, US})
end,
mnesia:transaction(F),
ejabberd_hooks:run(remove_user, LServer, [User, Server]).
ok.
%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request
%% Remove user if the provided password is correct.
remove_user(User, Server, Password) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
@ -264,7 +270,6 @@ remove_user(User, Server, Password) ->
end,
case mnesia:transaction(F) of
{atomic, ok} ->
ejabberd_hooks:run(remove_user, LServer, [User, Server]),
ok;
{atomic, Res} ->
Res;

View File

@ -153,6 +153,7 @@ check_password(User, Server, Password, _StreamID, _Digest) ->
set_password(_User, _Server, _Password) ->
{error, not_allowed}.
%% @spec (User, Server, Password) -> {error, not_allowed}
try_register(_User, _Server, _Password) ->
{error, not_allowed}.

View File

@ -114,6 +114,7 @@ set_password(User, Server, Password) ->
end.
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid}
try_register(User, Server, Password) ->
case jlib:nodeprep(User) of
error ->
@ -218,6 +219,9 @@ is_user_exists(User, Server) ->
end
end.
%% @spec (User, Server) -> ok | error
%% Remove user.
%% Note: it may return ok even if there was some problem removing the user.
remove_user(User, Server) ->
case jlib:nodeprep(User) of
error ->
@ -226,10 +230,11 @@ remove_user(User, Server) ->
Username = ejabberd_odbc:escape(LUser),
LServer = jlib:nameprep(Server),
catch odbc_queries:del_user(LServer, Username),
ejabberd_hooks:run(remove_user, jlib:nameprep(Server),
[User, Server])
ok
end.
%% @spec (User, Server, Password) -> ok | error | not_exists | not_allowed
%% Remove user if the provided password is correct.
remove_user(User, Server, Password) ->
case jlib:nodeprep(User) of
error ->
@ -243,8 +248,6 @@ remove_user(User, Server, Password) ->
LServer, Username, Pass),
case Result of
{selected, ["password"], [{Password}]} ->
ejabberd_hooks:run(remove_user, jlib:nameprep(Server),
[User, Server]),
ok;
{selected, ["password"], []} ->
not_exists;

View File

@ -91,7 +91,7 @@ remove_user(_User, _Server) ->
{error, not_allowed}.
remove_user(_User, _Server, _Password) ->
{error, not_allowed}.
not_allowed.
plain_password_required() ->
true.

View File

@ -223,8 +223,6 @@ try_register(User, Server, Password, Source, Lang) ->
true ->
case ejabberd_auth:try_register(User, Server, Password) of
{atomic, ok} ->
ejabberd_hooks:run(user_registered, Server,
[User, Server]),
send_welcome_message(JID),
send_registration_notifications(JID),
ok;

View File

@ -37,7 +37,8 @@
process_item/2,
in_subscription/6,
out_subscription/4,
user_registered/2,
register_user/2,
remove_user/2,
list_groups/1,
create_group/2,
create_group/3,
@ -46,6 +47,7 @@
set_group_opts/3,
get_group_users/2,
get_group_explicit_users/2,
is_user_in_group/3,
add_user_to_group/3,
remove_user_from_group/3]).
@ -83,8 +85,10 @@ start(Host, _Opts) ->
?MODULE, get_jid_info, 70),
ejabberd_hooks:add(roster_process_item, Host,
?MODULE, process_item, 50),
ejabberd_hooks:add(user_registered, Host,
?MODULE, user_registered, 50).
ejabberd_hooks:add(register_user, Host,
?MODULE, register_user, 50),
ejabberd_hooks:add(remove_user, Host,
?MODULE, remove_user, 50).
%%ejabberd_hooks:add(remove_user, Host,
%% ?MODULE, remove_user, 50),
@ -105,8 +109,10 @@ stop(Host) ->
?MODULE, get_jid_info, 70),
ejabberd_hooks:delete(roster_process_item, Host,
?MODULE, process_item, 50),
ejabberd_hooks:delete(user_registered, Host,
?MODULE, user_registered, 50).
ejabberd_hooks:delete(register_user, Host,
?MODULE, register_user, 50),
ejabberd_hooks:delete(remove_user, Host,
?MODULE, remove_user, 50).
%%ejabberd_hooks:delete(remove_user, Host,
%% ?MODULE, remove_user, 50),
@ -424,6 +430,7 @@ get_group_users(_User, Host, Group, GroupOpts) ->
[]
end ++ get_group_explicit_users(Host, Group).
%% @spec (Host::string(), Group::string()) -> [{User::string(), Server::string()}]
get_group_explicit_users(Host, Group) ->
Read = (catch mnesia:dirty_index_read(
sr_user,
@ -439,6 +446,7 @@ get_group_explicit_users(Host, Group) ->
get_group_name(Host, Group) ->
get_group_opt(Host, Group, name, Group).
%% Get list of names of groups that have @all@ in the memberlist
get_special_users_groups(Host) ->
lists:filter(
fun(Group) ->
@ -446,6 +454,8 @@ get_special_users_groups(Host) ->
end,
list_groups(Host)).
%% Given two lists of groupnames and their options,
%% return the list of displayed groups to the second list
displayed_groups(GroupsOpts, SelectedGroupsOpts) ->
DisplayedGroups =
lists:usort(
@ -457,6 +467,9 @@ displayed_groups(GroupsOpts, SelectedGroupsOpts) ->
[G || G <- DisplayedGroups,
not lists:member(disabled, proplists:get_value(G, GroupsOpts, []))].
%% Given a list of group names with options,
%% for those that have @all@ in memberlist,
%% get the list of groups displayed
get_special_displayed_groups(GroupsOpts) ->
Groups = lists:filter(
fun({_Group, Opts}) ->
@ -464,6 +477,9 @@ get_special_displayed_groups(GroupsOpts) ->
end, GroupsOpts),
displayed_groups(GroupsOpts, Groups).
%% Given a username and server, and a list of group names with options,
%% for the list of groups of that server that user is member
%% get the list of groups displayed
get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
Groups = case catch mnesia:dirty_read(sr_user, {LUser, LServer}) of
Rs when is_list(Rs) ->
@ -474,6 +490,7 @@ get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
end,
displayed_groups(GroupsOpts, Groups).
%% @doc Get the list of groups that are displayed to this user
get_user_displayed_groups(US) ->
Host = element(2, US),
DisplayedGroups1 =
@ -496,22 +513,60 @@ is_user_in_group({_U, S} = US, Group, Host) ->
_ -> true
end.
%% @spec (Host::string(), {User::string(), Server::string()}, Group::string()) -> {atomic, ok}
add_user_to_group(Host, US, Group) ->
{LUser, LServer} = US,
%% Push this new user to members of groups where this group is displayed
push_user_to_displayed(LUser, LServer, Group, both),
%% Push members of groups that are displayed to this group
push_displayed_to_user(LUser, LServer, Group, Host, both),
R = #sr_user{us = US, group_host = {Group, Host}},
F = fun() ->
mnesia:write(R)
end,
mnesia:transaction(F).
push_displayed_to_user(LUser, LServer, Group, Host, Subscription) ->
GroupsOpts = groups_with_opts(LServer),
GroupOpts = proplists:get_value(Group, GroupsOpts, []),
DisplayedGroups = proplists:get_value(displayed_groups, GroupOpts, []),
[push_members_to_user(LUser, LServer, DGroup, Host, Subscription) || DGroup <- DisplayedGroups].
remove_user_from_group(Host, US, Group) ->
GroupHost = {Group, Host},
R = #sr_user{us = US, group_host = GroupHost},
F = fun() ->
mnesia:delete_object(R)
end,
mnesia:transaction(F).
Result = mnesia:transaction(F),
{LUser, LServer} = US,
%% Push removal of the old user to members of groups where the group that this user was members was displayed
push_user_to_displayed(LUser, LServer, Group, remove),
%% Push removal of members of groups that where displayed to the group which this user has left
push_displayed_to_user(LUser, LServer, Group, Host, remove),
Result.
user_registered(User, Server) ->
push_members_to_user(LUser, LServer, Group, Host, Subscription) ->
GroupsOpts = groups_with_opts(LServer),
GroupOpts = proplists:get_value(Group, GroupsOpts, []),
GroupName = proplists:get_value(name, GroupOpts, Group),
Members = get_group_users(Host, Group),
lists:foreach(
fun({U, S}) ->
push_roster_item(LUser, LServer, U, S, GroupName, Subscription)
end, Members).
register_user(User, Server) ->
%% Get list of groups where this user is member
Groups = get_user_groups({User, Server}),
%% Push this user to members of groups where is displayed a group which this user is member
[push_user_to_displayed(User, Server, Group, both) || Group <- Groups].
remove_user(User, Server) ->
push_user_to_members(User, Server, remove).
push_user_to_members(User, Server, Subscription) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
GroupsOpts = groups_with_opts(LServer),
@ -523,17 +578,31 @@ user_registered(User, Server) ->
GroupName = proplists:get_value(name, GroupOpts, Group),
lists:foreach(
fun({U, S}) ->
Item = #roster{usj = {U, S, {LUser, LServer, ""}},
us = {U, S},
jid = {LUser, LServer, ""},
name = "",
subscription = both,
ask = none,
groups = [GroupName]},
push_item(U, S, jlib:make_jid("", S, ""), Item)
push_roster_item(U, S, LUser, LServer, GroupName, Subscription)
end, get_group_users(LUser, LServer, Group, GroupOpts))
end, lists:usort(SpecialGroups++UserGroups)).
push_user_to_displayed(LUser, LServer, Group, Subscription) ->
GroupsOpts = groups_with_opts(LServer),
GroupOpts = proplists:get_value(Group, GroupsOpts, []),
GroupName = proplists:get_value(name, GroupOpts, Group),
DisplayedToGroupsOpts = displayed_to_groups(Group, LServer),
[push_user_to_group(LUser, LServer, GroupD, GroupName, Subscription) || {GroupD, _Opts} <- DisplayedToGroupsOpts].
push_user_to_group(LUser, LServer, Group, GroupName, Subscription) ->
lists:foreach(
fun({U, S}) ->
push_roster_item(U, S, LUser, LServer, GroupName, Subscription)
end, get_group_users(LServer, Group)).
%% Get list of groups to which this group is displayed
displayed_to_groups(GroupName, LServer) ->
GroupsOpts = groups_with_opts(LServer),
lists:filter(
fun({_Group, Opts}) ->
lists:member(GroupName, proplists:get_value(displayed_groups, Opts, []))
end, GroupsOpts).
push_item(_User, _Server, _From, none) ->
ok;
push_item(User, Server, From, Item) ->
@ -558,6 +627,16 @@ push_item(User, Server, From, Item) ->
ejabberd_router:route(JID, JID, Stanza)
end, ejabberd_sm:get_user_resources(User, Server)).
push_roster_item(User, Server, ContactU, ContactS, GroupName, Subscription) ->
Item = #roster{usj = {User, Server, {ContactU, ContactS, ""}},
us = {User, Server},
jid = {ContactU, ContactS, ""},
name = "",
subscription = Subscription,
ask = none,
groups = [GroupName]},
push_item(User, Server, jlib:make_jid("", Server, ""), Item).
item_to_xml(Item) ->
Attrs1 = [{"jid", jlib:jid_to_string(Item#roster.jid)}],
Attrs2 = case Item#roster.name of