How to map LDAP Users and Groups to Confluence via Atlassian User

This configuration is not supported. Please see USER-105.

Instructions here relate to use of the command line 'ldapsearch' utility to check that mappings between Atlassian User and your LDAP system are correct.

Mappings between LDAP and Atlassian User.

Here is an explanation of each property in the atlassian-user.xml file.

Property Key Example Value Description Link
baseUserNamespace ou=users This namespace should identify your DIT's (Directory Information Tree) base context holding user accounts. This is absolute, not relative to baseContext. see below
baseGroupNamespace ou=groups This namespace should identify your DIT's base context holding groups. This is absolute, not relative to baseContext. see below
userSearchAllDepths false true to search all subtrees beneath baseUserNamespace, false to search one level beneath it only see below
groupSearchAllDepths false true to search all subtrees beneath baseGroupNamespace, false to search one level beneath it only see below
usernameAttribute uid This attribute should identify the RDN (Relative Distinguished Name) of the user entry see below
groupnameAttribute gid This attribute should identify the RDN of the group entry see below
firstnameAttribute givenname This attribute should identify the first name of the user see below
surnameAttribute sn This attribute should identify the surname of the user see below
emailAttribute mail This attribute should identify the email address of the user see below
membershipAttribute atlassianGroupMemberOf This attribute should identify which groups the user belongs to. see below
userSearchFilter (objectClass=inetorgperson) This property represents an additional search filter ANDed to each query for a user (see RFC 2254) see below
groupSearchFilter (objectClass=group) This property represents an additional search filter ANDed to each query for a group (ibid) see below

Also read below to learn how to specify a group adaptor, LDAP user and group factories, and a LDAP user searcher.

Converting Special Characters

There are 5 special characters that you must escape when saving search filters to the atlassian-user.xml file.

LDAP Character XML Equivalent Description
< &lt; less than
> &gt; greater than
& &amp; ampersand
' &apos; apostrophe
" &quot; quotation mark

User Mappings

1. Identifying the baseUserNamespace and the userSearchAllDepths mappings

The baseUserNamespace indicates the top level of a tree in your LDAP system which holds your users. The userSearchAllDepths property can either be set to true or false. True indicates that only the level directly beneath the baseUserNamespace should be searched, false indicates that all additional sub-levels of the tree should be searched.

For example, if you wanted to include users beneath 'cn=contractors,cn=employees,cn=acme,cn=org' and users beneath 'cn=permanent,cn=employees,cn=acme,cn=org' in an Atlassian application you would specify a baseUserNamespace of 'cn=employees,cn=acme,cn=org' with a userSearchAllDepths value of 'true'. Otherwise, if all users are grouped in one level of your tree, set userSearchAllDepths to false.

Searching for all users with search depth scope of entire subtree
[nickf@little-creatures dls]$ ldapsearch -h 'localhost' -p 389 -D 'cn=admin,dc=atlassian,dc=private' -x -w secret -b 'ou=people,dc=atlassian,dc=private' -s sub

# extended LDIF
#
# LDAPv3
# base <ou=people,dc=atlassian,dc=private> with scope sub
# filter: (objectclass=*)
# requesting: ALL
#

# people, atlassian.private
dn: ou=people,dc=atlassian,dc=private
objectClass: top
objectClass: organizationalUnit
ou: people

... etc.

Would lead to the following values for baseUserNamespace , userSearchAllDepths, and usernameAttribute:

baseUserNamespace=cn=employees,cn=acme,cn=org
userSearchAllDepths=true
Searching for all users with search depth scope of one
[nickf@little-creatures dls]$ ldapsearch -h 'localhost' -p 389 -D 'cn=admin,dc=atlassian,dc=private' -x -w secret -b 'ou=people,dc=atlassian,dc=private' -s one

# extended LDIF
#
# LDAPv3
# base <ou=people,dc=atlassian,dc=private> with scope one
# filter: (objectclass=*)
# requesting: ALL
#

# ldapusera, people, atlassian.private
dn: cn=ldapusera,ou=people,dc=atlassian,dc=private
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: ldapusera
sn: ldapuseraSN
givenName: ldapuseraGN
mail: ldapusera@testingarea.org
userPassword:: cGFzc2E=

... etc.

Would lead to the following values for baseUserNamespace and userSearchAllDepths:

baseUserNamespace=cn=employees,cn=acme,cn=org
userSearchAllDepths=false

Here, also, the usernameAttribute property is identified as being 'cn'. The property mapping therefore becomes

usernameAttribute=cn

2. Identifying the userSearchFilter

The userSearchFilter allows for an extra condition to be placed on search results. This is useful if a search on the baseUserNamespace is returning results which will not equate to User objects. Use it to specify a search criteria which will identify users.

[nickf@little-creatures dls]$ ldapsearch -h 'localhost' -p 389 -D 'cn=admin,dc=atlassian,dc=private' -x -w secret -b 'ou=people,dc=atlassian,dc=private'
 -s sub '(objectClass=inetorgperson)'

# extended LDIF
#
# LDAPv3
# base <ou=people,dc=atlassian,dc=private> with scope sub
# filter: (objectClass=inetorgperson)
# requesting: ALL
#

# ldapusera, people, atlassian.private
dn: cn=ldapusera,ou=people,dc=atlassian,dc=private
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: ldapusera
sn: ldapuseraSN
givenName: ldapuseraGN
mail: ldapusera@testingarea.org
userPassword:: cGFzc2E=

Here, we specify userSearchFilter as '(objectClass=inetorgperson)'. That is, we are saying our users are identified by an objectClass of inetorgperson (given our search result above, we could have also used organizationalPerson or person). Please note that inetorgperson is an LDAP objectClass. It does not exist in Active Directory. You will need to specify a different class if using AD.

3. Identifying the firstnameAttribute, surnameAttribute, and emailAttribute

As with the identification of username property search above, similar mappings can be found for firstnameAttribute, surnameAttribute, and emailAttribute.

firstnameAttribute=givenname
surnameAttribute=sn
emailAttribute=mail

Group Related Mappings

4. Identifying the groupnameAttribute and the baseGroupNamespace mappings

The tactic here is similar identifying the baseUserNamespace and userSearchAllDepths attributes.

It is assumed that groups are entries in their own right within your LDAP system. If groups exist by virtue of being listed against user entries, or in some other way, please raise a support request to discuss an integration plan.

The baseGroupNamespace indicates the top level of a tree in your LDAP system which holds groups. As with userSearchAllDepths, setting groupSearchAllDepths to true instructs the search function to traverse all available subtrees beneath the baseGroupNamespace. Set it to false if all of your groups are contained in the level indicated by baseGroupNamespace.

Static vs Dynamic Groups

Groups are generally one of two types in LDAP systems - static or dynamic. A static group maintains its own membership list. A dynamic group records its membership on a user entry.

Here is an example of a search on an OpenLDAP DIT (Data Information Tree), which stores static groups.

Example of a ldapsearch on baseGroupNamespace of static groups
[nickf@little-creatures ~]$ ldapsearch -h 'localhost' -p 389 -D 'cn=admin,dc=atlassian,dc=private' -x -w secret -b 'ou=groups,dc=atlassian,dc=private' -s sub
# extended LDIF
#
# LDAPv3
# base <ou=groups,dc=atlassian,dc=private> with scope sub
# filter: (objectclass=*)
# requesting: ALL
#

# groups, atlassian.private
dn: ou=groups,dc=atlassian,dc=private
objectClass: top
objectClass: organizationalUnit
ou: groups

# groupA, groups, atlassian.private
dn: cn=groupA,ou=groups,dc=atlassian,dc=private
objectClass: top
objectClass: groupOfNames
cn: groupA
member: cn=ldapusera,ou=people,dc=atlassian,dc=private
member: cn=ldapuserb,ou=people,dc=atlassian,dc=private

The baseGroupNamespace is identified as being 'ou=groups,dc=atlassian,dc=private', the groupSearchAllDepths is 'false', and the groupnameAttribute as being 'cn':

baseGroupNamespace=ou=groups,dc=atlassian,dc=private
groupSearchAllDepths=false
groupnameAttribute=cn

Here is an example of a search on an Apache Directory Server DIT (Data Information Tree), which stores dynamic groups.

Example of a ldapsearch on baseGroupNamespace of dynamic groups
[nickf@little-creatures user_standalone]$  ldapsearch -D 'uid=admin,ou=system' -h localhost -p 10389 -x -w secret -s one -b 'ou=groups,ou=system'
'(objectclass=group)'
# extended LDIF
#
# LDAPv3
# base <ou=groups,ou=system> with scope one
# filter: (objectclass=group)
# requesting: ALL
#

# groupA, groups, system
dn: uid=groupA,ou=groups,ou=system
ou: Teams
ou: Human Resources
cn: groupA
objectclass: group
objectclass: top
uid: groupA
l: groupA

Here the baseGroupNamespace is 'ou=groups', groupNameAttribute is 'cn', and groupSearchFilter is false.

baseGroupNamespace=ou=groups
groupnameAttribute=uid
groupSearchAllDepths=false

You will notice that for the baseGroupNamespace I drop the full path: <ou=groups,ou=system> . This is just for convenience with ApacheDS. Whereas OpenLDAP requires the baseGroupNamespace mapping to be the full DN of the node, ApacheDS uses the baseContext property specified in connection.properties to complete it.

5. Identifying the groupSearchFilter filter.

As with the userSearchFilter property, the groupSearchFilter allows you to specify an added search criteria to all search results. This is useful if your baseGroupNamespace holds data which cannot be modelled into a Group alongside data which can be.

Please see the discussion on userSearchFilter for an example of how to build this property.

The default value for this filter is (objectClass=groupOfNames). If you are using Active Directory, please change it to (objectClass=group) or select another objectClass that exists in your instance

Membership Mappings

6. Identifying a membership attribute

There are two requirements for membership mappings to work.

  1. The membership value must carry the DN of the user or group, for static and dynamic groups respectively.
  2. The search for membership is constructed by using the username or groupname, for static or dynamic groups, attribute and attribute value, along with the the baseContext. If these values do not match membership will not work.

The membershipAttribute property can be identified via the same search which identified the baseGroupNamespace . On a static group, an attribute will be present to identify a membership list.

For example, in the search result below, the membershipAttribute is 'member'.

# groupA, groups, atlassian.private
dn: cn=groupA,ou=groups,dc=atlassian,dc=private
objectClass: top
objectClass: groupOfNames
cn: groupA
member: cn=ldapusera,ou=people,dc=atlassian,dc=private
member: cn=ldapuserb,ou=people,dc=atlassian,dc=private
membershipAttribute=member

Whereas, in the example for dynamic above, the membershipAttribute is not on the group but, instead, on the user:

Search result showing dynamic group membership
[nickf@little-creatures ~]$ ldapsearch -h 'localhost' -p 10389 -D 'uid=admin,ou=system' -x -w secret -b 'ou=users,ou=system' -s sub
# extended LDIF
#
# LDAPv3
# base <ou=users,ou=system> with scope sub
# filter: (objectclass=*)
# requesting: ALL
#

# users, system
dn: ou=users,ou=system
ou: users
objectClass: organizationalUnit
objectClass: top

# ldapusera, users, system
dn: uid=ldapusera,ou=users,ou=system
sn: Surname1
ou: People
ou: Human Resources
cn: ldapusera
objectclass: atlassianPerson
objectclass: inetOrgPerson
objectclass: organizationalPerson
objectclass: person
objectclass: top
uid: ldapusera
userpassword:: cGFzc2E=
givenname: ldapusera
mail: ldapusera@testingarea.org
member: cn=groupA,ou=groups,ou=system
l: Atlassian test subject

... etc.
membershipAttribute=atlassianGroupMemberOf

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Feb 08, 2006

    Amos Gouaux says:

    Is there any significant performance differences between static and dynamic grou...

    Is there any significant performance differences between static and dynamic groups?

    1. Feb 17, 2006

      Andrew Miller says:

      As far as Confluence is concerned, it should just be an LDAP query so performanc...

      As far as Confluence is concerned, it should just be an LDAP query so performance would really be dependent on your LDAP directory (some directories are faster with static groups than dynamic groups).

  2. May 24, 2006

    YL says:

    Does this LDAP integration support nested group? I guess I need t...

    Does this LDAP integration support nested group? I guess I need to set groupSearchAllDepths to true? If I have group AB nested under group A, will member in group AB be recognized as a member of group A?

    1. Aug 10, 2006

      David Soul [Atlassian] says:

      Sorry, nested groups are not supported, so group AB is not recognised as a membe...

      Sorry, nested groups are not supported, so group AB is not recognised as a member of A. You'll need to set groupSearchAllDepths to true.

  3. May 17, 2007

    Michael Redinger says:

    This document explains in detail the difference between static and dynamic group...

    This document explains in detail the difference between static and dynamic groups.

    However, Add LDAP Integration says that static groups are needed.

    I found a JIRA issue which basically says that only static groups are suppported, but that dynamic groups should work somehow, too: http://jira.atlassian.com/browse/USER-105

    So: Do dynamic groups work, although there seems to be no documentation except this page? And if yes, could someone give a quick example on how to use them?

    1. Oct 22, 2007

      John Allen says:

      Yes dynamic groups work

      Yes dynamic groups work

  4. Aug 16, 2007

    David Peterson [CustomWare] says:

    Wow. This page is really hard to digest. In particular, it is really hard to und...

    Wow. This page is really hard to digest. In particular, it is really hard to understand how to get Confluence to use dynamic groups. Could the page be rearranged so that the static group instructions are together, and the dynamic group instructions are together. At the moment they are spread all over the place.

    Also, it is really unclear how to specify that it should be getting group membership from the user (aka dynamic groups), as opposed to the group. The only clue above is the 'membershipAttribute=atlassianGroupMemberOf' entry above, which doesn't seem to relate to the example above it at all. What is this mysterious 'atlassianGroupMemberOf'? Is it a special attribute name which tells Atlassian User that it should use the user's 'memberOf' attribute to determine group membership? Or something else?

  5. Nov 16, 2007

    senthilraja says:

    Hi,  We have a requirement, where we have multiples of DL groups nested in...

    Hi,

     We have a requirement, where we have multiples of DL groups nested in multiple order.

    When we assign group permissions to a space, we require that even if the users belong to one of the sub-level groups, should be authenticated.

    Will groupSearchAllDepths solve the problem?

  6. Feb 08, 2008

    John Allen says:

    Are there any plans for adding support for pulling XMPP ids out of LDAP? Bamboo ...

    Are there any plans for adding support for pulling XMPP ids out of LDAP? Bamboo would obviouslybenefit

  7. Mar 28, 2008

    Nick Campbell says:

    I have a couple of questions. The first is, does this support multiple group ty...

    I have a couple of questions.

    The first is, does this support multiple group types in the same system? If so, how do we specify in the xml that groups can be dynamic or static?

    My next question is with the dynamic groups. We have a few of them. Our dynamic groups are done with an actual search using the ldap protocol "ldap://<server>...(objectclass=*)". While thats an example, it's similar to how we have dynamic groups. Does confluence handle this kind of dynamic group setup or do the users need to have a specific attribute on their object where the values are their groups?

    Let me know, thanks!

    - Nick 

  8. Dec 05, 2008

    Stephen Edwards says:

    So, this document is not very good, but I did manage to figure out how to make d...

    So, this document is not very good, but I did manage to figure out how to make dynamic LDAP groups work. (Using 2.9.2 Standalone).

    First of all as http://jira.atlassian.com/browse/USER-105 explains, you need to add a class to your atlassian-user.xml. So, inside of the <ldap> tags add this:

    <classes> 
        <groupAdaptor>com.atlassian.user.impl.ldap.adaptor.LDAPDynamicGroupAdaptor</groupAdaptor> 
    </classes>

    Next, the example above of a user query in a dynamic group LDAP is wrong. Here's what the query results should look like:

    # extended LDIF
    #
    # LDAPv3
    # base <cn=sedward5,ou=users,o=campus> with scope sub
    # filter: (objectclass=*)
    # requesting: ALL
    #
    
    # sedward5, people, campus
    dn: cn=sedward5,ou=people,o=campus
    sn: Edwards
    uid: sedward5
    givenName: Stephen
    cn: sedward5
    objectClass: inetOrgPerson
    objectClass: posixAccount
    objectClass: organizationalPerson
    objectClass: Person
    objectClass: Top
    mail: email@email.com
    groupMembership: cn=helpdesk,ou=rbsgroups,o=campus  ## THIS IS THE IMPORTANT PART
    

    This means that in atlassian-user.xml you want:

    <baseGroupNamespace>ou=rbsgroups,o=campus</baseGroupNamespace>
    <membershipAttribute>groupMembership</membershipAttribute>

    In this example my user account would then be recognized as a member of the helpdesk rbsgroup.
    One other thing that I ran into was the groupSearchFilter, if you are unsure on this one set it to:

    <groupSearchFilter>(objectClass=*)</groupSearchFilter>

    I hope that helps clarify.
    -Steve