Windows AD mapping groups (previously used Openfire)

I'm trying to get re-configured with ejabberd2.1.8 (installed from binary to /opt/ejabberd2.1.8/); replacing an openfire 3.6.4 cluster. Long story short, it seems the clustering isn't transmitting presence information like it should, and users are complaining that others aren't online, when in fact they are.

So in comes ejabberd, I have a basic config up and running, users are able to auth through AD and now comes the problem of shared rosters (which is why I chose openfire in the first place). Openfire made it easy with a GUI... you go into the groups area, select the groups you want, and make them 'public', then they show in the roster. Subsequently when new users are added to the groups, the roster is automatically updated.

The groups are all prefixed with 'Jabber-' and our MIS team knows to add them to the relevent department. I'm trying to get this same sort of feel in ejabberd but I'm having a hard time. Not to mention, I'd like to be able to filter to these groups only, and display them AS groups, not just the users from those groups.

For example, group 'Jabber-Support' shows in our roster as 'Support', and this is globally true for all groups. How can I setup the filters in ejabberd to have the same thing? Is there something I can set in the AD to perhaps filter by as an additional info item, or some other trick? Help would be appreciated. Here's an example of the current roster filter and setup:

  {mod_shared_roster_ldap,[
        {ldap_base,             "ou=ExampleORG,dc=exampleorg,dc=com"},
        {ldap_groupattr,        "cn"},
        {ldap_groupdesc,        "description"},
        {ldap_memberattr,       "memberUid"},
        {ldap_memberattr_format, "%u"},
        {ldap_useruid,          "cn"},
        {ldap_userdesc,         "displayName"},
        {ldap_rfilter,          "(objectClass=*)"},
        {ldap_gfilter,          "(objectClass=group)"},
        {ldap_ufilter,          "(objectClass=organizationalPerson)"},
        {ldap_filter,           ""}
  ]},

Seems I'm only getting a roster of 27 people in PSI when testing... and that roster changes every time I log in... really odd. Thanks for the help.

OK, well, I seem to be taking

OK, well, I seem to be taking a few steps backwards, so here's how I understand our AD infrastruct:

There is an OU Jabber within our company OU, so 'OU=Jabber,OU=Company,DC=example,DC=com'. I'm trying to get the groups and users referenced in that OU to show up, but I'm having difficulty with the filters. I've tried several revisions, here's my current revision; can someone help with what is wrong?

  {mod_shared_roster_ldap,[
%%      {ldap_base,             "OU=Jabber,OU=Company,DC=example,DC=com"},
%%      {ldap_base,             "OU=Company,DC=example,DC=com"},
        {ldap_base,             "DC=example,DC=com"},
        {ldap_groupattr,        "cn"},
        {ldap_groupdesc,        "displayName"},
%%      {ldap_memberattr,       "memberUid"},
        {ldap_memberattr,       "member"},
        {ldap_memberattr_format_re, "cn=(.*),ou=Jabber,ou=Company,dc=example,dc=com"},
%%      {ldap_memberattr_format_re, "cn=(.*),ou=(.*),ou=Company,dc=example,dc=com"},
        {ldap_useruid,          "cn"},
        {ldap_userdesc,         "displayName"},
        {ldap_rfilter,          "(objectClass=group)"},
        {ldap_gfilter,          "(&(objectClass=group)(cn=%g)"},
        {ldap_ufilter,          "(&(objectClass=person)(cn=(.*)))"},
        {ldap_filter,           ""}
  ]},

This 'Jabber' OU is just a container. The actual users are members of a few different OU's, under the 'OU=Company,DC=example,DC=com' tree.
I've based this filter off of a post: http://www.ejabberd.im/node/4722; as you can see I've tried a few different methods but I'm still turning up empty.

Help?

One step forward. The

One step forward. The following shows a flat list of 27 of my users... but definitely not all of them:

  {mod_shared_roster_ldap,[
        {ldap_base, "ou=Company,dc=example,dc=com"},
        {ldap_groupattr,        "cn"},
        {ldap_groupdesc,        "description"},
        {ldap_memberattr,       "memberUid"},
        {ldap_rfilter,          "(objectClass=*)"},
%%      {ldap_gfilter,          "(&(objectClass=group)(cn=Jabber-*))"},
        {ldap_ufilter,          "(uid=%u)"},
%%      {ldap_ufilter,          "(&(objectCategory=user)(memberOf=*))"},
        {ldap_useruid,          "uid"},
        {ldap_userdesc,         "displayName"},
        {ldap_filter,           ""}
  ]},

Now, when using ldapsearch, and I filter with '(&(objectClass=group)(cn=Jabber-*))' I get the groups in my OU for the Jabber user groups; they're presently prefixed with 'Jabber-' since I can re-label them in Openfire. When I add this filter in, my incomplete roster disappears. I'm also wondering why I'm getting a roster of 27 users, instead of my org which has over 200 users. When filtering my AD with '(&(objectCategory=user)(memberOf=*))' I get all my users.

First, I must say that I

First, I must say that I configured my AD shared roster some time ago using an archaic version (before porridge took over the maintanence of this mod). The things I describe in http://www.ejabberd.im/mod_shared_roster_ldap#comment-55054 and later comments may not be up-to-date, but I'm afraid they're not.

Unfortunately, the mod seems to incorrectly treat multi-valued attributes in the AD (the memberUid being one of those attributes). In the AD, the memberUid attribute of a group contains multiple [u]LDAP names[/u] of users belonging to that group (this data may not contain the information needed to construct the Jabber ID, and it may contain spaces). The user object doesn't contain an attribute indicating the groups it belongs to. In the OpenLdap world, it's common to take an alternate approach - that is, to include this information into the user object.

I had to implement a workaround - I use the "department" attribute to be the group. Thus, I have to manually populate this attribute for every user, and check that I don't make mistakes.
By the way, my full config is referenced from the abovementioned page (it's on http://www.ejabberd.im/node/3661#comment-54984).

mikekaganski

mikekaganski wrote:

Unfortunately, the mod seems to incorrectly treat multi-valued attributes in the AD (the memberUid being one of those attributes). In the AD, the memberUid attribute of a group contains multiple [u]LDAP names[/u] of users belonging to that group (this data may not contain the information needed to construct the Jabber ID, and it may contain spaces). The user object doesn't contain an attribute indicating the groups it belongs to. In the OpenLdap world, it's common to take an alternate approach - that is, to include this information into the user object.

Thanks for the reply... I was beginning to think no one exists here.

So, from what I gather in your reply and the thread you've linked is that I need a single attribute to filter on for groups and user objects? That seems a little chaotic considering the current revisions of the plugin should understand that group and user objects will differ ... not even CAN, but most likely WILL.

So I can see during some recent probing that the groups i want have element 'member', while the groups that I can get have memberUid. member has spaces, usually a 'firstName sn' format, with a matching OU=... blah blah blah how ever deep the OU goes.

I guess ultimately... if I want to use ejabberd, I'd have to write my own AD aware ldap shared roster... which is going to be rather cumbersome since my openfire installation works, i just need to get it tuned or find some better situation. This is quite an upsetting issue, since I would think this sort of need could be fulfilled by a seasoned server ... and what appeared to be a very seasoned plugin.

So... i'm wondering if I have my filters all wrong then.

My needs -
The 'Jabber' OU has my groups, they're CN is 'Jabber-*', each Jabber-* CN has the users I want listed under 'member' attribute, which each member has a varying length OU, but otherwise in the regex format (i believe) is 'CN=cn,(OU=*)*(,)OU=Company,DC=Example,DC=com.

example.com
--Company
----Billing
------Joe
------Steve
----Sales
------Billy
------Guy
----External
------Team1
--------User1
--------User2
------Team2
--------User4
--------User5
----Jabber
------Jabber-Group1
------Jabber-Group2

So we have users spread out pretty sparsely; but the groups are all in one contained OU. Isn't there an easier way to just get the groups, and therefore display the CN's of the referenced 'member' of that group? Openfire is pretty explicit with this .. select the group, mark it as shared for the roster, the group has the userlist (it is displayed as sAMAccountName in the portal, but shows on the roster as the CN due to openfire configurations).

The real hitch here is we need clustering/redundancy - this is why ejabberd is coming into play.

I think that you are lucky

I think that you are lucky that you have your CN part of users to match the user part of the jid. I, for instance, have mine as "CN=Mike Kaganski,OU=....,DC=example,DC=com" (note the space between Mike and Kaganski). On my side, I would need to ask LDAP for one additional attribute, in my case it would be sAMAccountName.

I suppose that what you need is proper ldap_memberattr_format_re. You may look at its description in the operation guide, but I must say that I haven't used it.

What to the single attribute to filter both on users and groups - no, if I understand you correctly. You should carefully check http://www.process-one.net/docs/ejabberd/guide_en.html#msrlconfigroster. The algorithm that is described there may shed some light.

Note that it's not possible to get users that are referenced in a group indirectly (i.e. a user is memger of a group, that is a member of our group).

mikekaganski wrote: Note that

mikekaganski wrote:

Note that it's not possible to get users that are referenced in a group indirectly (i.e. a user is memger of a group, that is a member of our group).

This is really where openfire and ejabberd separate and makes ejabberd just that much more difficult. Openfire browses the AD already, and looks for groups - map a name to the group and mark it publicly shared (thus, creating a roster). Updates to that group means updates to the roster automagically; hence why we chose it initially.

It sounds like a better approach in my case, but it also sounds like a more difficult thing to succeed with, because I'd have to create a new module (which, by the way sounds REALLY good... if there are any developers out there). Maybe you can help me a little more. I'm going to yank my ldapsearch syntax for my matching, and see if you can help me out with the rules to map; or if anyone can help me with the rules to map.

This query returns the groups I need:

ldapsearch -b ou=Company,dc=example,dc=com -v -W -h dc-XXX-XXX -D cn=MYUSER,dc=example,dc=com -Y DIGEST-MD5 '(&(objectClass=group)(cn=Jabber-*))'

This query returns the users I want (based on the groups... this is majorly hairy:

ldapsearch -b ou=Company,dc=example,dc=com -v -W -h dc-XXX-XXX -D cn=MYUSER,dc=example,dc=com -Y DIGEST-MD5 '(&(objectCategory=user)(|(memberOf=CN=Jabber-Department,OU=Jabber,OU=Company,DC=example,DC=com)(memberOf=CN=Jabber-Department Two,OU=Jabber,OU=Jabber,OU=Company,DC=example,DC=com)(...)))'

You get the idea... and yes this filter is VERY long, but hey, this feeds me only the users in these groups (what I want). Do note that the 'CN' of the groups listed may have spaces... ldapsearch handles these fine because of the encapsulated single quote and matches the CN object.

Is there a way to get this to work? I need the roster to show the CN's for the users, and you know... it can display Jabber-BLAH for each group... I don't care, but they need to show the users under the groups in that style. I can then work on changing the displayed group name later.

Any help?

---

You know... looking at the above, it sounds more worthwhile for me to start looking into developing a 'group based' ldap selection. I've never coded erlang, but maybe I should give it a stab using mod_shared_roster_ldap as a base.

What you describe is quite

What you describe is quite easy... until you get to the user jids.

1. The Roster Filter should be like (&(objectClass=group)(cn=Jabber-*)), and Group Filter should be like (&(objectClass=group)(cn=%g)). Here the spaces are OK. The ldap_groupattr should stay as it is by default (= "cn") (otherwise you will get troubles constructing the User Filter), ldap_groupdesc may be the default (=ldap_groupattr) or you may choose some other attribute of the group where you will store its pretty name (without Jabber- part). The ldap_memberattr must be "member", because that's the only field where the members may be found. That's the most trouble, because the "member" stores its members as DNs, thus if you have your users like "CN=John Doe,OU=blah,OU=blah,DC=example,DC=com", then we're stuck. In the better case when you have CNs of your users without spaces, you may choose to use ldap_memberattr_format_re = "CN=(\\w*),(OU=.*,)*DC=example,DC=com" (this is from the guide, I didn't test this regex).

2. The User Filter must be like "(&(objectCategory=user)(cn=%u)(memberOf=CN=%g,OU=Jabber,OU=Company,DC=example,DC=com)". After the ejabber gets all the data in the 1st stage (i.e. the list of groups, its names and their members), it will generate one query per group and in each query will substitute the "%g" with the ldap_groupattr of the current group (and that's the group's cn, as we put in the 1st stage). The ldap_userdesc should be something you choose to represent the user (like "displayName"), and the ldap_useruid should stay "cn", thus it will match the data that is taken by applying ldap_memberattr_format_re to the ldap_memberattr.

That's what I would start with first.

mikekaganski wrote: That's

mikekaganski wrote:

That's the most trouble, because the "member" stores its members as DNs, thus if you have your users like "CN=John Doe,OU=blah,OU=blah,DC=example,DC=com", then we're stuck. In the better case when you have CNs of your users without spaces, you may choose to use ldap_memberattr_format_re = "CN=(\\w*),(OU=.*,)*DC=example,DC=com" (this is from the guide, I didn't test this regex).

the CN=(\\w*) indeed matches single word only, I've tried opening that up to [^,]* (shows the same as \\w*), or .* (which shows NOTHING?? Why?); still not getting the regex match that I would anticipate, unless %u can be filtered in there, but I'm guessing that %u contains the JID portion before the domain name... thus it would match on only the members with one name, firstName (and it does).

Indeed, you are correct that the members are stored like DNs:

member: CN=firstName sn,OU=blah,OU=blaaaah,OU=more blaaaaaaaah,OU=Company,DC=example,DC=com

So pardon me... since I'm still a little confused with the whole attributes and filters process, but why can't the JID be different from the member list? sAMAccountName is definitely the JID (and is similar for Openfire), but why can't it match CN=displayName ? It looks like I'd have to dive into the source code... and not being a erlang programmer means lots of brain hurt ... but it appears this is the only way.

A sysadmin hack would be to pull the AD into an openLDAP server, and bend it to my will... oh joy (so hacky).

Thank you for the explanations, this was very helpful. Not sure if there's anything else that can be helpful here... I can't believe that this is unresolvable and that all of ProcessOne's customers don't use a Windows AD. Sadness...

I have explained the cause of

I have explained the cause of this defficiency (I mean why can't you use sAMAccountName), as well as a way to fix this deficiency, in my post to process one's bug list.

I'm a little lost in what do you want to understand: either why can't you use the sAMAccountName, or why the user part of the jid can't be taken from the CN.
And to answer the last part, you need to understand what your jids look like. I suppose that in your LDAP auth config, they are defined to be like "sAMAccountName@example.com". The XMPP standard applies restrictions to the jid structure, so the localpart (in our case, user part) can't contain the spaces. And the process that happens in the mod_shared_roster_ldap must yield the jids that sorrespond to those jids that the auth module expects.

I'm going to make a patch implementing this approach. I will need someone to help me test it.
However, you could choose to use another approach (one that I have chosen a while ago - i.e. to store the Jabber Group data inside the User object in some field).

mikekaganski wrote: I have

mikekaganski wrote:

I have explained the cause of this defficiency (I mean why can't you use sAMAccountName), as well as a way to fix this deficiency, in my post to process one's bug list.

I'm going to make a patch implementing this approach. I will need someone to help me test it.
However, you could choose to use another approach (one that I have chosen a while ago - i.e. to store the Jabber Group data inside the User object in some field).

I'm in a situation where I can help you test since our jabber domain is on another product for the time being. I know the cause has been outlined in a few posts; I also wonder if your bug isn't also the one listed in the msrl maintainer's bug: https://alioth.debian.org/tracker/index.php?func=detail&aid=312703

I'll read those posts and see what I can get back to. I've also been informed I have access to an erlang programmer; hopefully they can also be of assistance.

I attempted the patch listed

I attempted the patch listed in https://support.process-one.net/browse/EJAB-1480 which I copied from the mod_shared_roster_ldap bug and suggested fix; didn't work as expected.

I'm not sure if this is a good place for you to start or not, perhaps it will shed some light. I personally do not follow this code as well as other languages.

By the way, you said you

By the way, you said you create some special groups to hold the jabber users. They are named like "Jabber-blah-blah-blah". And you miss the flexibility of Openfire which lets you mark some groups for inclusion to the shared roster.

You could do this trick to do this (almost) as much easy in ejabberd: create a special group (say, it would be "CN=jab,OU=something,DC=example,DC=com"), and then make each group you want to appear in your shared roster to be member of this group. Then, you could specify your Roster Filter like this: (&(objectClass=group)(memberOf=CN=jab,OU=something,DC=example,DC=com)).

Though it will not let you overcome the problems with user names, and will not allow for traversing the subgroups for users, it will still let you manage groups using the AD Users and Computers tool, doesn't require a special prefix, and doesn't need many dedicated groups just for one purpose (and they can be anywhere below your root dn, no need to gather them in one OU).

So, with the two queries

So, with the two queries above... and injected into the config, what I really need are the rest of the settings required to show the right stuff for the filters. I've lost track of and am horribly confused with the ldap_(r|g|u)filter stuff now... ugh

mengesb wrote: ... member has

mengesb wrote:

... member has spaces, usually a 'firstName sn' format, with a matching OU=... blah blah blah how ever deep the OU goes...

Oh I missed that part... That's exactly the point I tried to bring the maintainer's attention for quite some time. Today I posted a message concerning this again. Possibly I need to either open the case on the mod's development page (http://ejabberd-msrl.alioth.debian.org), or open it on the process-one site, taking into account that this module is now part of the distribution (https://support.process-one.net/browse/EJAB/fixforversion/10796).

Syndicate content