]> granicus.if.org Git - ejabberd/commitdiff
Add access rule to mod_roster (EJAB-72)
authorBadlop <badlop@process-one.net>
Wed, 4 Dec 2013 13:55:21 +0000 (14:55 +0100)
committerBadlop <badlop@process-one.net>
Wed, 4 Dec 2013 13:57:44 +0000 (14:57 +0100)
doc/guide.tex
src/ejabberd_c2s.erl
src/mod_roster.erl

index 4d3b2b4ff757ce9548ba6990fe6c93cb82cfaf3a..468faf009ce365df15abf8936da2d42a75fc6870 100644 (file)
@@ -4039,6 +4039,13 @@ Options:
   This option is disabled by default.
   Important: if you use \modsharedroster{} or \modsharedrosterldap{},
   you must disable this option.
+ \titem{access} \ind{options!access}
+   This option can be configured to specify rules to restrict roster management.
+   If a rule returns `deny' on the requested user name,
+   that user cannot modify his personal roster:
+   not add/remove/modify contacts,
+   or subscribe/unsubscribe presence.
+   By default there aren't restrictions.
 \end{description}
 
 This example configuration enables Roster Versioning with storage of current id:
@@ -4051,6 +4058,24 @@ modules:
   ...
 \end{verbatim}
 
+With this example configuration, only admins can manage their rosters;
+everybody else cannot modify the roster:
+\begin{verbatim}
+acl:
+  admin:
+    user:
+      - "sarah": "example.org"
+access:
+  roster:
+    admin: allow
+
+modules:
+  ...
+  mod_roster:
+    access: roster
+  ...
+\end{verbatim}
+
 \makesubsection{modservicelog}{\modservicelog{}}
 \ind{modules!\modservicelog{}}\ind{message auditing}\ind{Bandersnatch}
 
index fa8ec3f5b22a0f86836e0539f0d70fc3fd1d97a8..e5304044a4bd94cadece0cfd7f6783a5e043fa94 100644 (file)
@@ -1879,10 +1879,7 @@ presence_track(From, To, Packet, StateData) ->
          A = remove_element(LTo, StateData#state.pres_a),
          StateData#state{pres_a = A};
       <<"subscribe">> ->
-         ejabberd_hooks:run(roster_out_subscription, Server,
-                            [User, Server, To, subscribe]),
-         check_privacy_route(From, StateData,
-                             jlib:jid_remove_resource(From), To, Packet),
+         try_roster_subscribe(subscribe, User, Server, From, To, Packet, StateData),
          StateData;
       <<"subscribed">> ->
          ejabberd_hooks:run(roster_out_subscription, Server,
@@ -1891,10 +1888,7 @@ presence_track(From, To, Packet, StateData) ->
                              jlib:jid_remove_resource(From), To, Packet),
          StateData;
       <<"unsubscribe">> ->
-         ejabberd_hooks:run(roster_out_subscription, Server,
-                            [User, Server, To, unsubscribe]),
-         check_privacy_route(From, StateData,
-                             jlib:jid_remove_resource(From), To, Packet),
+         try_roster_subscribe(unsubscribe, User, Server, From, To, Packet, StateData),
          StateData;
       <<"unsubscribed">> ->
          ejabberd_hooks:run(roster_out_subscription, Server,
@@ -1943,6 +1937,22 @@ is_privacy_allow(StateData, From, To, Packet, Dir) ->
     allow ==
       privacy_check_packet(StateData, From, To, Packet, Dir).
 
+%%% Check ACL before allowing to send a subscription stanza
+try_roster_subscribe(Type, User, Server, From, To, Packet, StateData) ->
+    JID1 = jlib:make_jid(User, Server, <<"">>),
+    Access = gen_mod:get_module_opt(Server, mod_roster, access, fun(A) when is_atom(A) -> A end, all),
+    case acl:match_rule(Server, Access, JID1) of
+       deny ->
+           %% Silently drop this (un)subscription request
+           ok;
+       allow ->
+           ejabberd_hooks:run(roster_out_subscription,
+                              Server,
+                              [User, Server, To, Type]),
+           check_privacy_route(From, StateData, jlib:jid_remove_resource(From),
+                               To, Packet)
+    end.
+
 %% Send presence when disconnecting
 presence_broadcast(StateData, From, JIDSet, Packet) ->
     JIDs = ?SETS:to_list(JIDSet),
index dd211b95ab091f72b7f1a9a2bf9f436b67a766ae..7415aa3ded6351fd4c7c8a3087ea430965ec03e0 100644 (file)
@@ -142,7 +142,7 @@ process_iq(From, To, IQ) ->
 
 process_local_iq(From, To, #iq{type = Type} = IQ) ->
     case Type of
-      set -> process_iq_set(From, To, IQ);
+      set -> try_process_iq_set(From, To, IQ);
       get -> process_iq_get(From, To, IQ)
     end.
 
@@ -455,6 +455,16 @@ get_roster_by_jid_t(LUser, LServer, LJID, odbc) ->
          end
     end.
 
+try_process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
+    #jid{server = Server} = From,
+    Access = gen_mod:get_module_opt(Server, ?MODULE, access, fun(A) when is_atom(A) -> A end, all),
+    case acl:match_rule(Server, Access, From) of
+       deny ->
+           IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
+       allow ->
+           process_iq_set(From, To, IQ)
+    end.
+
 process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
     #xmlel{children = Els} = SubEl,
     lists:foreach(fun (El) -> process_item_set(From, To, El)
@@ -1508,7 +1518,7 @@ user_roster_item_parse_query(User, Server, Items,
                                  {value, _} ->
                                      UJID = jlib:make_jid(User, Server,
                                                           <<"">>),
-                                     process_iq(UJID, UJID,
+                                     process_iq_set(UJID, UJID,
                                                 #iq{type = set,
                                                     sub_el =
                                                         #xmlel{name =