UAA integrates with the Lightweight Directory Access Protocol in two major areas. Authentication, the first integration point, can be done using three different authentication methods. Once authenticated, a users LDAP groups can be retrieved and mapped to scopes, the second integration point.
In this document we use the term 'bind' a lot and it refers to the LDAP bind operation. In short, it is the LDAP way of performing an authentication on a given connection to the LDAP server.
At this time, integration with LDAP, both for authentication, is limited to users and the groups they belong to. We do foresee that we will also be able to authenticate clients and propagate client authorities in the future.
When integrating with an external identity provider, such as LDAP, authentication within the UAA becomes chained. An authentication attempt with a user's credentials is first attempted against the UAA user store before the external provider, LDAP.
Chained authentication allows a certain number of boot strap users to exists within the UAA itself without the need to configure them in a potential read only external store.
Usernames are not unique within the UAA. The combination of a username and its origin, 'ldap' for example, is unique.
A potential collision does exist in a chained authentication. If the exact same set of credentials, username/password combination, exist in both the UAA and the LDAP server, a user would always be authenticated against the UAA and an LDAP authentication would not be attempted. To avoid such a collision, and effectively disable chained authentication, do not bootstrap or create users in the UAA directly.
Authentication against the UAA database is the first step in the chained authentication. Authentication is done using three variables
- username - case insensitive username search, often email address
- origin - matched against 'uaa'
- password - an encoded password, the input is encoded and then matched against the DB value
This is how the UAA performs user authentication ouf ot the box with no external identity provider configured. When enabling LDAP, this method of authentication is always tried prior to attempting to authenticate against the external provider.
LDAP authentication within the UAA is done leveraging the Spring Security LDAP module thus the authentication methods and configuration options you will find available within the UAA are directly correlated to those found in Spring Security LDAP. There are three different authentication methods supported against an LDAP compatible server
- Search and Bind - find the user DN, authenticate as the user
- Simple Bind - construct the user DN, authenticate as the user
- Search and Compare - find the user DN, perform a comparison against the users password attribute
The UAA will attempt to retrieve the user's email address and update the UAA user's record with it so that there is a current email address on file as external systems may rely on the UAA SCIM record of the user for notifications.
The most common LDAP authentication method is the 'search and bind'. During a search and bind
- the user inputs a username
- the UAA server performs a search using an LDAP filter and a set of known search credentials
- If there is exactly one match the user's DN will be retrieved. Zero or more than one matches are automatically rejected.
- The user's DN and supplied password is then used to attempt a bind against the LDAP server
- The LDAP server performs the authentication
This method allows the most flexibility, as username to DN match is done using an LDAP search filter (query), and the user's credentials are not exposed.
The security consideration with this authentication method is that a set of, preferably read only, credentials has to be made available to the UAA to perform a search for a sub set of the LDAP tree where the user resides. Should the UAA's configuration be compromised, so becomes the LDAP read only data that the UAA can query.
An LDAP bind authentication never exposes any of the LDAP directory contents to the UAA. This is the benefit for a a less flexible authentication method. In this method, the user supplies a username, and with that username, the UAA statically constructs a DN. For example
- user supplies user, for example, filip - we construct DN, dn=uid=filip,ou=users,dc=test,dc=com
- The UAA uses the statically constructed DN, and supplied password and attempts a bind to the LDAP server
In the simple bind method, the administrator of the UAA can configure one or more DN patterns to be tried for a single username input. A pattern can also be the input itself, such as '{0}' as the pattern means the user would have to type in his or her DN as the username,
While this method provides the most secure installation as no LDAP credentials or data is exposed through configuration, it is not very common as the flexibility is reduced by not being able to customize the username through a search query.
Similar to the search and bind, the search and compare will perform a search using a filter, retrieve the user's LDAP record, including the password field. It will then perform a password comparison against the password field in a similar way that the UAA does against its user store.
This method is rarely used, it requires additional privileges to retrieve the password field.
Configuration of the UAA is done through the uaa.yml This allows easy configuration for consumers like Cloud Foundry to generate a configuration file, and deploy the UAA as a job.
The UAA is a Spring based application, and reads the values from the uaa.yml and performs a variable substitution in the XML configuration files
Enabling any level of LDAP authentication requires the
spring_profiles: ldap
configuration
to be enabled. This ldap
profile triggers the chained authentication to be enabled and the
ldap configuration files
to be loaded.
spring_profiles: ldap
All further configurations will be placed under the ldap:
configuration element
Selecting an authentication method, simple bind
, search and bind
or search and compare
is done using the
ldap.profiles.file
configuration attribute. There are three different values for this attribute, each mapped to the
different authentication methods
ldap/ldap-simple-bind.xml
- simple bindldap/ldap-search-and-bind.xml
- search and bindldap/ldap-search-and-compare.xml
- search and compare
As noticed, the attribute is an actual reference to a configuration file. The configuration,
at a minimum, should provide a bean named ldapAuthProvider
that will be used
to configure the
LDAP authentication manager.
This allows a user/administrator of the UAA to configure a Spring XML file for a custom ldap authentication method.
spring_profiles: ldap ldap: profile: file: ldap/ldap-search-and-bind.xml
The following attributes are available for the default bind configuration
ldap.base.url
- A URL pointing to the LDAP server, must start withldap://
orldaps://
ldap.base.userDnPattern
- one or more patterns used to construct DN.ldap.base.userDnPatternDelimiter
- the delimiter character to break up multiple patterns. Default is semi colon;
ldap.base.mailAttributeName
- the name of the attribute that contains the user's email address, default value ismail
spring_profiles: ldap ldap: profile: file: ldap/ldap-simple-bind.xml base: url: 'ldap://localhost:10389/' mailAttributeName: mail userDnPattern: 'cn={0},ou=Users,dc=test,dc=com;cn={0},ou=OtherUsers,dc=example,dc=com'
The following attributes are available for the default search and bind configuration
ldap.base.url
- A URL pointing to the LDAP server, must start withldap://
orldaps://
In the case of SSL (ldaps), the server must hold a trusted certificate or the certificate must be imported into the JVM's truststore.ldap.base.mailAttributeName
- the name of the attribute that contains the user's email address, default value ismail
ldap.base.userDn
- The DN for the LDAP credentials used to search the directoryldap.base.password
- Password credentials for the above DN to search the directoryldap.base.searchBase
- Specify only if a part of the directory should be searched, for exampledc=test,dc=com
ldap.base.searchFilter
- the search filter used for the query.{0}
is used to annotate where the username will be inserted. For examplecn={0}
will search the LDAP directory records where the attributecn
matches the users input.
spring_profiles: ldap ldap: profile: file: ldap/ldap-search-and-bind.xml base: url: 'ldap://localhost:10389/' mailAttributeName: mail userDn: 'cn=admin,ou=Users,dc=test,dc=com' password: 'password' searchBase: '' searchFilter: 'cn={0}'
The following attributes are available for the default search and bind configuration
ldap.base.url
- A URL pointing to the LDAP server, must start withldap://
orldaps://
In the case of SSL (ldaps), the server must hold a trusted certificate or the certificate must be imported into the JVM's truststore.ldap.base.mailAttributeName
- the name of the attribute that contains the user's email address, default value ismail
ldap.base.userDn
- The DN for the LDAP credentials used to search the directoryldap.base.password
- Password credentials for the above DN to search the directoryldap.base.searchBase
- Specify only if a part of the directory should be searched, for exampledc=test,dc=com
ldap.base.searchFilter
- the search filter used for the query.{0}
is used to annotate where the username will be inserted. For examplecn={0}
will search the LDAP directory records where the attributecn
matches the users input.ldap.base.passwordAttributeName
- the name of the LDAP attribute that holds the passwordldap.base.localPasswordCompare
- set to true if the comparison should be done locally Setting this value to false, implies that rather than retrieving the password, the UAA will run a query to match the password. In order for this query to work, you must know what type of hash/encoding/salt is used for the LDAP password.ldap.base.passwordEncoder
- A fully qualified Java classname to a password encoder. The default uses the Apache Directory Server password utilities to support several different encodings.
spring_profiles: ldap ldap: profile: file: ldap/ldap-search-and-compare.xml base: url: 'ldap://localhost:10389/' mailAttributeName: mail userDn: 'cn=admin,ou=Users,dc=test,dc=com' password: 'password' searchBase: '' searchFilter: 'cn={0}' passwordAttributeName: userPassword passwordEncoder: org.cloudfoundry.identity.uaa.provider.ldap.DynamicPasswordComparator localPasswordCompare: true
As of now, the primary purpose of the UAA is to issue Oauth 2 tokens to the client on behalf of the user.
The UAA integrates with LDAP groups during the user authentication process. Each time a user is authenticated, group memberships, if configured, are retrieved and refreshed.
These groups are then mapped to UAA scopes. When LDAP group integration is enabled, groups are translated to scopes and stored in the UAA database as SCIM groups. Upon each authentication, a user's group membership is reset. Thus, a change in group membership for a user in LDAP, does not manifest itself until the next successful authentication of the user against the LDAP server.
Support for nested groups has been implemented. Either through the search filter, Active Directory style,
or for static nested groups by using the search filter, most commonly using the memberOf
attribute.
In the UAA, we refer to a scope as a group, or a SCIM group. Groups in the UAA are mapped one to one to a scope. The group name, in fact, is the scope name. Groups can be nested.
A token contains a list of scopes and authorities. The scopes in the token represent the permissions of the user while the authorities represent the permissions of the client itself. Thus, a resource server receiving a request containing a token can decide to authorize the client on behalf of the user, or just the client itself.
The UAA integrates with LDAP groups in such way that groups can translate into scopes. There are three different ways we incorporate LDAP groups
- No group integration - this is the default setting when you enable LDAP. Groups will be ignored.
- A group is a scope or scopes - With this configuration, UAA will read a designated attribute of a group and the value of the record attribute is either one scope, or a list of comma delimited scope names. These scopes, if configured, will be automatically added to the UAA database as groups.
- Mapping of LDAP groups to scopes - With this configuration you can map a group DN
to a group/scope UUID using the UAA
external_group_mapping
table.
We foresee that the strategy of mapping groups to scopes using the mapping table, will become the most common one, along with no group integration at all. Mapping groups to scopes by putting a group name in the LDAP record will require modification to the group record itself, either by adding an attribute or modifying an existing attribute requires changes to the LDAP directory that most organizations may not wish to perform. It will couple the LDAP tree to the Cloud Foundry, CF, installation, and thus, in multi CF installations, become troublesome.
Choose this strategy if you wish to not retrieve LDAP groups during authentication. All group membership is managed by the UAA. May be suitable when all users receive the same groups, either through the manifest, or through the Spring configuration files
When you wish to control scopes directly in your LDAP directory, my modifying your LDAP group entries to contain one or more scope names. In this setup, you'd define an attribute, or reuse an existing attribute and store the names of the scopes that you wish the group represent.
dn: cn=developers,ou=scopes,dc=test,dc=com changetype: add objectClass: groupOfNames objectClass: top cn: developers description: blog.read,blog.write,blog.delete member: cn=operators,ou=scopes,dc=test,dc=com member: cn=marissa6,ou=Users,dc=test,dc=com
For the above example, the user marissa6, would inherit the scopes blog.read
,blog.write
and blog.delete
upon authentication.
If the scopes are not present in the UAA schema, they will get created and assigned a UUID.
Users that are members of the cn=operators,ou=scopes,dc=test,dc=com
group, would also inherit the above mentioned scopes if nested
group searches are enabled.
A major benefit to this integration method, is that scopes/groups don't have to preexist in the UAA. They can be
automatically created upon user authentication. Together with clients having wildcard support for authorities, for example,
blog.*
, making meaningful LDAP groups to client authority mappings becomes very easy.
Probably the most flexible integration with LDAP groups is when they are mapped to a UAA group. This allows the administrator of CF to create the scopes (aka groups in the UAA schema) and then map these to LDAP groups. The mapping is many-to-many, so any LDAP group can be mapped to one or more UAA scopes, and likewise, any UAA scope can be mapped to one or more. Mappings are not created automatically, and both groups(scopes) and mappings to LDAP groups must exist when the user authenticates in order for the user/scope relationship to be created.
The property ldap.groups.file
controls what group mapping is used, and is also a reference to a Spring XML configuration file.
The different values are
ldap/ldap-groups-null.xml
- no groups will be retrievedldap/ldap-groups-as-scopes.xml
- group names will be derived from an attribute, like CN, in the group recordldap/ldap-groups-map-to-scopes.xml
- groups will be mapped to UAA groups using theexternal_group_mapping
table
The file exports a bean named ldapAuthoritiesPopulator
to be used in the LDAP configuration files.
ldap.groups.file
- set toldap/ldap-groups-null.xml
to never retrieve group information
This is the default.
The configuration looks like
spring_profiles: ldap ldap: profile: file: ldap/ldap-search-and-bind.xml groups: file: ldap/ldap-groups-null.xml base: url: 'ldap://localhost:10389/' userDn: 'cn=admin,ou=Users,dc=test,dc=com' password: 'password' searchBase: '' searchFilter: 'cn={0}'
is the same as omitting the value all together
spring_profiles: ldap ldap: profile: file: ldap/ldap-search-and-bind.xml base: url: 'ldap://localhost:10389/' userDn: 'cn=admin,ou=Users,dc=test,dc=com' password: 'password' searchBase: '' searchFilter: 'cn={0}'
ldap.groups.file
- set toldap/ldap-groups-as-scopes.xml
to create scopes out of LDAP groupsldap.group.searchBase
- the search base for the group searchldap.group.groupRoleAttribute
- the name of the attribute in the LDAP record that contains the scope name(s)ldap.group.searchSubtree
- boolean value, true indicates that we search the sub tree of the LDAP baseldap.group.groupSearchFilter
- similar to a user filter, most common ismember={0}
ldap.group.maxSearchDepth
- how many levels deep do we search for nested groups Set this value to 1 to disable nested groups. The default is 10ldap.groups.autoAdd
- boolean value, true indicates that groups(scopes) will be added automatically if they don't exist
spring_profiles: ldap ldap: profile: file: ldap/ldap-search-and-bind.xml base: url: 'ldap://localhost:10389/' userDn: 'cn=admin,ou=Users,dc=test,dc=com' password: 'password' searchBase: '' searchFilter: 'cn={0}' groups: file: ldap/ldap-groups-as-scopes.xml searchBase: ou=scopes,dc=test,dc=com groupRoleAttribute: scopenames searchSubtree: true groupSearchFilter: member={0} maxSearchDepth: 10 autoAdd: true
ldap.groups.file
- set toldap/ldap-groups-map-to-scopes.xml
to map scopes to LDAP groupsldap.group.searchBase
- the search base for the group searchldap.group.groupRoleAttribute
- ignored by this implementationldap.group.searchSubtree
- boolean value, true indicates that we search the sub tree of the LDAP baseldap.group.groupSearchFilter
- similar to a user filter, most common ismember={0}
ldap.group.maxSearchDepth
- how many levels deep do we search for nested groups Set this value to 1 to disable nested groups. The default is 10ldap.groups.autoAdd
- has no effect in this configuration, as if the mapping doesn't exist and is valid, there will not be authorities assigned to the user
spring_profiles: ldap ldap: profile: file: ldap/ldap-search-and-bind.xml base: url: 'ldap://localhost:10389/' userDn: 'cn=admin,ou=Users,dc=test,dc=com' password: 'password' searchBase: '' searchFilter: 'cn={0}' groups: file: ldap/ldap-groups-map-to-scopes.xml searchBase: ou=scopes,dc=test,dc=com searchSubtree: true groupSearchFilter: member={0} maxSearchDepth: 10 autoAdd: true
Once you have configured UAA to map Ldap Groups to Scopes, you can use the Cloud Controller API to manage the group mappings:
As you may have noticed through the different examples, the property ldap.base.mailAttributeName
is always
configured, and even has a default value. Each time the UAA authenticates an LDAP user it will update the
user's email record in the database. This is so that systems that provide notifications, have an email
address that is as current as the user's last authentication.
If an LDAP user does not have an email address, the UAA can automatically generate one.
spring_profiles: ldap ldap: profile: file: ldap/ldap-search-and-bind.xml base: url: 'ldap://localhost:10389/' userDn: 'cn=admin,ou=Users,dc=test,dc=com' password: 'password' searchBase: '' searchFilter: 'cn={0}' mailAttributeName: 'mail' mailSubstitute: 'generated-{0}@company.example.com' mailSubstituteOverridesLdap: true
In the above example, if user marissa
has a mail record, her UAA email will be set to the email address she has on file.
However, if marissa
does not have an email address in the mail
attribute, her UAA email will become
[email protected]
.
The UAA provides an ability to override the email address that is set in LDAP
by setting the mailSubstituteOverridesLdap
flag to true.
spring_profiles: ldap ldap: profile: file: ldap/ldap-search-and-bind.xml base: url: 'ldap://localhost:10389/' userDn: 'cn=admin,ou=Users,dc=test,dc=com' password: 'password' searchBase: '' searchFilter: 'cn={0}' mailAttributeName: 'mail' mailSubstitute: 'generated-{0}@company.example.com' mailSubstituteOverridesLdap: true
In the above example, the user marissa
's UAA email always become [email protected]
.
-
ldap.profiles.file
authentication file reference. Value must be a file path to a Spring XML configuration file that delivers a bean namedldapAuthProvider
used by the LDAP authentication manager There are three different values available with the- ldap/ldap-simple-bind.xml
- ldap/ldap-search-and-bind.xml
- ldap/ldap-search-and-compare.xml
-
ldap.base.url
A URL pointing to the LDAP server, must start withldap://
orldaps://
When using SSL, an ldaps URL, the certificate must be trusted, or be imported in the JVM trust store. The string value may contain multiple LDAP URLs, space delimited.
This property is always used. -
ldap.base.referral
Should the LDAP client instruct the server to follow referrals. Possible values areignore
andfollow
. The default isfollow
This property is always used. -
ldap.base.userDnPattern
one or more patterns used to construct DN. Contains one or more patterns used to construct a DN. A pattern can look likecn={0},ou=Users,dc=test,dc=com}
where{0}
will be replaced with the user's input. theuserDnPattern
property can contain multiple patterns, that will be tried in sequence. The patterns are delimited by theuserDnPatternDelimiter
character
This property is used with the simple bind authentication mechanism. -
ldap.base.userDnPatternDelimiter
the delimiter character to break up multiple patterns in theldap.base.userDnPattern
property. Default is semi colon;
This property is used with the simple bind authentication mechanism. -
ldap.base.mailAttributeName
the name of the attribute that contains the user's email address Default value ismail
If an email address is not available, one will be generated for the user.
This property is always used. -
ldap.base.mailSubstitute
Defines a pattern,{0}@ldap-generated.email.com
, that the system uses to generate an email address based on the username for the user. This property will set the email address for the user if the LDAP email address is null or if the propertyldap.base.mailSubstituteOverridesLdap
is set to true. The pattern must contain{0}
to be substituted for the username. Default value isnull
This property is optional -
ldap.base.mailSubstituteOverridesLdap
If set to true and the property/patternldap.base.mailSubstitute
is defined the users email address will be generated from the pattern, always. Default value isfalse
This property is optional -
ldap.base.userDn
The DN for the LDAP credentials used to search the directory. When searching the LDAP directory UAA uses a, preferably read only, account to find the DN for a user matching a user inputted username. The userDn property is a complete DN for an account that has search privileges.
This property is used with the 'search and bind' and 'search and compare' authentication mechanisms. -
ldap.base.password
Password credentials for the above account/DN to search the LDAP directory
This property is used with the 'search and bind' and 'search and compare' authentication mechanisms. -
ldap.base.searchBase
Specify only if a part of the directory should be searched, for exampledc=test,dc=com
. It is the user-search-base property of Spring Security LDAP.
This property is used with the 'search and bind' and 'search and compare' authentication mechanisms. -
<a name="ldap.base.searchFilter>
ldap.base.searchFilter
the search filter used for the query.{0}
is used to annotate where the username will be inserted. For examplecn={0}
will search the LDAP directory records where the attributecn
matches the users input.
This property is used with the 'search and bind' and 'search and compare' authentication mechanisms. -
ldap.base.passwordAttributeName
the name of the LDAP attribute that holds the password to be compared with user's input
This property is used with the 'search and compare' authentication mechanism. -
ldap.base.localPasswordCompare
set to true if the comparison should be done locally Setting this value to false, implies that rather than retrieving the password, the UAA will run a query to match the password. In order for this query to work, you must know what type of hash/encoding/salt is used for the LDAP password.
This property is used with the 'search and compare' authentication mechanism. -
ldap.base.passwordEncoder
A fully qualified Java classname to a password encoder. The default uses the Apache Directory Server password utilities to support several different encodings.
This property is used with the 'search and compare' authentication mechanism. -
ldap.groups.file
group integration file reference. Value must be a file path to a Spring XML configuration file that delivers a bean namedldapAuthoritiesPopulator
used by the LDAP authentication provider- set to
ldap/ldap-groups-null.xml
to never retrieve group information (group integration disabled) - set to
ldap/ldap-groups-as-scopes.xml
to directly map LDAP groups to scopes - set to
ldap/ldap-groups-map-to-scopes.xml
to leverage the external_group_mappings table to map an LDAP group to one or more UAA scopes
This property is always used, but may be omitted when no group integration is desired.
- set to
-
ldap.groups.ignorePartialResultException
How should the client react when it receives apartial results
message back from the LDAP server. If set to true, it is ignored. If set to false, authentication and group search will be marked as failed. Default istrue
. User searches are always ignoring partial results, and always expect 1 result back from the query. -
ldap.group.searchBase
the search base for the group search. This references the group-search-base property in Spring Security LDAP
This property is always used, but may be omitted when no group integration is desired. -
ldap.group.groupRoleAttribute
the name of the attribute in the LDAP record that contains the scope name(s). In case of multiple scopes, they must be delimited by a comma,
This property is used by the LDAP Groups as Scopes mapping -
ldap.group.searchSubtree
boolean value, true indicates that we search the sub tree of the LDAP base. The default value is true.
This property is used when group integration enabled -
ldap.group.groupSearchFilter
similar to a user filter, most common ismember={0}
. This is the search filter used when user group memberships are retrieved. For nested Active Directory groups, this string can be modified. Initially the{0}
will be replaced with the user's DN and then for statically nested groups, the{0}
will be replaced with the group DNs to search nested groups in the hierarchy.
This property is used when group integration enabled -
ldap.group.maxSearchDepth
how many levels deep do we search for nested groups Set this value to 1 to disable nested groups. The default is 10
This property is used when group integration enabled, but does not apply when the group search filter contains an Active Directory command to retrieve nested groups and should then be set to 1 to avoid unnecessary queries. -
ldap.groups.autoAdd
boolean value, true indicates that groups(scopes) will be added automatically if they don't exist
This property is used by the LDAP Groups as Scopes mapping -
ldap.emailDomain
List value, Optional List of email domains associated with the UAA provider that selects an authentication source for an invited user. If null and no domains are explicitly matched with any other providers, the UAA acts as a catch-all, wherein the email will be associated with the UAA provider. Wildcards supported. -
ldap.externalGroupsWhitelist
List value, Optional List of external groups that will be included in the ID Token if theroles
scope is requested. The list should containDN
values for the groups that are associated with the user. The display name of the group in the ID token will be the taken from theldap.group.groupRoleAttribute
attribute -
ldap.attributeMappings
Map<String,Object> value where Object can be a String or a List, Optional List of UAA attributes mapped to attributes from LDAP that are presented as part of the ID token when theprofile
scope is requested. Currently we support mapping for keysgiven_name
(String),family_name
(String),phone_number
(String). LDAP integration also supports custom user attributes to be populated in the id_token when theuser_attributes
scope is requested. The attributes are pulled out of the user records and have the formatuser.attribute.<name of attribute in ID token>: <ldap attribute name>
ldap: attributeMappings: first_name: givenname family_name: sn phone_number: telephonenumber user: attribute: employeeCostCenter: costCenter terribleBosses: manager