1 |
slords |
1.1 |
%%%---------------------------------------------------------------------- |
2 |
|
|
%%% File : mod_shared_roster.erl |
3 |
|
|
%%% Author : Alexey Shchepin <alexey@sevcom.net> |
4 |
|
|
%%% Author : Stanislav Bogatyrev <realloc@realloc.spb.ru> |
5 |
|
|
%%% Purpose : Shared roster management |
6 |
|
|
%%% Created : 5 Mar 2005 by Alexey Shchepin <alexey@sevcom.net> |
7 |
|
|
%%% Id : $Id: mod_shared_roster.erl 24 2005-04-14 01:15:31Z alexey $ |
8 |
|
|
%%%---------------------------------------------------------------------- |
9 |
|
|
|
10 |
|
|
-module(mod_shared_roster_ad). |
11 |
|
|
-author('alexey@sevcom.net'). |
12 |
|
|
-author('realloc@realloc.spb.ru'). |
13 |
|
|
-vsn('$Revision: 24 $ '). |
14 |
|
|
|
15 |
|
|
-behaviour(gen_mod). |
16 |
|
|
|
17 |
|
|
-export([start/2, stop/1, |
18 |
|
|
get_user_roster/2, |
19 |
|
|
get_subscription_lists/3, |
20 |
|
|
get_jid_info/4, |
21 |
|
|
in_subscription/5, |
22 |
|
|
out_subscription/4, |
23 |
|
|
list_groups/1, |
24 |
|
|
create_group/2, |
25 |
|
|
create_group/3, |
26 |
|
|
delete_group/2, |
27 |
|
|
get_group_opts/2, |
28 |
|
|
set_group_opts/3, |
29 |
|
|
get_group_users/2, |
30 |
|
|
get_group_explicit_users/2, |
31 |
|
|
add_user_to_group/3, |
32 |
|
|
remove_user_from_group/3]). |
33 |
|
|
|
34 |
|
|
-include("ejabberd.hrl"). |
35 |
|
|
-include("jlib.hrl"). |
36 |
|
|
-include("mod_roster.hrl"). |
37 |
|
|
-include("eldap/eldap.hrl"). |
38 |
|
|
|
39 |
|
|
-record(sr_group, {group_host, opts}). |
40 |
|
|
-record(sr_user, {us, group_host}). |
41 |
|
|
|
42 |
|
|
start(Host, _Opts) -> |
43 |
|
|
mnesia:create_table(sr_group, |
44 |
|
|
[{disc_copies, [node()]}, |
45 |
|
|
{attributes, record_info(fields, sr_group)}]), |
46 |
|
|
mnesia:create_table(sr_user, |
47 |
|
|
[{disc_copies, [node()]}, |
48 |
|
|
{type, bag}, |
49 |
|
|
{attributes, record_info(fields, sr_user)}]), |
50 |
|
|
mnesia:add_table_index(sr_user, group_host), |
51 |
|
|
ejabberd_hooks:add(roster_get, Host, |
52 |
|
|
?MODULE, get_user_roster, 70), |
53 |
|
|
ejabberd_hooks:add(roster_in_subscription, Host, |
54 |
|
|
?MODULE, in_subscription, 30), |
55 |
|
|
ejabberd_hooks:add(roster_out_subscription, Host, |
56 |
|
|
?MODULE, out_subscription, 30), |
57 |
|
|
ejabberd_hooks:add(roster_get_subscription_lists, Host, |
58 |
|
|
?MODULE, get_subscription_lists, 70), |
59 |
|
|
ejabberd_hooks:add(roster_get_jid_info, Host, |
60 |
|
|
?MODULE, get_jid_info, 70), |
61 |
|
|
|
62 |
|
|
%ejabberd_hooks:add(remove_user, Host, |
63 |
|
|
% ?MODULE, remove_user, 50), |
64 |
|
|
LDAPServers = ejabberd_config:get_local_option({ad_servers, Host}), |
65 |
|
|
RootDN = ejabberd_config:get_local_option({ad_rootdn, Host}), |
66 |
|
|
Password = ejabberd_config:get_local_option({ad_password, Host}), |
67 |
|
|
eldap:start_link("mod_shared_roster_ad", LDAPServers, 389, RootDN, Password). |
68 |
|
|
|
69 |
|
|
|
70 |
|
|
|
71 |
|
|
stop(Host) -> |
72 |
|
|
ejabberd_hooks:delete(roster_get, Host, |
73 |
|
|
?MODULE, get_user_roster, 70), |
74 |
|
|
ejabberd_hooks:delete(roster_in_subscription, Host, |
75 |
|
|
?MODULE, in_subscription, 30), |
76 |
|
|
ejabberd_hooks:delete(roster_out_subscription, Host, |
77 |
|
|
?MODULE, out_subscription, 30), |
78 |
|
|
ejabberd_hooks:delete(roster_get_subscription_lists, Host, |
79 |
|
|
?MODULE, get_subscription_lists, 70), |
80 |
|
|
ejabberd_hooks:delete(roster_get_jid_info, Host, |
81 |
|
|
?MODULE, get_jid_info, 70). |
82 |
|
|
%ejabberd_hooks:delete(remove_user, Host, |
83 |
|
|
% ?MODULE, remove_user, 50), |
84 |
|
|
|
85 |
|
|
|
86 |
|
|
get_user_roster(Items, US) -> |
87 |
|
|
{U, S} = US, |
88 |
|
|
DisplayedGroups = get_user_displayed_groups_ad(US), |
89 |
|
|
SRUsers = |
90 |
|
|
lists:foldl( |
91 |
|
|
fun(Group, Acc1) -> |
92 |
|
|
lists:foldl( |
93 |
|
|
fun(User, Acc2) -> |
94 |
|
|
dict:append(User, Group, Acc2) |
95 |
|
|
end, Acc1, get_group_users_ad(S, Group)) |
96 |
|
|
end, dict:new(), DisplayedGroups), |
97 |
|
|
{NewItems1, SRUsersRest} = |
98 |
|
|
lists:mapfoldl( |
99 |
|
|
fun(Item, SRUsers1) -> |
100 |
|
|
{_, _, {U1, S1, _}} = Item#roster.usj, |
101 |
|
|
US1 = {U1, S1}, |
102 |
|
|
case dict:find(US1, SRUsers1) of |
103 |
|
|
{ok, _GroupNames} -> |
104 |
|
|
{Item#roster{subscription = both, ask = none}, |
105 |
|
|
dict:erase(US1, SRUsers1)}; |
106 |
|
|
error -> |
107 |
|
|
{Item, SRUsers1} |
108 |
|
|
end |
109 |
|
|
end, SRUsers, Items), |
110 |
|
|
SRItems = [#roster{usj = {U, S, {U1, S1, ""}}, |
111 |
|
|
us = US, |
112 |
|
|
jid = {U1, S1, ""}, |
113 |
|
|
name = get_user_fn(U1,S1), |
114 |
|
|
subscription = both, |
115 |
|
|
ask = none, |
116 |
|
|
groups = GroupNames} || |
117 |
|
|
{{U1, S1}, GroupNames} <- dict:to_list(SRUsersRest)], |
118 |
|
|
SRItems ++ NewItems1. |
119 |
|
|
|
120 |
|
|
get_subscription_lists({F, T}, User, Server) -> |
121 |
|
|
LUser = jlib:nodeprep(User), |
122 |
|
|
LServer = jlib:nameprep(Server), |
123 |
|
|
US = {LUser, LServer}, |
124 |
|
|
DisplayedGroups = get_user_displayed_groups_ad(US), |
125 |
|
|
SRUsers = |
126 |
|
|
lists:usort( |
127 |
|
|
lists:flatmap( |
128 |
|
|
fun(Group) -> |
129 |
|
|
get_group_users_ad(LServer, Group) |
130 |
|
|
end, DisplayedGroups)), |
131 |
|
|
SRJIDs = [{U1, S1, ""} || {U1, S1} <- SRUsers], |
132 |
|
|
{lists:usort(SRJIDs ++ F), lists:usort(SRJIDs ++ T)}. |
133 |
|
|
|
134 |
|
|
get_jid_info({Subscription, Groups}, User, Server, JID) -> |
135 |
|
|
LUser = jlib:nodeprep(User), |
136 |
|
|
LServer = jlib:nameprep(Server), |
137 |
|
|
US = {LUser, LServer}, |
138 |
|
|
{U1, S1, _} = jlib:jid_tolower(JID), |
139 |
|
|
US1 = {U1, S1}, |
140 |
|
|
DisplayedGroups = get_user_displayed_groups_ad(US), |
141 |
|
|
SRUsers = |
142 |
|
|
lists:foldl( |
143 |
|
|
fun(Group, Acc1) -> |
144 |
|
|
lists:foldl( |
145 |
|
|
fun(User1, Acc2) -> |
146 |
|
|
dict:append( |
147 |
|
|
User1, Group, Acc2) |
148 |
|
|
end, Acc1, get_group_users_ad(LServer, Group)) |
149 |
|
|
end, dict:new(), DisplayedGroups), |
150 |
|
|
case dict:find(US1, SRUsers) of |
151 |
|
|
{ok, GroupNames} -> |
152 |
|
|
NewGroups = if |
153 |
|
|
Groups == [] -> GroupNames; |
154 |
|
|
true -> Groups |
155 |
|
|
end, |
156 |
|
|
{both, NewGroups}; |
157 |
|
|
error -> |
158 |
|
|
{Subscription, Groups} |
159 |
|
|
end. |
160 |
|
|
|
161 |
|
|
in_subscription(Acc, User, Server, JID, Type) -> |
162 |
|
|
process_subscription(in, User, Server, JID, Type, Acc). |
163 |
|
|
|
164 |
|
|
out_subscription(User, Server, JID, Type) -> |
165 |
|
|
process_subscription(out, User, Server, JID, Type, false). |
166 |
|
|
|
167 |
|
|
process_subscription(Direction, User, Server, JID, _Type, Acc) -> |
168 |
|
|
LUser = jlib:nodeprep(User), |
169 |
|
|
LServer = jlib:nameprep(Server), |
170 |
|
|
US = {LUser, LServer}, |
171 |
|
|
{U1, S1, _} = jlib:jid_tolower(jlib:jid_remove_resource(JID)), |
172 |
|
|
US1 = {U1, S1}, |
173 |
|
|
DisplayedGroups = get_user_displayed_groups_ad(US), |
174 |
|
|
SRUsers = |
175 |
|
|
lists:usort( |
176 |
|
|
lists:flatmap( |
177 |
|
|
fun(Group) -> |
178 |
|
|
get_group_users_ad(LServer, Group) |
179 |
|
|
end, DisplayedGroups)), |
180 |
|
|
case lists:member(US1, SRUsers) of |
181 |
|
|
true -> |
182 |
|
|
case Direction of |
183 |
|
|
in -> |
184 |
|
|
{stop, false}; |
185 |
|
|
out -> |
186 |
|
|
stop |
187 |
|
|
end; |
188 |
|
|
false -> |
189 |
|
|
Acc |
190 |
|
|
end. |
191 |
|
|
|
192 |
|
|
list_groups(Host) -> |
193 |
|
|
get_user_displayed_groups_ad({"",Host}). |
194 |
|
|
|
195 |
|
|
|
196 |
|
|
create_group(Host, Group) -> |
197 |
|
|
create_group(Host, Group, []). |
198 |
|
|
|
199 |
|
|
create_group(Host, Group, Opts) -> |
200 |
|
|
R = #sr_group{group_host = {Group, Host}, opts = Opts}, |
201 |
|
|
F = fun() -> |
202 |
|
|
mnesia:write(R) |
203 |
|
|
end, |
204 |
|
|
mnesia:transaction(F). |
205 |
|
|
|
206 |
|
|
delete_group(Host, Group) -> |
207 |
|
|
F = fun() -> |
208 |
|
|
mnesia:delete({sr_group, {Group, Host}}) |
209 |
|
|
end, |
210 |
|
|
mnesia:transaction(F). |
211 |
|
|
|
212 |
|
|
get_group_opts(Host, Group) -> |
213 |
|
|
case catch mnesia:dirty_read(sr_group, {Group, Host}) of |
214 |
|
|
[#sr_group{opts = Opts}] -> |
215 |
|
|
Opts; |
216 |
|
|
_ -> |
217 |
|
|
error |
218 |
|
|
end. |
219 |
|
|
|
220 |
|
|
set_group_opts(Host, Group, Opts) -> |
221 |
|
|
R = #sr_group{group_host = {Group, Host}, opts = Opts}, |
222 |
|
|
F = fun() -> |
223 |
|
|
mnesia:write(R) |
224 |
|
|
end, |
225 |
|
|
mnesia:transaction(F). |
226 |
|
|
|
227 |
|
|
|
228 |
|
|
|
229 |
|
|
get_user_groups(US) -> |
230 |
|
|
Host = element(2, US), |
231 |
|
|
case catch mnesia:dirty_read(sr_user, US) of |
232 |
|
|
Rs when is_list(Rs) -> |
233 |
|
|
[Group || #sr_user{group_host = {Group, H}} <- Rs, H == Host]; |
234 |
|
|
_ -> |
235 |
|
|
[] |
236 |
|
|
end ++ get_all_users_groups(Host). |
237 |
|
|
|
238 |
|
|
is_group_enabled(Host, Group) -> |
239 |
|
|
case catch mnesia:dirty_read(sr_group, {Group, Host}) of |
240 |
|
|
[#sr_group{opts = Opts}] -> |
241 |
|
|
not lists:member(disabled, Opts); |
242 |
|
|
_ -> |
243 |
|
|
false |
244 |
|
|
end. |
245 |
|
|
|
246 |
|
|
get_group_opt(Host, Group, Opt, Default) -> |
247 |
|
|
case catch mnesia:dirty_read(sr_group, {Group, Host}) of |
248 |
|
|
[#sr_group{opts = Opts}] -> |
249 |
|
|
case lists:keysearch(Opt, 1, Opts) of |
250 |
|
|
{value, {_, Val}} -> |
251 |
|
|
Val; |
252 |
|
|
false -> |
253 |
|
|
Default |
254 |
|
|
end; |
255 |
|
|
_ -> |
256 |
|
|
false |
257 |
|
|
end. |
258 |
|
|
|
259 |
|
|
get_group_users(Host, Group) -> |
260 |
|
|
case get_group_opt(Host, Group, all_users, false) of |
261 |
|
|
true -> |
262 |
|
|
ejabberd_auth:get_vh_registered_users(Host); |
263 |
|
|
false -> |
264 |
|
|
[] |
265 |
|
|
end ++ get_group_explicit_users(Host, Group). |
266 |
|
|
|
267 |
|
|
get_group_explicit_users(Host, Group) -> |
268 |
|
|
case catch mnesia:dirty_index_read( |
269 |
|
|
sr_user, {Group, Host}, #sr_user.group_host) of |
270 |
|
|
Rs when is_list(Rs) -> |
271 |
|
|
[R#sr_user.us || R <- Rs]; |
272 |
|
|
_ -> |
273 |
|
|
[] |
274 |
|
|
end. |
275 |
|
|
|
276 |
|
|
get_group_name(Host, Group) -> |
277 |
|
|
get_group_opt(Host, Group, name, Group). |
278 |
|
|
|
279 |
|
|
get_all_users_groups(Host) -> |
280 |
|
|
lists:filter( |
281 |
|
|
fun(Group) -> get_group_opt(Host, Group, all_users, false) end, |
282 |
|
|
list_groups(Host)). |
283 |
|
|
|
284 |
|
|
get_user_displayed_groups(US) -> |
285 |
|
|
Host = element(2, US), |
286 |
|
|
DisplayedGroups1 = |
287 |
|
|
lists:usort( |
288 |
|
|
lists:flatmap( |
289 |
|
|
fun(Group) -> |
290 |
|
|
case is_group_enabled(Host, Group) of |
291 |
|
|
true -> |
292 |
|
|
get_group_opt(Host, Group, displayed_groups, []); |
293 |
|
|
false -> |
294 |
|
|
[] |
295 |
|
|
end |
296 |
|
|
end, get_user_groups(US))), |
297 |
|
|
[Group || Group <- DisplayedGroups1, is_group_enabled(Host, Group)]. |
298 |
|
|
|
299 |
|
|
|
300 |
|
|
|
301 |
|
|
|
302 |
|
|
add_user_to_group(Host, US, Group) -> |
303 |
|
|
R = #sr_user{us = US, group_host = {Group, Host}}, |
304 |
|
|
F = fun() -> |
305 |
|
|
mnesia:write(R) |
306 |
|
|
end, |
307 |
|
|
mnesia:transaction(F). |
308 |
|
|
|
309 |
|
|
remove_user_from_group(Host, US, Group) -> |
310 |
|
|
R = #sr_user{us = US, group_host = {Group, Host}}, |
311 |
|
|
F = fun() -> |
312 |
|
|
mnesia:delete_object(R) |
313 |
|
|
end, |
314 |
|
|
mnesia:transaction(F). |
315 |
|
|
|
316 |
|
|
|
317 |
|
|
|
318 |
|
|
find_user_attr(User, Host) -> |
319 |
|
|
Attr = ejabberd_config:get_local_option({ad_uidattr, Host}), |
320 |
|
|
Filter = eldap:equalityMatch(Attr, User), |
321 |
|
|
Base = ejabberd_config:get_local_option({ad_base, Host}), |
322 |
|
|
|
323 |
|
|
case eldap:search("mod_shared_roster_ad", |
324 |
|
|
[{base, Base}, |
325 |
|
|
{filter, Filter}, |
326 |
|
|
{attributes, []}]) of |
327 |
|
|
#eldap_search_result{entries = [E | _]} -> |
328 |
|
|
E; |
329 |
|
|
_ -> |
330 |
|
|
false |
331 |
|
|
end. |
332 |
|
|
|
333 |
|
|
get_user_displayed_groups_ad(US) -> |
334 |
|
|
{_, Host} = US, |
335 |
|
|
AdGroup = ejabberd_config:get_local_option({ad_group, Host}), |
336 |
|
|
FilterGroup = eldap:equalityMatch("memberOf", AdGroup), |
337 |
|
|
Base = ejabberd_config:get_local_option({ad_base, Host}), |
338 |
|
|
|
339 |
|
|
case eldap:search("mod_shared_roster_ad", |
340 |
|
|
[{base, Base}, |
341 |
|
|
{filter, FilterGroup}, |
342 |
|
|
{attributes, []}]) of |
343 |
|
|
#eldap_search_result{entries = E} -> |
344 |
|
|
lists:usort(lists:map( |
345 |
|
|
fun(X) -> |
346 |
|
|
case X of |
347 |
|
|
#eldap_entry{attributes = Attributes} -> |
348 |
|
|
ldap_get_value(Attributes,"department"); |
349 |
|
|
false -> |
350 |
|
|
"" |
351 |
|
|
end |
352 |
|
|
end, E |
353 |
|
|
)); |
354 |
|
|
|
355 |
|
|
_ -> |
356 |
|
|
[] |
357 |
|
|
end. |
358 |
|
|
|
359 |
|
|
get_eldap_id(Host, Name) -> |
360 |
|
|
atom_to_list(gen_mod:get_module_proc(Host, Name)). |
361 |
|
|
|
362 |
|
|
|
363 |
|
|
get_group_users_ad(Host, Group) -> |
364 |
|
|
Attr = ejabberd_config:get_local_option({ad_uidattr, Host}), |
365 |
|
|
% AdGroup = ejabberd_config:get_local_option({ad_group, Host}), |
366 |
|
|
FilterPerson = eldap:equalityMatch("objectCategory", "person"), |
367 |
|
|
FilterComp = eldap:equalityMatch("objectClass", "computer"), |
368 |
|
|
FilterHidden = eldap:equalityMatch("description", "hidden"), |
369 |
|
|
% FilterGroup = eldap:equalityMatch("memberOf", AdGroup), |
370 |
|
|
FilterDep = eldap:equalityMatch("department", Group), |
371 |
|
|
FilterLive = eldap:equalityMatch("userAccountControl", "66050"), |
372 |
|
|
FilterDef = eldap:present(Attr), |
373 |
|
|
Filter = eldap:'and'([ |
374 |
|
|
FilterDef, |
375 |
|
|
FilterPerson, |
376 |
|
|
% FilterGroup, |
377 |
|
|
FilterDep, |
378 |
|
|
eldap:'not'(FilterComp), |
379 |
|
|
eldap:'not'(FilterHidden), |
380 |
|
|
eldap:'not'(FilterLive)]), |
381 |
|
|
Base = ejabberd_config:get_local_option({ad_base, Host}), |
382 |
|
|
case eldap:search(get_eldap_id(Host, ejabberd), |
383 |
|
|
[{base, Base}, |
384 |
|
|
{filter, Filter}, |
385 |
|
|
{attributes, [Attr]}]) of |
386 |
|
|
#eldap_search_result{entries = Es} -> |
387 |
|
|
lists:flatmap( |
388 |
|
|
fun(E) -> |
389 |
|
|
case lists:keysearch(Attr, 1, E#eldap_entry.attributes) of |
390 |
|
|
{value, {_, [U]}} -> |
391 |
|
|
case jlib:nodeprep(U) of |
392 |
|
|
error -> |
393 |
|
|
[]; |
394 |
|
|
LU -> |
395 |
|
|
[{LU, Host}] |
396 |
|
|
end; |
397 |
|
|
_ -> |
398 |
|
|
[] |
399 |
|
|
end |
400 |
|
|
end, Es); |
401 |
|
|
_ -> |
402 |
|
|
[] |
403 |
|
|
end. |
404 |
|
|
|
405 |
|
|
|
406 |
|
|
|
407 |
|
|
|
408 |
|
|
|
409 |
|
|
ldap_get_value(E,Attribute) -> |
410 |
|
|
case lists:filter(fun({A,_}) -> |
411 |
|
|
string:equal(A,Attribute) |
412 |
|
|
end,E) of |
413 |
|
|
[{_,[Value|_]}] -> |
414 |
|
|
Value; |
415 |
|
|
_ -> |
416 |
|
|
none |
417 |
|
|
end. |
418 |
|
|
|
419 |
|
|
get_user_fn(User, Host) -> |
420 |
|
|
case find_user_attr(User,Host) of |
421 |
|
|
#eldap_entry{attributes = Attributes} -> |
422 |
|
|
ldap_get_value(Attributes,"cn"); |
423 |
|
|
|
424 |
|
|
false -> |
425 |
|
|
"" |
426 |
|
|
end. |
427 |
|
|
|
428 |
|
|
|