Name: mod_shared_roster_ldap Purpose: LDAP shared roster management Author: Marcin Owsiany (porridge), Evgeniy Khramtsov and Realloc Type: Module Requirements: Download: ejabberd-msrl. Older version: mod_shared_roster_ldap.erl
Check also the mod_shared_roster_ldap documentation



mod_shared_roster_ldap crashes on with 2.1.4 bundle
Hello guys,
I've downloaded ejabberd 2.1.4 and compiled mod_shared_roster_ldap with the bundled erlc without any warnings.
But the module crashes with the following trace in the log:
ERROR: "~p crashed: ~p~n" - ['mod_shared_roster_ldap_mydomain.net',{noproc,
{gen_server,call,
['mod_shared_roster_ldap_mydomain.net',
{get_user_displayed_groups,
"joedowy"},
10000]}}]
Please give me a hint on what I'm missing.
(The system is Debian Lenny)
Also I only copied config from 2.0.5 version. LDAP syslog shows only login BINDs but nothing queried by the module.
Thank you in advance!
This looks like a consequence
This looks like a consequence of the module crash. Please show the log before that message.
I guess nothing interesting here
Thank you for the prompt answer Marcin!
I guess nothing interesting here:
=INFO REPORT==== 2010-06-24 15:54:40 ===I(<0.341.0>:ejabberd_listener:232) : (#Port<0.534>) Accepted connection {{xxx,xxx,xxx,xxx},40319} -> {{xxx,xxx,xxx,xxx},5222}
=INFO REPORT==== 2010-06-24 15:54:41 ===
I(<0.1080.0>:ejabberd_c2s:580) : ({socket_state,tls,{tlssock,#Port<0.534>,#Port<0.536>},<0.1079.0>}) Accepted authentication for joedowy by ejabberd_auth_ldap
=INFO REPORT==== 2010-06-24 15:54:41 ===
I(<0.1080.0>:ejabberd_c2s:839) : ({socket_state,tls,{tlssock,#Port<0.534>,#Port<0.536>},<0.1079.0>}) Opened session for joedowy@mydomain.net/Home
=ERROR REPORT==== 2010-06-24 15:54:41 ===
ERROR: "~p crashed: ~p~n" - ['mod_shared_roster_ldap_mydomain.net',
{noproc,
{gen_server,call,
['mod_shared_roster_ldap_mydomain.net',
{get_user_displayed_groups,
"joedowy"},
10000]}}]
=ERROR REPORT==== 2010-06-24 15:54:42 ===
ERROR: "~p crashed: ~p~n" - ['mod_shared_roster_ldap_mydomain.net',
{noproc,
{gen_server,call,
['mod_shared_roster_ldap_mydomain.net',
{get_user_displayed_groups,
"joedowy"},
10000]}}]
This is from ejabberd.log. erlang.log is silent.
Still, this does not show the
Still, this does not show the root cause. Have a look earlier, perhaps when the module initializes or when the first user connects. If you can't find anything, increase log verbosity, restart ejabberd and retry.
If you still cannot find anything then, send the whole log to my email address.
Replying here for other
Replying here for other people's benefit.
We've found the following crash report in erlang.log:
=CRASH REPORT==== 25-Jun-2010::16:57:04 === crasher: pid: <0.340.0> registered_name: 'eldap_mod_shared_roster_ldap_secondarydomain.net' exception exit: {function_clause, [{proplists,get_value,[encrypt,none,undefined]}, {eldap,init,1}, {gen_fsm,init_it,6}, {proc_lib,init_p,5}]} in function gen_fsm:init_it/6 initial call: gen:init_it(gen_fsm,<0.339.0>,<0.339.0>, {local,'eldap_mod_shared_roster_ldap_secondarydomain.net'}, eldap, {["localhost"], 389, "uid=admin,cn=admin,dc=primarydomain,dc=net", "********",none}, []) ancestors: ['mod_shared_roster_ldap_secondarydomain.net',ejabberd_sup, <0.36.0>] messages: [] links: [<0.339.0>] dictionary: [] trap_exit: false status: running heap_size: 610 stack_size: 23 reductions: 180 neighbours: neighbour: [{pid,<0.339.0>}, {registered_name,'mod_shared_roster_ldap_secondarydomain.net'}, {initial_call, {gen,init_it, [gen_server,<0.216.0>,<0.216.0>, {local,'mod_shared_roster_ldap_secondarydomain.net'}, mod_shared_roster_ldap, ["secondarydomain.net", [{ldap_servers,["localhost"]}, {ldap_port,389}, {ldap_rootdn, "uid=admin,cn=admin,dc=primarydomain,dc=net"}, {ldap_password,"********"}, {ldap_base,"dc=primarydomain,dc=net"}, {ldap_rfilter, "(&(objectClass=organizationalUnit)(ou=Team))"}, {ldap_ufilter,"(&(objectClass=inetOrgPerson)(cn=*))"}, {ldap_gfilter,"(objectClass=inetOrgPerson)"}, {ldap_groupattr,"ou"}, {ldap_memberattr,"uid"}, {ldap_useruid,"uid"}, {ldap_groupdesc,"ou"}, {ldap_userdesc,"cn"}]], []]}}, {current_function,{proc_lib,sync_wait,2}}, {ancestors,[ejabberd_sup,<0.36.0>]}, {messages, [{ack,<0.340.0>, {error, {function_clause, [{proplists,get_value,[encrypt,none,undefined]}, {eldap,init,1}, {gen_fsm,init_it,6}, {proc_lib,init_p,5}]}}}]}, {links,[<0.216.0>,<0.340.0>]}, {dictionary,[]}, {trap_exit,false}, {status,runnable}, {heap_size,987}, {stack_size,18}, {reductions,263}]After having a look at eldap:init function, it's obvious that it has changed in an incompatible fashion in 2.1.4. It is now:
init({Hosts, Port, Rootdn, Passwd, Opts}) ->where is used to be:
init({Hosts, Port, Rootdn, Passwd, Encrypt}) ->since msrl is calling it with "none" as the last arg, then proplists:get_value crashes as none is not a valid list.
Perhaps "none" is not the best value to use, as it basically forces the module to not use TLS even if it's supported.
It would be great if someone could come up with a working patch for that. I might not be able to produce one before I come back from holidays in the middle of July.
Look at the changes in mod_vcard_ldap
After having a look at eldap:init function, it's obvious that it has changed in an incompatible fashion in 2.1.4.
since msrl is calling it with "none" as the last arg, then proplists:get_value crashes as none is not a valid list.
Perhaps "none" is not the best value to use, as it basically forces the module to not use TLS even if it's supported.
It would be great if someone could come up with a working patch for that. I might not be able to produce one before I come back from holidays in the middle of July.
Right, the function API changed between 2.1.3 and 2.1.4:
https://support.process-one.net/browse/EJAB-1229
You can see in the same commit that mod_vcard_ldap changed, too:
https://git.process-one.net/ejabberd/mainline/commit/f58d03c12e1160f40a7...
You can do a similar change; instead of passing 'none', or '[]', you can get the ldap tions from the module Opts, fallback to global options, and then pass a list [{encrypt, ...}, {...}]
THANK YOU!
Marcin!
I owe you one!
I've changed none argument to Opts at mod_shared_roster_ldap.erl:145 and it works now!
AFAIU, that should be enough for a patch, isn't it?
Thank you!
Well, I'm not sure it's a
Well, I'm not sure it's a proper fix - potentially passing Opts there is no different than passing an empty list, but I'd say it's good enough for now if it does not crash.
New mod_shared_roster_ldap version: 0.5.1
The only important change this time is that using ``Display Names'' for usernames which are not all-lower-case now actually works.
Another thing I would like to ask the admin of this page for, is to replace the note about lack of documentation with a link to https://alioth.debian.org/docman/?group_id=100433 - I feel the module is now quite well documented there.
Link to doc, and change authorship
replace the note about lack of documentation
Done. And you are now the 'author' of this page, so you can edit it when needed :)
Thanks!
replace the note about lack of documentation
Done. And you are now the 'author' of this page, so you can edit it when needed :)
Thanks!
Although I cannot figure out how to edit the page? Do I just hit "Quote" and then remove the quote markers?
Also for some reason the forum starts talking to me in Spanish when I click "Create content" :-)
Edit node solved
Although I cannot figure out how to edit the page? Do I just hit "Quote" and then remove the quote markers?
Ah, my mistake. Try again now: between the node title and the "Submitted by" line, it should display you links to view and edit the node.
Also for some reason the forum starts talking to me in Spanish when I click "Create content" :-)
Yes, seems the English strings table got some Spanish sentences, and now I don't know how to fix it :S
Thanks!
Although I cannot figure out how to edit the page? Do I just hit "Quote" and then remove the quote markers?
Ah, my mistake. Try again now: between the node title and the "Submitted by" line, it should display you links to view and edit the node.
Seems to work now, thanks!
My opinion about mod_shared_roster_ldap
This project is very necessary for operation in the corporate environment.
IMHO
I consider that this project should be divided into two projects:
1. Only shared roster with data storage in database (as much as possible simple table) +
may be possible API for service of the data in roster
(as variant: to add record, to remove record, to change record,
to remove all records, to receive version number roster, to change version number roster)
2 Connectors set for the data recording in th rosters table in database.
Connectors realises logic of data acquisition from external
sources and updating of the rosters table.
Necessary connectors(as variant):
- LDAP
- File csv
- HTTP
/IMHO
Thanks for your comments. I
Thanks for your comments. I agree that having this kind of functionality is important for a company. That's why I'm working on it :-)
Can you explain what kind of data management API technology do you have in mind? Do you mean internal ejabberd API, or exposing it as a kind of web service?
As for the split between shared roster and connectors - until recently there was not that much connector-independent logic to warrant a separate connector-independent part. This might change now that "msrl" has caching capabilities. But first I think it would have to catch up with the original "msr" in terms of feature set. For example all roster groups are shown to all users, while in mod_shared_roster the administrator can choose which groups to expose to which group members.
Also, why do you feel an RDBMS-specific connector should be built into the base shared roster module? I would think a connector-agnostic architecture would be cleaner.
I am sorry for long term of
I am sorry for long term of the answer.
I imagines the following architecture:
The roster module <->Intermediate long-term storehouse <->Connectors for storehouse filling.
Data which are displayed by a roster, are the structured information.It makes sense to store them in RDBMS.
Simultaneously this storehouse serves for caching to the information from sources with high-cost access (for example, LDAP). Additional efforts for connection of this storehouse it is not required, as ejabberd already uses DBMS.
The roster module is responsible for logic of display of this structured information. Rules of display can be rigidly defined at once, or be modified at runtime. As the roster is views of the structured data, in this module it makes sense to define rules of views for various users (to what user what to show). This module should have a set of filters. Views is formed from local storehouse that raises speed and gives the chance to optimization (create indexes, tuning query, and etc.).
Modules of connectors receive the information from external sources (any), make its structurization and put into storehouse by rules. Rules can be or are rigidly defined, or flexibly to be modified. Connector should know how to obtain the raw data from a source, to structure them and to place in storehouse. The logic of connector should be as much as possible simple. Possibly, the module of connector also should contain logic of processing of the out-of-date data from the given source (to delete or keep).
As the information in storehouse is structured, it makes sense to abstract from physical structure of storage of the information and to give to it access through API. (export a set of functions for addition, updating, removal, etc.) Connectors (and other modules) can use API for work with the information in storehouse. In my opinion, given API should belong to the roster module.
At such scheme
- Displaying and functions of mining of the data is divided.
- We have an additional layer where the data caching and can be in addition processed
In my opinion it raises reliability of system and simplifies a code.
At such scheme the user can bring the additional information in a roster and if rules of change and processing of the out-of-date data do not result in loss of this information the given roster will possess interesting possibilities.
What do You name connector-agnostic architecture? Probably I have slightly lagged behind a life…
P.S. I apologise for my English.
New mod_shared_roster_ldap version: 0.5.0
Now available at https://alioth.debian.org/projects/ejabberd-msrl/
Most important changes:
See the Changes on the released files page for more information.
Need help on getting AD to work
Has anyone succeed in getting this to work with AD ?
Here is my current config any hints would be nice
{mod_shared_roster_ldap, [
{ldap_groupattr, "cn"},
{ldap_memberattr, "member"},
{ldap_filter, ""},
{ldap_memberattr_format_re, "CN=(\[^,\]*),(OU=.*,)*DC=corenagroup,DC=local"},
{ldap_ufilter, "(&(objectClass=person)(CN=%u))"},
{ldap_rfilter, "(&(objectClass=group)(CN=*_dev))"},
{ldap_user_cache_validity, 1},
{ldap_auth_check,off},
{ldap_useruid, "cn"},
{ldap_userdesc, "sAMAccountName"}
some hints
Some hints based on how version 0.5.0 (just released) works:
(&(objectClass=group)(CN=*_dev))query manually and see if the returned entries have a "member" attribute.(&(member=*)(cn=*))since you did not specify one) and see if the returned entries have attributes "cn" and "member" and their values are sane-looking group name and member list, respectivelyCN=(\[^,\]*),(OU=.*,)*DC=corenagroup,DC=localThese are the basics. Show us example data from of the above steps before you go any further.
New mod_shared_roster_ldap version: 0.4.0
Now available at https://alioth.debian.org/projects/ejabberd-msrl/
Most important changes:
Credits:
See the Changes at the released files page for more information.
DN in member attribute in AD <> sAMAccountName
I have successfully used mod_shared_roster_ldap v0.3.0 to integrate ejabberd to an OpenLDAP server with no problems what so ever.
Recently I tried to integrate to an Active Directory server in our organization, but without success.
I believe my problem is described here, http://www.igniterealtime.org/builds/openfire/docs/latest/documentation/...
See "ldap.posixMode"
The groups has the members in a member attribute which uses the distinguishedName and that doesn't match the sAMAccountName that's used to login
E.g "CN=Homer Simpson,OU=Springfield,OU=People,DC=simpsons,DC=com" vs hsi (hsi@simpsons.com as jid)
Here is some examples from our AD.
I have stripped the unnecessary details and changed domainname, usernames, groupnames etc.
CN=Office A,OU=Groups,DC=simpsons,DC=com
2 groups:
objectClass top
objectClass group
cn Office A
distinguishedName CN=Office A,OU=Groups,DC=simpsons,DC=com
member CN=Homer Simpson,OU=Springfield,OU=People,DC=simpsons,DC=com
member CN=Marge Simpson,OU=Springfield,OU=People,DC=simpsons,DC=com
name Office A
sAMAccountName Office A
objectClass top
objectClass group
cn Office B
distinguishedName CN=Office B,OU=Groups,DC=simpsons,DC=com
member CN=Bart Simpson,OU=Springfield,OU=People,DC=simpsons,DC=com
name Office B
sAMAccountName Office B
3 users:
cn Homer Simpson
objectClass top
objectClass person
objectClass organizationalPerson
objectClass user
distinguishedName CN=Homer Simpson,OU=Springfield,OU=People,DC=simpsons,DC=com
sAMAccountName hsi
cn Marge Simpson
objectClass top
objectClass person
objectClass organizationalPerson
objectClass user
distinguishedName CN=Marge Simpson,OU=Springfield,OU=People,DC=simpsons,DC=com
sAMAccountName msi
cn Bart Simpson
objectClass top
objectClass person
objectClass organizationalPerson
objectClass user
distinguishedName CN=Bart Simpson,OU=Springfield,OU=People,DC=simpsons,DC=com
sAMAccountName bsi
I'm trying to achieve this:
Office A
- hsi@simpsons.com
- msi@simpsons.com
Office B
- bsi@simpsons.com
Group membership is only specified on the group, member attribute, not on the user.
So no memberOf or equivalent
I'm unable to do any changes to the AD
Any ideas?
How to have human-friendly roster item names?
Hi,
It seems that get_user_name() in mod_shared_roster_ldap is meant to provide a way to return the roster items with a name different than the UserID retrieved using GroupFilter in get_group_users(). For example so that you can have "John Smith" appear in your roster, rather than "jsmith".
I cannot find a way to make it work, though. It seems to be due to the problem described here:
https://alioth.debian.org/tracker/index.php?func=detail&aid=312171&group...
I'm using a DIT which is similar to the one shown at https://alioth.debian.org/docman/view.php/100433/4084/msrl.html#htoc12
I have a preliminary patch that introduces more configuration parameters, which allow one to specify the User and/or Group filters manually, rather than have them constructed automatically. But maybe I'm simply missing something and it's possible to make it work without changing the code? Did anyone get it to work? What is your configuration and DIT layout?
regards,
Marcin
Working human-friendly names
I did. My config is here.
FreeBSD 7.2 + ejabberd 2.0.5 + AD (Win2003).
By the way, the "bug" you refer in the link above is not exactly a bug. The idea is to make it possible to use in the shared rosters not only the LDAP objects, but also LDAP multi-value properties. So that it's possible to use an object as group, and its multi-valued property as the users list.
Probably it would be desirable to use the rfilter to find the group descriptions, thus separating group handling and user handling, and minimizing the likeliness of breaking some working config.
Different ways to specify group membership.
I did. My config is here.
Thanks!
By the way, the "bug" you refer in the link above is not exactly a bug. The idea is to make it possible to use in the shared rosters not only the LDAP objects, but also LDAP multi-value properties. So that it's possible to use an object as group, and its multi-valued property as the users list.
It's interesting you say that, because that's exactly the DIT layout that I'm trying to use: groups are objectClass:posixGroup (with members' IDs stored in memberUid multivalued attributes), while users are separate objects of objectClass:inetOrgPerson.
From looking at your config, I can see that it's not the layout you are using. You seem to only have user objects, and your rfilter retrieves group names by listing all "memberOf" attributes of all your users, and taking unique values. Is that correct?
By the way, what objectClass does "memberOf" belong to? Is it some AD-specific thing? I cannot find it in the schemas provided with OpenLDAP.
Those seem to be different paradigms: whether to define group membership on the user-object side, or the group-object side.
It seems that in the paradigm I'm using (group objects, members defined by their memberUids) it's not possible to have get_user_name() work properly.
Probably it would be desirable to use the rfilter to find the group descriptions, thus separating group handling and user handling, and minimizing the likeliness of breaking some working config.
I'm not sure what you're suggesting here? AFAICT specifying an rfilter is the only way to use the module, and that's what I'm doing.
I see. This page in Russian
I see. This page in Russian discuss the same thing.
As I see, the problem is that this module is written in such a presumption that all the needed info is kept in one object (you can query some object for group name, user id, and user name at the same time). You may either use users objects where some of their property designate their group, and some other is their full name, or a group object with a name property and a multi-value users property, though here is no way to specify their display name.
I'm afraid that this is by design. It's because it builds queries from the simple property names given in the parameters. You cannot make a simple solution by restructuring the queries, as it will be inflexible in some other way. If you start adding new properties, you will end up with a pile of similar options that are very difficult to understand for user. Maybe the sensible solution is let user specify the queries themself, not options for them, clearly specifying the purpose and the expected return of each, assuming that as the user is trying to work with LDAP (s)he knows something about its structure and query? And as there is quite a big number of users of one specific scheme (I mean AD), it will be simple to provide a reference working query for this case.
The "memberOf" is a simple string that I fill manually :).
New mod_shared_roster_ldap version: 0.3.0
I've created a new version which adds a couple of options that let you work around the aforementioned problem. It's available at:
https://alioth.debian.org/projects/ejabberd-msrl/
This version is backwards-compatible in the sense that it works just like the previous one in case you do not specify any of the new options.
I've also updated the documentation and added an alternative configuration example, for "flat" tree layouts.
Regarding the last comment
Regarding the last comment (DN in member attribute in AD <> sAMAccountName):
For now, the logic is that we look into "group" (whatever it is) to find the jabber users (so, to find those jids!!!). After that, we make another query to look for their display name.
Maybe it's good to change this logic to something like that: we look into "group" to find "user keys", assuming nothing about their syntax, and after that, for each "user key", we perform another query with user-defined filter to find all the jabber-related information, including both jid and display name. IMO, that could be made with no overhead and in a way that is backwards-compatible. This would lead to the change in the get_user_roster.
Re: New mod_shared_roster_ldap version: 0.3.0
Great job!
I've read the dox you've prepared. Clear and simple.
Maybe it's worth it to add the explanation how the new options are handled into the section 3.2? Because if you just read this chapter to understand how it's started up, you will not get the idea of these new options.
And it would be great to know the kind of the result these filters must provide (single/multiple records). It's clear from the examples, but I feel it desirable to be specified it in the corresponding section where the parameters are defined.
Thank you.
Edit: Sorry, I've missed the mention of the new parameters in the 3.2. So please ignore the first question.
New mod_shared_roster_ldap project page
Hello,
I have created a project for this module on Alioth:
https://alioth.debian.org/projects/ejabberd-msrl/
The goal is to improve the state of this code by providing a central place for:
- documentation
- version control
- bug tracker
I hope that when I can bring it to a state where the code and documentation may be considered for inclusion into the mainstream ejabberd code base. However so far I think that a simple forum page without proper version control is not enough for proper maintenance.
So far on the project page you can find release 0.1.1. The module source code is a bit outdated compared to what is currently available for download from this page. However the major deliverable for this release is the documentation (in HTML and PDF formats) which explains how to use the module, together with example DIT tree diagram and example configuration.
My next goal is to merge in the recent changes from this page.
--
Marcin
Version 0.2.0 released
Note, I've just released version 0.2.0, which brings it in sync with the version available from this page.
regexp compilation warning
Hi!
I use FreeBSD and everything is installed from the ports. My compiler complains with this warning which does not allow me to get a .beam output
/mod_shared_roster_ldap.erl:544: Warning: regexp:sub/3: the regexp module is deprecated (will be removed in R15A); use the re module instead
Unfortunately, I have no idea how to convert the line from regexp to re. The complete line is
case regexp:sub(Pattern, "%u", Result) of
{ok,String,_} ->
{ok,Result};
_ ->
{error,badmatch}
end
Or is there a newer version of this file that doesn't use the regexp anymore?
Thanks!
As you said, this is a
As you said, this is a warning, only a warning, of a future problem. The beam file should be generated. Compilation of ejabberd 2.1.0 with Erlang/OTP R13B shows many of those warnings, and it works perfectly.
Suggest improvements
get_user_roster(Items, US) ->{U, S} = US,
DisplayedGroups = get_user_displayed_groups(US),
%% Get shared roster users in all groups and remove self:
SRUsers =
lists:foldl(
fun(Group, Acc1) ->
%% =======> Add the next line:
GN = get_group_name(S, Group),
lists:foldl(
fun(User, Acc2) ->
if User == US -> Acc2;
true -> dict:append(User,
%% =======> Change the next line:
%% get_group_name(S, Group),
%% =======> to this:
GN,
Acc2)
end
end, Acc1, get_group_users(S, Group))
end, dict:new(), DisplayedGroups),
...
This will query for the group name once for each group, not for every user of the group.
...case catch get_user_part(User, UAF) of
{ok, U} ->
%% ======> Change the next 2 lines:
%%case ejabberd_auth:is_user_exists(U, Host) of
%% true -> [{U, Host} | Acc];
%% ======> to these 3 lines:
LU = jlib:nodeprep(U),
case ejabberd_auth:is_user_exists(LU, Host) of
true -> [{LU, Host} | Acc];
_ -> Acc
end;
_ -> Acc
end
...
As I see in the source code of the ejabberd, there's no general rules of handling the jids: some functions repeatedly STRINGPREP strings, others that may face non-normalized strings forget to do this. It would be nice to determine the points where the jids enter the server code, and make sure that in every such point the normalization takes place, and do not do it elsewhere, as it would be unnecessary (this would become the invariant). This would probably improve the overall performance, and surely would reduce the bugs count.
applied both fixes
Thanks. I've applied both fixes to the published file.
By the way, the first fix
By the way, the first fix could be applied to mod_shared_roster, too...
Right, there was the same
Right, there was the same problem in ejabberd svn. Thanks, it's fixed now.
mod_shared_roster_ldap not works!, my logs>
Hi all, привет всем!
i have probleme:
i download sources from svn.process-one.net/ejabberd 7 make file, so all it works, but i can't configure mod_shared_roster_ldap , its not work, i think it is because mod not load or maybe other...
my logs, when i start server:
root@gateway:/etc/ejabberd# ejabberdctl restartroot@gateway:/etc/ejabberd# sudo tail -f /var/log/ejabberd/ejabberd.log
{gen_server,call,
[mod_shared_roster_ldap_jabber,stop]}}
=ERROR REPORT==== 2009-07-09 14:13:44 ===
E(<0.5530.0>:gen_mod:95) : {noproc,
{gen_server,call,
[mod_shared_roster_ldap_gateway,stop]}}
=INFO REPORT==== 2009-07-09 14:13:44 ===
I(<0.5530.0>:ejabberd_app:83) : ejabberd 2.1.0-alpha is stopped in the node ejabberd@localhost
p.s. OS: Ubuntu 9.04 server
Patch for mod_shared_roster.erl (eJabberD 2.0.3)
I have looked around and put all the pieces together. This has been tested and works as expected using the Full Name as the Roster item name for the jid. No additional modules are needed for LDAP VCard information to populate Shared Rosters..
badlop.. you may want to upload this patch to the bug system in reference to major differences between 1.x and 2.x mod_shared_roster
https://support.process-one.net/browse/EJAB-114
--- mod_shared_roster.erl.orig 2009-02-11 11:46:49.000000000 -0500+++ mod_shared_roster.erl 2009-02-12 10:12:17.000000000 -0500
@@ -154,13 +154,29 @@
SRItems = [#roster{usj = {U, S, {U1, S1, ""}},
us = US,
jid = {U1, S1, ""},
- name = "",
+ name = get_rosteritem_name(U1, S1),
subscription = both,
ask = none,
groups = GroupNames} ||
{{U1, S1}, GroupNames} <- dict:to_list(SRUsersRest)],
SRItems ++ NewItems1.
+get_rosteritem_name(U, S) ->
+ From = jlib:make_jid("", S, mod_shared_roster),
+ To = jlib:make_jid(U, S, ""),
+ IQ = {iq,"",get,"vcard-temp","",
+ {xmlelement,"vCard",[{"xmlns","vcard-temp"}],[]}},
+ IQ_Vcard = mod_vcard_ldap:process_sm_iq(From, To, IQ),
+ case IQ_Vcard#iq.sub_el of
+ [] -> "";
+ [Vcard] ->
+ case xml:get_path_s(Vcard, [{elem, "FN"}, cdata]) of
+ "" -> xml:get_path_s(Vcard, [{elem, "NICKNAME"}, cdata]);
+ Nickname -> Nickname
+ end
+end.
+
+
%% This function rewrites the roster entries when moving or renaming
%% them in the user contact list.
process_item(RosterItem, Host) ->
Enjoy!
I don't understand what you explain.
I don't understand what you explain.
badlop.. you may want to upload this patch to the bug system in reference to major differences between 1.x and 2.x mod_shared_roster
https://support.process-one.net/browse/EJAB-114
The patch that you show here is almost the same than was previously posted in https://support.process-one.net/browse/EJAB-114
The only difference is that this once calls mod_vcard_ldap:process_sm_iq to get the stored Vcard.
I have looked around and put all the pieces together. This has been tested and works as expected using the Full Name as the Roster item name for the jid. No additional modules are needed for LDAP VCard information to populate Shared Rosters..
Do you mean mod_shared_roster_ldap is not required at all, for anything, in ejabberd 2.0.3?
The file doesn't seem to be
The file doesn't seem to be available anymore, nor was there any comment on how to implement it. Is this still being developed? The perl script put's all users into one group so that's only "OK" for small environments.
Reuploaded, check local link
The file doesn't seem to be available anymore, nor was there any comment on how to implement it.
I found the file in Google cache and have uploaded to this site. Check the download link now.
I think it isn't actively developed, or documented. It seems that the module is enabled in ejabberd.cfg, 'modules' section, as usual:
{modules, [ {mod_shared_roster_ldap, [ {ldap_base, ""}, {ldap_groupattr, "cn"}, {ldap_filter, "(uniqueMember=*)} ]}, ... ]}.Check the comments for more examples.
updateGroups.pl
Due to problems with mod_shared_roster_ldap I ended up writing a simple perl script to copy our whole directory of users into the mnesia database for mod_shared_roster. Perhaps it will be of use to others. Its very simple (one huge contact list) but it seems to work fine, though I would think twice about this approach with a really large directory.
Compatibility
#!/usr/bin/perl -wuse strict;
use Net::LDAP;
#####START OF CONFIGURABLE OPTIONS#####
use constant LDAP_SERVER => "ldap.example.co.uk";
use constant LDAP_BIND => "";
use constant LDAP_PASSWD => "";
use constant USER_BASE => "ou=users,dc=example,dc=co,dc=uk";
use constant USER_FILTER => "(accountStatus=active)";
use constant LDAP_ATTRIB => "mail";
use constant GROUP_NAME => "example";
use constant GROUP_DESC => "Example Shared Roster";
use constant SERVER_NAME => "example.co.uk";
use constant DEBUG_LDAP => 0;
use constant QUERY_TIMEOUT => 30;
#####END OF CONFIGURABLE OPTIONS#####
warn("LDAP: Connecting to ".LDAP_SERVER) if(DEBUG_LDAP);
my $ldap=Net::LDAP->new(LDAP_SERVER, onerror => undef, async => 0 );
if(!$ldap)
{
warn "LDAP: Error connecting to ".LDAP_SERVER.": $@";
return;
}
my $result;
warn("LDAP: Binding as ".LDAP_BIND) if(DEBUG_LDAP);
if(LDAP_BIND eq "")
{
$result=$ldap->bind;
}
elsif(LDAP_PASSWD eq "")
{
$result=$ldap->bind(LDAP_BIND);
}
else
{
$result=$ldap->bind(LDAP_BIND, password => LDAP_PASSWD);
}
if($result->code())
{
warn("LDAP: Error binding: [user: ".LDAP_BIND."]: ".$result->error()) if(DEBUG_LDAP);
return;
}
warn("LDAP: Success binding") if(DEBUG_LDAP);
system("ejabberdctl", "srg-delete", GROUP_NAME, SERVER_NAME);
system("ejabberdctl", "srg-create", GROUP_NAME, SERVER_NAME, GROUP_NAME, GROUP_DESC, GROUP_NAME);
#Used to ensure cleanup of server connection
LDAP_BLOCK:
{
my $result=$ldap->search
(
base => USER_BASE,
filter=> USER_FILTER,
time=> QUERY_TIMEOUT);
if($result->code())
{
warn "Search error: ".$result->error()
if(DEBUG_LDAP);
last LDAP_BLOCK;
}
my $count=$result->count();
warn "LDAP: User count: ".$count
if(DEBUG_LDAP);
NAME_LOOP: for(my $i=0; $i<$count; $i++)
{
my $entry=$result->entry($i);
if(!$entry)
{
warn("LDAP: Failed to get record ".$entry." data");
last LDAP_BLOCK;
}
my $name=$entry->get_value(LDAP_ATTRIB);
if( (!$name) or (!length($name)))
{
warn "LDAP: Skipped record $i"
if(DEBUG_LDAP);
next NAME_LOOP;
}
warn "LDAP: Adding $name"
if(DEBUG_LDAP);
my @sname=split '@', $name;
if( (scalar(@sname)!=2) || (!length($sname[0])) || (!length($sname[1])) )
{
warn "LDAP: Skipped record $i due to bad name: $name";
next NAME_LOOP;
}
system("ejabberdctl", "srg-user-add", $sname[0], $sname[1], GROUP_NAME, SERVER_NAME);
}
}
$ldap->unbind;
Documentation of schema
Hello,
I'm using ejabberd with an openldap serveur, I can authenticate users, get vcard_ldap and now I want to manage my shared rosters in LDAP.
- I have groupofUniqueNames in the ou=groups,dc=mydomaine,dc=tld.
- Each group has several uniqueMember with dn of users.
- Each users can be in more than one group.
I didn't manage to get mod_shared_roster_ldap working and I wonder if it's possible to have some documentation about how it works.
Regards.
No LDAP request
Hello,
I'm back, with more tests, I see in wireshark that no LDAP request is done when a user connect to the ejabberd server :-/, except for authentication.
My configuration:
{mod_shared_roster_ldap,[{ldap_base, "<mybase>"},
{ldap_groupattr, "cn"},
{ldap_groupdesc, "cn"},
{ldap_memberattr, "uniqueMember"},
{ldap_memberattr_format, "uid=%u*"},
{ldap_filter, "(uniqueMember=*)}
]},
My groups look like:
ou=TECH,ou=groups,<mybase>objectClass: top
objectClass: groupOfUniqueNames
cn: TECH
uniqueMember: uid=me,ou=People,<mybase>
uniqueMember: uid=boss,ou=Pople,<mybase>
Any idea how to debug an ejabberd module ?
Regards.
Example of debugging prints
Any idea how to debug an ejabberd module ?
The first thing is to look at ejabberd.log and sasl.log, not only when a user tries to connect but also when ejabberd is started, because maybe they show some info.
You can add your own debugging messages, for example try this:
start(Host, Opts) -> io:format("~n--- Hey, we are executing 'start'. ~n", []), io:format("~n the value of host is: ~p ~n", [Host]), Proc = gen_mod:get_module_proc(Host, ?MODULE), ChildSpec = { Proc, {?MODULE, start_link, [Host, Opts]}, permanent, 1000, worker, [?MODULE] }, supervisor:start_child(ejabberd_sup, ChildSpec).The messages are logged in ejabberd.log.
No logs
The first thing is to look at ejabberd.log and sasl.log, not only when a user tries to connect but also when ejabberd is started, because maybe they show some info.
That's the problem, nothing strange in sasl.log when ejabberd starts:
=PROGRESS REPORT==== 18-Feb-2008::11:31:54 === supervisor: {local,ejabberd_sup} started: [{pid,<0.208.0>}, {name,'mod_shared_roster_ldap_tatooine.example.com'}, {mfa, {mod_shared_roster_ldap, start_link, ["tatooine.example.com", [{ldap_memberattr,"uniqueMember"}, {ldap_memberattr_format,"uid=%u*"}, {ldap_filter,"(uniqueMember=*)"}]]}}, {restart_type,permanent}, {shutdown,1000}, {child_type,worker}]Nothing more in ejabberd.log, even when I added the log message you give me.
I wonder why I don't see any LDAP request in wireshark, to search my group membership when I log in.
Even If my configuration about attributes to seach is wrong, I should see some requests, and the only ones I see are authentication requests and vcard_ldap.
Regards.
additional functionality required
This module assume that additional OU like JabberUsers must be created in AD. Also one user can't be in more than one group (may be have more records one user?).
I think, better will use existent groups and users and apply on it skip filter (for groups and users).
Even better for avoid AD massive usage create another shared_roster module, it will read on user connection external file with shared rosters (like other servers do).
For jabberd2+mysql I created python-script. It sync rosters from AD to jabberd2 (without deletion deleted users).
#!/usr/bin/python# -*- coding: utf8 -*-
import os, ldap, MySQLdb
ignore = ('all', 'stats', 'controller', 'commerce', 'abonent', 'check_engine', 'technics', 'ao', 'info', 'resume')
bind_user = "CN=jabberd2,DC=example,DC=org"
bind_password = "thepassword"
base = "OU=Office,DC=example,DC=org"
l = ldap.initialize("ldap://AD.AD.AD.AD:389")
l.simple_bind_s(bind_user, bind_password)
persons = {}
for dn, attr in l.search_s(base, ldap.SCOPE_SUBTREE, filterstr='(objectClass=person)', attrlist=('userPrincipalName', 'displayName')):
if attr.has_key('userPrincipalName'):
persons[dn] = (attr['userPrincipalName'][0], attr['displayName'][0])
groups = []
for dn,attr in l.search_s(base, ldap.SCOPE_SUBTREE, filterstr='(objectClass=group)', attrlist=('cn', 'member', 'description')):
members = []
if attr.has_key('description'):
for member in attr['member']:
if (persons.has_key(member)) and (persons[member][0].strip() != ''):
members.append(persons[member])
if (len(members) > 0) and (attr['cn'][0].lower() not in ignore):
groups.append( (attr['cn'][0], attr['description'][0], members) )
con = MySQLdb.connect(host='localhost',user='jabberd2',passwd='thepassword',db='jabberd2')
c = con.cursor()
c.execute('SET NAMES UTF8')
groups.sort(cmp=lambda x,y: cmp(x[1].lower(), y[1].lower()))
rg = {}
c.execute('SELECT `collection-owner`, `jid`, `group` FROM `roster-groups`')
for row in c:
if not rg.has_key(row[0]):
rg[row[0]] = {row[1]: [row[2]]}
else:
if not rg[row[0]].has_key(row[1]):
rg[row[0]][row[1]] = [row[2]]
else:
rg[row[0]][row[1]].append(row[2])
ri = {}
c.execute('SELECT `collection-owner`, `jid` FROM `roster-items`')
for row in c:
if not ri.has_key(row[0]):
ri[row[0]] = {row[1]: 1}
else:
ri[row[0]][row[1]] = 1
cg, ci = 0, 0
for p in persons.itervalues():
for g in groups:
for member in g[2]:
if p[0] != member[0]:
if not (rg.has_key(p[0]) and rg[p[0]].has_key(member[0]) and (g[1] in rg[p[0]][member[0]])):
c.execute('INSERT INTO `roster-groups` (`collection-owner`, `jid`, `group`) VALUES (%s, %s, %s)', (p[0], member[0], g[1]))
cg += 1
if not (ri.has_key(p[0]) and ri[p[0]].has_key(member[0])):
c.execute('INSERT INTO `roster-items` (`collection-owner`, `jid`, `name`, `to`, `from`, `ask`) VALUES (%s, %s, %s, 1, 1, 0)', (p[0], member[0], member[1]))
if not ri.has_key(p[0]):
ri[p[0]] = {member[0]: 1}
else:
ri[p[0]][member[0]] = 1
ci += 1
con.close()
print 'added groups=%d items=%d'%(cg, ci)
I'm not familar with erlang and can't do the task. But I can save instead of MySQL rosters in any format (ex: erlang native lists).
mod_shared_roster_ldap configuration options
I followed tips from
http://www.ejabberd.im/compile-module-windows
to get mod_shared_roster_ldap into my from-binary linux ejabberd installation. So now it's at least into my installation, but I also don't yet have it working.
There seems to be a question of how to do configuration. I also can't read Russian, but I can read code.
Here's what I was able to deduce from reading mod_shared_ldap.erl, specifically, the section that starts with "parse_options". Once you get to that part of the code, try searching for "get_local_option". The options have rather descriptive names, like "ldap_servers", and "ldap_port". Here's the list as I was able to discern from the code...
ldap_servers
ldap_port
ldap_base
ldap_memberattr
ldap_memberattr_format
ldap_rootdn
ldap_password
ldap_filter
ldap_rfilter
I'm still playing with getting this working in my environment. I'll post results and my configuration settings, and of course whether it's working for me or not. So far, the answer is no, but I have more guesses to try.
Try this (in
Try this (in Russian)
http://realloc.spb.ru/share/ejabberd112ad.html
Automaticaly translated by Google:
http://translate.google.com/translate?u=http://realloc.spb.ru/share/ejabberd112ad.html&hl=en&langpair=ru|en&tbb=1&ie=UTF-8
config
Here is my config file :
{mod_shared_roster_ldap,[ {ldap_servers, ["LDAP_SERVER"]},
{ldap_rootdn, "ROOTDN"},
{ldap_password, "PASSWD"} ]
}.
Now, I'm wondering how to configure it ;-)