Nuxeo Server

LDAP and Active Directory

Updated: March 18, 2024

In the Nuxeo Platform, users and groups are managed by directories. If you want your Nuxeo instance to use a LDAP directory you will need to:

  • configure a user directory pointing to your LDAP server(s),
  • configure a group directory pointing to your LDAP server(s) (if you need LDAP groups).

Of course you can have a specific custom config where:

  • you use a custom user / group schema,
  • you use several LDAP directories, or a mix of SQL and LDAP directories.

But for the most common use case, all you want to do is map the default userDirectory to your LDAP Server. Since groups are used in Nuxeo to associate permissions with content, fetching groups from LDAP is usually not very efficient: LDAP groups are usually not designed for that.

Configuration

The Users & Groups step of the startup wizard enables you to set up your LDAP, SQL or multidirectory configuration: Select the kind of "directory" you want (SQL, LDAP, Multi-directory), and fill in the required information.

The wizard will actually generate a contribution to the userManager extension point and some contributions for declaring users and groups directories, and it will copy them in the nxserver/config folder (ex: default-ldap-users-directory-bundle.xml).

You can find a full example of contribution to the userManager extension point on the explorer. Here is a review of the specific useful parts.

Users are defined on the users element:

<userManager>
    <users>
        <directory>somedirectory</directory> ...

The value somedirectory is the name of a contributed directory (see LDAP and SQL users contributions, as well as multidirectory).

Groups are defined on the groups element (also referencing already contributed directory).

 <groups>
    <directory>somegroupdir</directory>
    <membersField>members</membersField>
    <subGroupsField>subgroups</subGroupsField>
    <parentGroupsField>parentgroup</parentGroupsField>
    <listingMode>search_only</listingMode>
</groups>

Default Users and Groups Configuration

By default, the platform's administrator is the principal "Administrator". On the same contribution to the UserManager extension point you can define which principal from the remote identity provider will be the administrator of the application, instead of Administrator. That way you can assign a "real" user. This is done using the defaultAdministratorId element.

You can also choose a group from your company's directory instead of using the default "administrators" group, to determine the users who will benefit from all the rights in the platform. This is done using the administratorsGroup element.

Simple Configuration Example

  1. Create a file called default-ldap-users-directory-config.xml in your config directory:

    • nxserver/config/ in Tomcat.
  2. Then copy this content (make sure it's valid XML, sometimes what you think is a space character is actually a non-breaking space (U+00A0) which is invalid in XML):

    <?xml version="1.0"?>
    <component name="org.nuxeo.ecm.directory.ldap.users">
    
      <require>org.nuxeo.ecm.directory.ldap.LDAPDirectoryFactory</require>
    
      <!-- the groups SQL directories are required to make this bundle work -->
      <require>org.nuxeo.ecm.directory.sql.storage</require>
    
      <extension target="org.nuxeo.ecm.directory.ldap.LDAPDirectoryFactory"
        point="servers">
    
        <!-- Configuration of a server connection
          A single server declaration can point to a cluster of replicated
          servers (using OpenLDAP's slapd + sluprd for instance). To leverage
          such a cluster and improve availability, please provide one
          <ldapUrl/> tag for each replica of the cluster.
        -->
        <server name="default">
          <ldapUrl>ldap://localhost:389</ldapUrl>
          <!-- Optional servers from the same cluster for failover
            and load balancing:
    
            <ldapUrl>ldap://server2:389</ldapUrl>
            <ldapUrl>ldaps://server3:389</ldapUrl>
    
            "ldaps" means TLS/SSL connection.
          -->
    
          <!-- Credentials used by Nuxeo5 to browse the directory, create
            and modify entries.
    
            Only the authentication of users (bind) use the credentials entered
            through the login form if any.
          -->
          <bindDn>cn=nuxeo5,ou=applications,dc=example,dc=com</bindDn>
          <bindPassword>changeme</bindPassword>
        </server>
      </extension>
    
      <extension target="org.nuxeo.ecm.directory.ldap.LDAPDirectoryFactory"
        point="directories">
        <directory name="userDirectory">
          <server>default</server>
          <schema>user</schema>
          <idField>username</idField>
          <passwordField>password</passwordField>
    
          <searchBaseDn>ou=people,dc=example,dc=com</searchBaseDn>
          <searchClass>person</searchClass>
          <!-- To additionally restricte entries you can add an
            arbitrary search filter such as the following:
    
            <searchFilter>(&amp;(sn=toto*)(myCustomAttribute=somevalue))</searchFilter>
    
            Beware that "&" writes "&amp;" in XML.
          -->
    
          <!-- use subtree if the people branch is nested -->
          <searchScope>onelevel</searchScope>
    
          <!-- using 'subany', search will match *toto*. use 'subfinal' to
            match *toto and 'subinitial' to match toto*. subinitial is the
            default  behaviour-->
          <substringMatchType>subany</substringMatchType>
    
          <readOnly>false</readOnly>
    
          <!-- comment <cache* /> tags to disable the cache -->
          <cacheEntryName>ldap-user-entry-cache</cacheEntryName>
          <cacheEntryWithoutReferencesName>ldap-user-entry-cache-without-references</cacheEntryWithoutReferencesName>
          <!--
               If the id field is not returned by the search, we set it with the searched entry, probably the login.
               Before setting it, you can change its case. Accepted values are 'lower' and 'upper',
               anything else will not change the case.
          -->
          <missingIdFieldCase>lower</missingIdFieldCase>
    
          <!-- Maximum number of entries returned by the search -->
          <querySizeLimit>200</querySizeLimit>
    
          <!-- Time to wait for a search to finish. 0 to wait indefinitely -->
          <queryTimeLimit>0</queryTimeLimit>
    
          <creationBaseDn>ou=people,dc=example,dc=com</creationBaseDn>
          <creationClass>top</creationClass>
          <creationClass>person</creationClass>
          <creationClass>organizationalPerson</creationClass>
          <creationClass>inetOrgPerson</creationClass>
    
          <rdnAttribute>uid</rdnAttribute>
          <fieldMapping name="username">uid</fieldMapping>
          <fieldMapping name="password">userPassword</fieldMapping>
          <fieldMapping name="firstName">givenName</fieldMapping>
          <fieldMapping name="lastName">sn</fieldMapping>
          <fieldMapping name="company">o</fieldMapping>
          <fieldMapping name="email">mail</fieldMapping>
    
          <references>
            <inverseReference field="groups" directory="groupDirectory"
              dualReferenceField="members" />
          </references>
        </directory>
      </extension>
    
      <extension target="org.nuxeo.ecm.core.cache.CacheService" point="caches">
    
        <cache name="ldap-user-entry-cache" class="org.nuxeo.ecm.core.cache.InMemoryCacheImpl">
          <option name="maxSize">1000</option>
          <ttl>20</ttl><!-- minutes -->
          <option name="concurrencyLevel">500</option>
        </cache>
        <cache name="ldap-user-entry-cache-without-references" class="org.nuxeo.ecm.core.cache.InMemoryCacheImpl">
          <option name="maxSize">1000</option>
          <ttl>20</ttl><!-- minutes -->
          <option name="concurrencyLevel">500</option>
        </cache>
    
      </extension>
    
      <extension target="org.nuxeo.ecm.platform.usermanager.UserService" point="userManager">
        <userManager>
          <defaultAdministratorId>johndoe</defaultAdministratorId>
          <defaultGroup>members</defaultGroup>
        </userManager>
      </extension>
    </component>
    
    

    Then you should edit this file:

  3. Set the correct server:

    • <ldapUrl>
    • <bindDn> and <bindPassword>
  4. Set the correct LDAP config:

    • <searchBaseDN>
    • <searchClass>
    • <fieldMapping>
  5. If you want Nuxeo to be able to create users in the LDAP directory:

    • Make sure the user you use to access LDAP has write access
    • Define the <creationBaseDn> and associated parameters
  6. Define the default mapping:

    • Since the Administrator user won't exists anymore, you should assign at least one user to be administrator using <defaultAdministratorId>
    • You can also choose to make all users members of the default "members" group using <defaultGroup>
  7. Restart the Nuxeo server, and you should now be able to authenticate against LDAP.

If you want to roll back the changes, simply delete the default-ldap-users-directory-config.xml file and restart the server.

For a more detailed view about possible configuration, see:

The ldaptools/ folder in source code of the nuxeo-platform-directory-ldap module further provides sample LDIF files and OpenLDAP configuration file to help you setup a sample OpenLDAP server you can use as a base setup to build your corporate directory.

Using Active Directory

If you use Active Directory and want to use it with Nuxeo, you need to:

  1. Be sure that LDAP mode is enabled on the Active Directory server,
  2. Get the schema info (because Active Directory schema changes depending on a lot of external factors).

Once you have this information, you can connect Nuxeo to Active Directory as it was a real LDAP server.

Active Directory users are advised to use the aggregated global catalog port number (3268 by default) instead of the default LDAP port (389) in order to avoid getting referrals request to sub-directories blocked by corporate firewalls.

Usually with AD you will have to map the field "username" to "sAMAccountName".

Also, it happens that for the bindDN, it expects only an email address, ex:

[[email protected]](mailto:[email protected])

Advanced Configuration

For more details on directories, such as configuring "multi-directories", see Data Lists and Directories.

Known Issues

LDAP Contribution Not Activated

Nuxeo Studio also generates a contribution that declares directories called userDirectory and groupDirectory. Those definitions could override yours and therefore disable your LDAP definition.

The solution is to disable the definition from Studio: In Studio go to Roles & Permissions > Users & Groups and active the checkbox Disable users and groups definition.

Deployment Order Issue

In the case you manipulate several directory definitions (SQL directory, LDAP directory, multi-directory), you may not have the expected behavior because you don't control the deployment order of all contributions.

A cleaner way to proceed is to define directories whose name are different from the default ones (userDirectory for users, groupDirectory for groups). Then you need to use the userManager to specify the name of the directories which will be used for authentication, searching, etc. Therefore you should apply the changes described below to your existing LDAP contributions:

<!-- directory for users -->
<directory name="userLdapDirectory">
  (...)
  <inverseReference field="groups" directory="groupLdapDirectory"
          dualReferenceField="members" />
</directory>

<!-- directory for groups -->
<directory name="groupLdapDirectory">
    (...)
    <ldapReference field="members" directory="userLdapDirectory" forceDnConsistencyCheck="false" staticAttributeId="uniqueMember" dynamicAttributeId="memberURL"/>

    <ldapReference field="subGroups" directory="groupLdapDirectory" forceDnConsistencyCheck="false" staticAttributeId="uniqueMember" dynamicAttributeId="memberURL"/>
    (...)
</directory>

<!-- definition in the user manager -->
<extension target="org.nuxeo.ecm.platform.usermanager.UserService" point="userManager">
  <userManager>
    (...)
    <users>
      <directory>userLdapDirectory</directory>
    </users>
    (...)
    <groups>
      <directory>groupLdapDirectory</directory>
    </groups>
    (...)
  </userManager>
</extension>

See attached files for templates of LDAP configuration:

This method applies to multi-directories too.

Debug Information

If you encounter some difficulties configuring LDAP, the first step is to get more details about what happens.

In the Log4J configuration, increase the log level for org.nuxeo.ecm.directory and org.nuxeo.runtime.model.impl:

<category name="org.nuxeo.ecm.directory">
  <priority value="DEBUG" />
</category>
<category name="org.nuxeo.runtime.model.impl">
  <priority value="INFO" />
</category>

This will give you more informations such as:

  • Is your XML contribution properly loaded? Search for the component name of your contribution in the log file (for instance org.nuxeo.ecm.directory.ldap.users).
  • Did the LDAP directory initialized? If so, your "servers" extension point is working.
  • What is the LDAP request sending when you try to log in Nuxeo? You must be run the same request outside Nuxeo, using your preferred LDAP tool.

Apache Directory Studio can be used to replicate the LDAP requests sent by Nuxeo to the LDAP server and check their responses. If you seek help on answers.nuxeo.com or connect.nuxeo.com please include the LDIF export of a sample user entry and a sample group entry (if you want to use the LDAP server to resolve the groups).