JBoss.orgCommunity Documentation
Table of Contents
The LDAP Identity Store allows a LDAP Directory to be used as a source of identity data. Most organizations rely on a LDAP Directory to store users, groups, roles and relationships between those entities. Some of them only store users and groups, others only users and so forth. The point is that each organization has its own structure, how data is organized on the server and policies to govern all that. That said, is very hard to get all different use cases satisfied given all those nuances.
To try to overcome that, the LDAP Identity Store provides a simple and easy mapping between the entries in
your LDAP tree and the PicketLink types (IdentityType
,
Relationship
and so forth),
plus some additional configuration options that give you more control how the store should integrate with your
server.
The store can be used in read-only or read-write mode. Depending on your permissions on the server, you should consider one of these alternatives, otherwise you can get errors when, for example, trying to add, update or remove entries from the server.
The list below summarizes some of the most important capabilities provided by this store:
Mapping
IdentityType
types to their corresponding LDAP entries and attributes.
Mapping
Relationship
types to their corresponding LDAP entries and attributes.
Mapping of parent/child relationships between the LDAP entries mapped to the same type.
Authentication of users based on username/password credentials.
Use of LDAP UUID attributes as the identifier for identity types. For each identity type in PicketLink we need
to
provide a single/unique identifier. The LDAP store uses the
entryUUID
and
objectGUID
(depending on your server implementation, of course) to identify each type.
But the LDAP Directory has also some limitations (schema limitations, restrictive usage policies) and because of that the LDAP Identity Store does not supports all the feature set provided by PicketLink. The table below lists what is not supported by the LDAP Identity Store:
Complex relationship mappings such asGroupRole
.
Relationships can not be updated directly using theIdentityManager
.
Limited support for credential types. Only username/password is available.
The LDAP Identity Store can be configured as follows:
IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();
builder
.named("ldap.config")
.stores()
.ldap()
// connection configuration
.baseDN("dc=jboss,dc=org")
.bindDN("uid=admin,ou=system")
.bindCredential("passwd")
.url("ldap://localhost:389")
// mapping configuration
.mapping(Agent.class)
.baseDN("ou=Agent,dc=jboss,dc=org")
.objectClasses("account")
.attribute("loginName", "uid", true)
.readOnlyAttribute("createdDate", "createTimeStamp")
.mapping(User.class)
.baseDN("ou=User,dc=jboss,dc=org")
.objectClasses("inetOrgPerson", "organizationalPerson")
.attribute("loginName", "uid", true)
.attribute("firstName", "cn")
.attribute("lastName", "sn")
.attribute("email", EMAIL)
.readOnlyAttribute("createdDate", "createTimeStamp")
.mapping(Role.class)
.baseDN("ou=Roles,dc=jboss,dc=org")
.objectClasses("role")
.attribute("name", "cn", true)
.readOnlyAttribute("createdDate", "createTimeStamp")
.mapping(Group.class)
.hierarchySearchDepth(4)
.objectClasses("group")
.attribute("name", "cn", true)
.readOnlyAttribute("createdDate", "createTimeStamp")
.parentMembershipAttributeName("member")
.mapping(Grant.class)
.forMapping(Role.class)
.attribute("assignee", "member")
.mapping(GroupMembership.class)
.forMapping(Group.class)
.attribute("member", "member");
The connection to your LDAP server can be configured as follows:
.ldap()
.baseDN("dc=jboss,dc=org")
.bindDN("uid=admin,ou=system")
.bindCredential("passwd")
.url("ldap://localhost:389")
You can also provide additional connection Properties
that will be used when creating the LdapContext
.
.ldap()
.connectionProperties(myProperties)
The table below describes each configuration option:
Table 9.1. LDAP Connection Configuration Options
Option | Description |
---|---|
baseDN
| Sets the base DN for a specific mapped type or all types. |
bindDN
| Sets the the DN used to bind against the ldap server. If you want to perform write operations the DN must have permissions on the agent,user,role and group contexts. |
bindCredential
| Sets the password for the bindDN. |
url
| Sets the url that should be used to connect to the server. Eg.: ldap://<<server>>:389 . |
connectionProperties
| Set a Properties instance from where additional connection properties will be retrieved
from when creating the LdapContext .
|
The LDAP configuration provides a simple mapping between your identity types and their corresponding LDAP entries. The way you map your types have a huge impact on how the LDAP Identity Store performs its operations.
Usually, a mapping is done as follows:
IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();
builder
.named("ldap.config")
.stores()
.ldap()
.mapping(User.class)
.baseDN("ou=User,dc=jboss,dc=org")
.objectClasses("inetOrgPerson", "organizationalPerson")
.attribute("loginName", "uid", true)
.attribute("firstName", "cn")
.attribute("lastName", "sn")
.attribute("email", "mail")
.readOnlyAttribute("createdDate", "createTimeStamp")
For each mapping you need to provide the identity type being mapped (in the case above the User
type)
plus all information required to store the type and populate its properties from their corresponding LDAP attributes.
In the example above, we're considering that User
entries are located at the baseDN "ou=User,dc=jboss,dc=org".
The baseDN is a very important information, specially if you want to store information from a type instance. Beside that,
the baseDN can have a huge impact on performance when querying your LDAP entries for a specific type, as
the search will be more restrictive and consider only those entries located at the baseDN and sub entries.
Another important configuration is the objectClass list related with a type. The objectClass is very important when storing new entries in your LDAP server. Also, the objectClass helps the LDAP Identity Store to make better queries against your server by restricting which entries should be considered during the search based on the objectClass list you provide.
In order to store and retrieve attributes from the LDAP server, you need to map them to the properties of your type. The attribute mapping is pretty simple, you just provide the name of the property being mapped and its corresponding LDAP attribute name. An important aspect when mapping the attributes is that you should always configure an attribute as the identifier. In the example above, we're telling the LDAP configuration to consider the following attribute as an identifier:
.mapping(User.class)
.attribute("loginName", "uid", true)
As mentioned before, the relationship support of the LDAP Identity Store is limited. But you can always map the most
common relationships such as Grant
and GroupMembership
.ldap()
.mapping(Grant.class)
.forMapping(Role.class)
.attribute("assignee", "member"))
When mapping a relationship type you need to configure which identity type is the owner of a relationship. For example,
when mapping a Grant
relationship, the LDAP attribute used to map the association between a role and other types
is the member attribute. This attribute belongs to role entries on the LDAP server, what makes the Role
type the owner of this relationship. For last, we need to tell which property on the Grant
type is related
with the associated entries. In the case of the Grant
relationship, we're configuring the assignee
property to store the associated type instances.
The LDAP configuration supports the mapping of simple hierarchies (parent/child) of a single type. This is specially useful when mapping groups, for example. Where groups can have a parent and also child groups.
.ldap()
.mapping(Group.class)
.parentMembershipAttributeName("member")
In the example above, we're using the member attribute from LDAP to store the childs of a parent group.
In some cases, the performance can be impacted when retrieving parent/child hierarchies from the LDAP server. By default, the LDAP Identity Store is configure to resolve only three levels of hierarchies. But you can always override this configuration as follows:
.ldap()
.mapping(Group.class)
.hierarchySearchDepth(1)
In the example above, we're telling the LDAP Identity Store to consider only one level depth. Which means that only the direct parent of a group will be resolved.
Sometimes may be useful to map a specific group to a specific context or DN.
The following configuration maps the group with path /QA Group to ou=QA,ou=Groups,dc=jboss,dc=org
mapping(Group.class)
.baseDN(embeddedServer.getGroupDnSuffix())
.objectClasses(GROUP_OF_NAMES)
.attribute("name", CN, true)
.readOnlyAttribute("createdDate", CREATE_TIMESTAMP)
.parentMembershipAttributeName("member")
.parentMapping("QA Group", "ou=QA,ou=Groups,dc=jboss,dc=org")
With this configuration you can have groups with the same name, but with different paths.
IdentityManager identityManager = getIdentityManager();
Group managers = new SimpleGroup("managers");
identityManager.add(managers); // group's path is /manager
Group qaGroup = identityManager.getGroup("QA Group");
Group managersQA = new SimpleGroup("managers", qaGroup);
// the QA Group is mapped to a different DN.
Group qaManagerGroup = identityManager.add(managersQA); // group's path is /QA Group/managers