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 |
|