]> granicus.if.org Git - ejabberd/commitdiff
* src/acl.erl: New ACL: shared_group (thanks to Maxim Ryazanov)
authorBadlop <badlop@process-one.net>
Tue, 23 Dec 2008 19:24:45 +0000 (19:24 +0000)
committerBadlop <badlop@process-one.net>
Tue, 23 Dec 2008 19:24:45 +0000 (19:24 +0000)
* 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: 1753

ChangeLog
doc/guide.tex
src/acl.erl
src/ejabberd_auth.erl
src/ejabberd_auth_internal.erl
src/ejabberd_auth_ldap.erl
src/ejabberd_auth_odbc.erl
src/ejabberd_auth_pam.erl
src/mod_register.erl
src/mod_shared_roster.erl

index 414842d676bfd81e9ed773d01911ac3d4d6cf917..46f26390ed67cfada85cb0d417ef8c0b40c5280b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +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
+
 2008-12-23  Christophe Romain <christophe.romain@process-one.net>
 
        * src/mod_pubsub/mod_pubsub.erl: Improve handling of PEP sent to
index f1e25fb728277809e5b19386488b555d22fa6d7b..6b3f0be597630c6ac7ad7692b7acde2c0fc4c859 100644 (file)
@@ -1215,6 +1215,14 @@ declarations of ACLs in the configuration file have the following syntax:
 \begin{verbatim}
 {acl, exampleorg, {server, "example.org"}}.
 \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}
index 435d175b2e51dd65f554bc4317164c7785d1ec96..6e1156e3480ebfeaf20e654ce2903b280266ea11 100644 (file)
@@ -178,6 +178,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);
index be48bcff2f6cc58e911e708ab11b7afe5eb0483c..d50278df16822208b01c9d4db247c18cde483bf2 100644 (file)
@@ -141,6 +141,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};    
@@ -151,12 +152,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
@@ -253,17 +261,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.
 
 ctl_process_get_registered(_Val, Host, ["registered-users"]) ->
     Users = ejabberd_auth:get_vh_registered_users(Host),
index b5661539f65e79fa3a1be9428ba9c0896c4bb7dd..1f545c2730712efd7d675eaa9330de80588d47e5 100644 (file)
@@ -117,6 +117,7 @@ set_password(User, Server, Password) ->
     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),
@@ -242,6 +243,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),
@@ -250,8 +254,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),
@@ -269,7 +275,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;
index 9adb5c696b1b28efa677bdca62055d02acbe76ff..ff5136fe677c727f736af088e0b2ffc9b02bca2f 100644 (file)
@@ -160,6 +160,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}.
 
index 38bb4760a1d1bac18f541bf6ce217227da0001ee..d2843137e198f64a1af06854d19de8a37bfc732a 100644 (file)
@@ -118,6 +118,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 ->
@@ -222,6 +223,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 ->
@@ -230,10 +234,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 ->
@@ -247,8 +252,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;
index f26296d33d07c000a19289cf9759daf673b0b5b0..5f67bedba4b23fcc88468744e4678afde88d36e8 100644 (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.
index 7a4eca79f7c82c7f7adce3ac41280c0f6496a44e..09867fdc72513c649f6f6ec6abb4654c0b53de05 100644 (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;
index 90614a35b04fa54a1f0d0829a8144309b5b9e293..f5091db45f2945a6c675adb021f678b0c4e2fe28 100644 (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.
+
+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).
 
-user_registered(User, Server) ->
+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