Enable LDAP Authentication and Synchronization
Introduction
Goal
Enable authentication through LDAP and synchronization of LDAP users and groups to Hippo Repository.
The LDAP Add-on
Bloomreach Experience Manager includes an optional LDAP add-on which enables authenticating against one or more LDAP servers from within Bloomreach Experience Manager and for synchronizing users and groups to Hippo Repository. Synchronizing back to the LDAP server is not supported.
The LDAP add-on supports normal connections over ldap:// as well as secure connections over ldaps://.
The LDAP Security Provider
The LDAP add-on implements a security provider in the repository. A security provider can authenticate users and handle synchronizations between the repository and an external authority.
In summary:
-
The security provider contains the general LDAP information
-
The user manager defines which users and what information needs to be synchronized
-
The group manager defines which groups and what information needs to be synchronized
Note that this does not grant the users or groups any access. You have to configure that in the security domains.
Login process
The login process is described below in detail. The key points are:
-
The credentials are always checked against the LDAP server. The repository does not cache credentials.
-
The user information and the groups the user belongs to are synchronized during the login.
step |
description |
1. |
A user logs in to the CMS (or site) |
2. |
The CMS logs in to the repository with the user credentials |
3. |
The repository authenticates with the LDAP server |
4. |
The user and group information of that user is synced in the repository |
4a. |
A background process syncs all users and groups now and then |
5. |
The repository returns an authenticated JCR session |
6. |
The CMS returns an authenticated web/HTTP session |
Supported LDAP servers
The following LDAP (compatible) servers are supported:
-
Microsoft Active Directory
-
OpenLDAP
-
Apache Directory Server
Configure the LDAP addon
The LDAP functionality is bundled as an add-on for the repository. To add it to a Bloomreach Experience Manager implementation project, add the following dependency in the file cms-dependencies/pom.xml (or cms/pom.xml if on version 12 and lower)
<dependency> <groupId>com.onehippo.cms7</groupId> <artifactId>hippo-addon-ldap</artifactId> </dependency>
Example Configuration
Two example configurations are available for download:
The examples can be imported through the console in /hippo:configuration/hippo:security as is shown in the example below:
LDAP Security Provider
The LDAP security provider maps to a single LDAP server. It is possible to connect the repository to multiple LDAP servers by adding an LDAP security provider for each server. The security provider node holds the configuration for the LDAP security provider to connect to the server like the address and the credentials.
LDAP Synchronization to the repository
The LDAP add-on synchronizes user and/or group information to the repository. The synchronization process will start on repository startup and it will not run again until the maximum cache age has expired. Only user and group fields that are explicitly configured with mappings (see below) are synced to the repository. Do not sync the password field of the users, the credentials are always checked against the LDAP server.
Node type definitions
hippoldap:securityprovider
[hippoldap:securityprovider] > hipposys:securityprovider orderable - hippoldap:providerurl (string) mandatory - hippoldap:authentication (string) = 'simple' mandatory autocreated - hippoldap:initialfactory (string) = 'com.sun.jndi.ldap.LdapCtxFactory' mandatory autocreated - hippoldap:socketfactory (string) - hippoldap:connecttimeoutms (string) - hippoldap:searchbase (string) - hippoldap:principal (string) - hippoldap:credentials (string) - hippoldap:cachemaxage (long) - hippoldap:disabled (boolean) - hippoldap:cronexpression (string) - hippoldap:loginsynccacheminutes (long)
property | description | example |
---|---|---|
providerurl | The ldap connection url | ldap://myldap.example.com/ , ldaps://localhost:636/ |
authentication | The authentication mechanism | simple |
initialfactory | The initial context factory | com.sun.jndi.ldap.LdapCtxFactory |
socketfactory |
The socket factory implementation for setting up the connection to the LDAP server. This class must implement the javax.net.SocketFactory abstract class and provide an implementation of the getDefault() method that returns an instance of the socket factory. |
javax.net.ssl.SSLSocketFactory (default value) |
connecttimeoutms | Timeout in ms for a connection attempt to the ldap server | 1000 |
searchbase | The global search base of the LDAP server | dc=mycompany,dc=com |
principal | The DN of the LDAP sync (read-only) user | cn=ldapadmin, dc=mycompany,dc=com |
credentials | The password of the LDAP sync user | supers3cr3t |
cachemaxage | The amount of time in seconds between full syncs - overridden by cronexpression | 86400 |
cronexpression | Cron expression to control when and how often the job runs - overrides cachemaxage | 0 0 12 * * ? |
disabled | Control whether the ldap synchronization job runs | true |
loginsynccacheminutes | Cache duration in minutes of user attributes before resynchronizing with the LDAP server on a subsequent login. If this option is not set, user attributes are synchronized on every login. | 30 |
Configure LDAP Provider's Credentials in the Application Context
The connection parameters of the LDAP Provider can be defined as a JNDI resource on container level, e.g. Apache Tomcat.
To configure the LDAP provider, add the following entry into the conf/context.xml file in your project.
<Environment name="securityprovider/ldap" value="{'credentials':'secret', 'principal':'cn=ldapadmin,dc=onehippo,dc=org', 'authentication':'simple', 'providerurl':'ldap://127.0.0.1:389'}" type="java.lang.String"/>
This will register a JNDI environment resource under java/comp:env/securityprovider/ldap when web application is started. The JSON string contains the properties needed to connect to an LDAD provider.
You can configure providerurl, authentication, searchbase, principal, credentials properties through the JNDI resource.
LDAP user provider
The user provider defines where to look for the users in the LDAP server and which information to synchronize to the repository such as first name, last name and email address. The user provider can contain multiple searches and mappings.
hippoldap:userprovider node type
[hippoldap:userprovider] > hipposys:userprovider + * (hippoldap:mapping) = hippoldap:mapping multiple + * (hippoldap:usersearch) = hippoldap:usersearch multiple - hippoldap:class (string) - hippoldap:saveInterval (long) - hippoldap:searchPageSize (long) - hippoldap:compareCaseSensitive (boolean) - hippoldap:compareUserIdBy (string) < 'LOWERCASE', 'UPPERCASE'
property |
description |
example |
class | Fully qualified name of a user manager to use instead of the built-in class | org.example.CustomLdapUserManager |
saveInterval |
Batch size for processing before saving to repository while synchronizing |
250 |
searchPageSize |
Page size used for ldap results |
200 |
compareCaseSensitive |
Whether or not to match userId by using case sensitivity |
false |
compareUserIdBy |
Whether or not to match userId by using lowercase username. If compareCaseSensitive is set to true, this property is ignored. If compareCaseSensitive is false, userId will be transformed to either LOWERCASE or UPPERCASE before comparison. If this option is not set, userId is matched in LOWERCASE by default. |
LOWERCASE |
Note these properties have reasonable defaults and should not be set under normal operation unless you understand the impact.
The mapping and search child nodes are described below.
LDAP user searches
The search defines where to look for the users. Only users that match the search (or filter) can login and will be synchronized.
hippoldap:usersearch node type
[hippoldap:usersearch] > nt:base - hippoldap:nameattribute (string) = 'uid' mandatory autocreated - hippoldap:basedn (string) mandatory - hippoldap:filter (string)
property |
description |
default |
example |
nameattribute |
the attribute that contains the username |
uid |
cn, uid or somethimes on AD sAMAccountName |
basedn |
the LDAP search base |
|
dc=mycompany,dc=com |
filter |
the LDAP filter/search |
(objectclass=posixAccount) |
(&(objectCategory=Person)(uuid=*)) or (&(objectCategory=Person)(sAMAccountName=*)) |
LDAP user mappings
The user mappings define which information about the user is synchronized to the repository. A mapping maps a single attribute from LDAP to the repository. An attribute can be single or multivalued. The mapped information is stored as a string value.
hippoldap:mapping nodetype
[hippoldap:mapping] > nt:base - hippoldap:target (string) mandatory - hippoldap:multi (boolean) = 'false' mandatory autocreated - hippoldap:source (string) mandatory
property |
description |
default |
example |
source |
the attribute in LDAP to synchronize |
|
sn |
target |
the name of the property in the repository |
|
lastname |
multi |
whether or not this is a multi-valued property |
false |
true |
The multi property is included for future needs but is currently not supported.
LDAP group provider
The group provider defines where to look for the groups in the LDAP server and which information to synchronize to the repository. The group provider can contain multiple searches and mappings.
hippoldap:groupprovider node type
[hippoldap:groupprovider] > hipposys:groupprovider + * (hippoldap:groupsearch) = hippoldap:groupsearch multiple + * (hippoldap:mapping) = hippoldap:mapping multiple - hippoldap:class (string) - hippoldap:saveInterval (long) - hippoldap:searchPageSize (long) - hippoldap:compareCaseSensitive (boolean)
The properties here are equivalent to the user provider.
LDAP group searches
The search defines where to look for the groups. Only groups that match the search (or filter) will be synchronized.
hippoldap:groupsearch node type
[hippoldap:groupsearch] > hippoldap:usersearch - hippoldap:membernamematcher (string) - hippoldap:memberattribute (string) - hippoldap:lookupusernameattribute (string)
property |
description |
default |
example |
nameattribute |
the attribute that contains the group name (inherited) |
|
cn |
basedn |
the LDAP search base (inherited) |
|
dc=mycompany,dc=com |
filter |
the LDAP filter/search (inherited) |
(objectclass=posixGroup) |
(objectCategory=Group) |
membernamematcher |
how to match the member names of the group |
<uid> |
<dn> |
memberattribute |
the ldap attribute that contains the members |
memberUid |
member |
lookupusernameattribute | user attribute to match for the username | samAccountName |
The membernamematcher can be one of two values:
<uid> |
Search for LDAP memberships is done based on uid: username |
<dn> |
Search for LDAP memberships is done based on full dn: cn=username, dc=mycompany, dc=com |
The values must be entered exactly as written. For example, with a group defined as (ldiff format):
dn: cn=ldapadmins,ou=groups,dc=onehippo,dc=org objectClass: groupOfUniqueNames objectClass: top cn: ldapadmins uniqueMember: uid=foobar,ou=people,dc=onehippo,dc=org
Then you need to have memberattribute=uniqueMember and match the user on <dn>. The member in this group has <dn> equal to uid=foobar,ou=people,dc=onehippo,dc=org. If matching on <uid> then you only need to identify the user as foobar
The property lookupusernameattribute can be used to match the username if the username information is not available in the <dn>. You could map your username to samAccountName. If samAccountName is not part of the <dn> then an extra lookup is needed to find the user attribute to map to the group members.
LDAP group mappings
The group mappings define which information about the user is synchronized to the repository. A mapping maps a single attribute from LDAP to the repository. Currently only single value properties are included. The mapped information is stored as a string value.
hippoldap:mapping nodetype
[hippoldap:mapping] > nt:base - hippoldap:target (string) mandatory - hippoldap:multi (boolean) = 'false' mandatory autocreated - hippoldap:source (string) mandatory
property |
description |
default |
example |
source |
the attribute in LDAP to synchronize |
|
description |
target |
the name of the property in the repository |
|
description |
multi |
whether or not this is a multi-valued property |
false |
false |
The multi property is included for future needs but is currently not supported.
Tips and tricks
Logging
To get some information about what is going on in the repository set the following three classes to log level INFO or DEBUG:
-
org.hippoecm.repository.security.ldap.LdapSecurityProvider
-
org.hippoecm.repository.security.ldap.LdapUserManager
-
org.hippoecm.repository.security.ldap.LdapGroupManager
This can usually be done in your log4j2.xml. Another way to configure the logging is by using the logging servlet. The servlet is configured by default at: http://localhost:8080/cms/logging/
Only sync LDAP users and not groups
To only sync LDAP users and not the groups create a hipposys:groupprovider without any searches or mappings in the group provider.
Disable user creation in Bloomreach Experience Manager
To disable user creation from within Bloomreach Experience Manager in the admin panel you can set the property user.creation.enabled to false on the node /hippo:configuration/hippo:frontend/cms/cms-admin/users.
Repository Browser
The repository browser is stateless and logs in again with every page request, so you can check your results by just pressing F5. The repository browser is by default configured at: http://localhost:8080/cms/repository/. Together with the debug log enabled this can be very useful to quickly check and adjust the LDAP configuration.
Secure Connection in Test Environments
When using a secure connection (ldaps://) in a test environment, the socket factory implementation org.hippoecm.repository.security.ldap.GullibleSSLSocketFactory can be used to prevent the check of the certificate itself.