JBoss.orgCommunity Documentation

Chapter 3. Identity Management

3.1. Overview
3.2. Identity Model
3.2.1. Architectural Overview
3.3. Configuration
3.3.1. Architectural Overview
3.3.2. Programmatic Configuration
3.3.3. Security Context Configuration
3.3.4. Identity Store Feature Set
3.3.5. Identity Store Configurations
3.3.6. JPAIdentityStoreConfiguration
3.3.7. LDAPIdentityStoreConfiguration
3.3.8. FileIdentityStoreConfiguration
3.3.9. Providing a Custom IdentityStore
3.4. Java EE Environments
3.5. Using the IdentityManager
3.5.1. Accessing the IdentityManager in Java EE
3.5.2. Accessing the IdentityManager in Java SE
3.6. Managing Users, Groups and Roles
3.6.1. Managing Users
3.6.2. Managing Groups
3.7. Managing Relationships
3.7.1. Built In Relationship Types
3.7.2. Creating Custom Relationships
3.8. Authentication
3.9. Managing Credentials
3.10. Credential Handlers
3.10.1. The CredentialStore interface
3.11. Built-in Credential Handlers
3.11.1.
3.12. Advanced Topics
3.12.1. Multi Realm Support

PicketLink's Identity Management (IDM) features provide a rich and extensible API for managing the users, groups and roles of your applications and services. The org.picketlink.idm.IdentityManager interface declares all the methods required to create, update and delete Identity objects and create relationships between them such as group and role memberships.

Interaction with the backend store that provides the persistent identity state is performed by configuring one or more IdentityStores. PicketLink provides a few built-in IdentityStore implementations for storing identity state in a database, file system or LDAP directory server, and it is possible to provide your own custom implementation to support storing your application's identity data in other backends, or extend the built-in implementations to override their default behaviour.

Before PicketLink IDM can be used, it must first be configured. See the configuration section below for details on how to configure IDM for both Java EE and Java SE environments.

PicketLink's identity model consists of a number of core interfaces that define the fundamental identity types upon which much of the Identity Management API is based. The following class diagram shows the classes and interfaces in the org.picketlink.idm.model package that form the base identity model.

  • AttributedType is the base interface for the identity model. It declares a number of methods for managing a set of attribute values, plus getId() and setId() methods for setting a unique UUID value.

  • Attribute is used to represent an attribute value. An attribute has a name and a (generically typed) value, and may be marked as read-only. Attribute values that are expensive to load (such as large binary data) may be lazy-loaded; the isLoaded() method may be used to determine whether the Attribute has been loaded or not.

  • IdentityType is the base interface for Identity objects. It declares properties that indicate whether the identity object is enabled or not, optional created and expiry dates, plus methods to read and set the owning Partition.

  • Agent represents a unique entity that may access the services secured by PicketLink. In contrast to a user which represents a human, Agent is intended to represent a third party non-human (i.e. machine to machine) process that may authenticate and interact with your application or services. It declares methods for reading and setting the Agent's login name.

  • User represents a human user that accesses your application and services. In addition to the login name property defined by its parent interface Agent, the User interface declares a number of other methods for managing the user's first name, last name and e-mail address.

  • Group is used to manage collections of identity types. Each Group has a name and an optional parent group.

  • Role is used in various relationship types to designate authority to another identity type to perform various operations within an application. For example, a forum application may define a role called moderator which may be assigned to one or more Users or Groups to indicate that they are authorized to perform moderator functions.

The following diagram shows the main components that realize PicketLink Identity Management:

  • IdentityConfiguration is the the class responsible for holding all PicketLink configuration options. This class is usually built using the Configuration Builder API, which we'll cover in the next sections. Once created and populated with the configuration options, an instance is used to create a IdentityManagerFactory.

  • IdentityManagerFactory is the class from which IdentityManager instances are created for a specific realm, considering all configurations provided by a IdentityConfiguration instance.

  • SecurityContextFactory is an interface that provides methods for creating SecurityContext instances. This component knows how to properly create and prepare the context that will be propagated during identity management operations.

  • SecurityContext is the class that holds context data that will be used during the execution of identity management operations. Once created, the context is used to create IdentityStore instances and to invoke their methods.

    This component allows to share data between the IdentityManager and IdentityStore instances. And also provides direct access for some IDM subsystems such as: event handling, caching and so on.

    Beyond that, this component is critical when access to external resources are required, such as the current EntityManager when using a JPA-based store.

    Each IdentityManager instance is associated with a single SecurityContext.

  • StoreFactory is an interface that provides methods for creating IdentityStore instances. Instances are created considering the Feature Set supported by each identity store and also the current SecurityContext instance.

  • IdentityStore is an interface that provides a contract for implementations that store data using a specific repository such as: LDAP, databases, file system, etc.

    It is a critical component as it provides all the necessary logic about how to store data.

  • IdentityManager is an interface that provides a simple access for all identity management operations using one or more of the configured identity stores.

    All functionality provided by PicketLink is available from this interface, from where applications will interact most of the time.

For most use cases, users will only work with the IdentityManagerFactory and IdentityManager classes. Only advanced use cases may require a deep knowledge about other components in order to customize the default behaviour/implementation to suit a specific requirement.

The diagram below shows an overview about how a specific identity management operation is realized:

  • 1 - The Application creates an IdentityManagerFactory instance from a previously created IdentityConfiguration. At this point, the factory reads the configuration and bootstraps the identity management ecosystem.

  • 1.1 - The IdentityManagerFactory initializes the SecurityContextFactory.

  • 1.2 - The IdentityManagerFactory initializes the StoreFactory.

  • 2 - With a fully initialized IdentityManagerFactory instance, the Application is able to create IdentityManager instances and execute operations. IdentityManager instances are created for a specific realm, in this specific case we're creating an instance using the default realm.

  • 2.1 and 2.1.1 - An IdentityManager instance is always associated with a SecurityContext. The SecurityContext is created and set into the IdentityManager instance. The same security context is used during the entire lifecycle of the IdentityManager, it will be used to share state with the underlying identity stores and provide access to external resources (if necessary) in order to execute operations.

    At this time, the IdentityManager is also configured to hold a reference to the StoreFactory in order to execute the operations against the underlying/configured IdentityStore instances.

  • 3 - Now the application holds a reference to the IdentityManager instance and it is ready to perform identity management operations (eg.: add an user, queries, validate credentials, etc).

  • 3.1 and 3.1.1 - But before executing the operations, the IdentityManager needs to obtain from the StoreFactory the IdentityStore instance that should be used to execute a specific operation. Identity stores are selected by examining the configuration to see which store configuration supports a specific operation or feature.

  • 3.2 - Now that the IdentityManager have selected which IdentityStore instance should be used, this last is invoked in order to process the operation.

  • 3.2.1 - Usually, during the execution of an operation, the IdentityStore uses the current SecurityContext. The SecurityContext can hold some state that may be useful during the execution (eg.: the JPA store uses the security context to gain access to the current EntityManager instance) and also provide access for some IDM internal services like event handling, caching, etc.

  • 3.2.2 - Finally, the IdentityStore executes the operation and persist or retrieve identity data from the underlying repository.

PicketLink IDM design is quite flexible and allows you to configure or even customize most of the behaviours described above. As stated earlier, most use cases require minimal knowledge about these details and the default implementation should be enough to satisfy the majority of requirements.

Configuration in PicketLink is in essence quite simple; an IdentityConfiguration object must first be created to hold the PicketLink configuration options. Once all configuration options have been set, you just create a IdentityManagerFactory instance passing the previously created configuration. The IdentityManagerFactory can then be used to create IdentityManager instances via the createIdentityManager() method.

The IdentityConfiguration is usually created using a Configuration Builder API, which provides a rich and fluent API for every single aspect of PicketLink configuration.

Note

For now, all configuration is set programmatically using the Configuration Builder API only. Later versions will also support a declarative configuration in a form of XML documents.

Each IdentityManager instance has its own security context, represented by the SecurityContext class. The security context contains temporary state which is maintained for one or more identity management operations within the scope of a single realm or tier. The IdentityManager (and its associated SecurityContext) is typically modelled as a request-scoped object (for environments which support such a paradigm, such as a servlet container), or alternatively as an actor within the scope of a transaction. In the latter case, the underlying resources being utilised by the configured identity stores (such as a JPA EntityManager) would participate in the active transaction, and changes made as a result of any identity management operations would either be committed or rolled back as appropriate for the logic of the encapsulating business method.

The following sections describe various ways that configuration may be performed in different environments.

Configuration for Identity Management can be defined programmatically using the Configuration Builder API. The aim of this API is to make it easier to chain coding of configuration options in order to speed up the coding itself and make the configuration more readable.

Let's assume that you want to quick start with PicketLink Identity Management features using a file-based Identity Store. First, a fresh instance of IdentityConfiguration is created using the IdentityConfigurationBuilder helper object, where we choose which identity store we want to use (in this case a file-based store) and any other configuration option, if necessary. Finally, we use the configuration to create a IdentityManagerFactory from where we can create IdentityManager instances and start to perform Identity Management operations:

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();

        
builder
  .stores()
  .file()
  .supportAllFeatures();
IdentityConfiguration configuration = builder.build();
IdentityManagerFactory identityManagerFactory = new IdentityManagerFactory(configuration);
IdentityManager identityManager = identityManagerFactory.createIdentityManager();
User user = new SimpleUser("john");
identityManager.add(user);

The SecurityContext plays an important role in the PicketLink IDM achitecture. As discussed in the Architectural Overview, it is strongly used during the execution of operations. It carries very sensitive and contextual information for a specific operation and provides access for some of the IDM underlying services such as caching, event handling, UUID generator for IdentityTpe and Relationship instances, among others.

Operations are always executed by a specific IdentityStore in order to persist or store identity data using a specific repository (eg.: LDAP, databases, filesystem, etc). When executing a operation the identity store must be able to:

  • Access the current Partition. All operations are executed for a specific Realm or Tier

  • Access the current IdentityManager instance, from which the operation was executed.

  • Access the Event Handling API in order to fire events such as when an user is created, updated, etc.

  • Access the Caching API in order to cache identity data and increase performance.

  • Access the Credential Handler API in order to be able to update and validate credentials.

  • Access to external resources, provided before the operation is executed and initialized by a ContextInitializer.

Sometimes you may need to provide additional configuration or even references for external resources before the operation is executed by an identity store. An example is how you tell to theJPAIdentityStore which EntityManager instance should be used. When executing an operation, the JPAIdentityStore must be able to access the current EntityManager to persist or retrieve data from the database. You need someway to populate the SecurityContext with such information. When you're configuring an identity store, there is a configuration option that allows you to provide a ContextInitializer implementation.

public interface ContextInitializer {    

    void initContextForStore(SecurityContext context, IdentityStore<?> store);
}

The method initContextForStore will be invoked for every single operation and before its execution by the identity store. It can be implemented to provide all the necessary logic to initialize and populate the SecurityContext for a specific IdentityStore.

The configuration is also very simple, you just need to provide the following configuration:

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();


builder
  .stores()
    .file()
      .addContextInitializer(new MySecurityContextInitializer());  
}

You can provide multiple initializers.

Note

Remember that initializers are executed for every single operation. Also, the same instance is used between operations which means your implementation should be stateless. You should be careful about the implementation in order to not impact performance, concurrency or introduce unexpected behaviors.

When configuring identity stores you must tell which features and operations should be executed by them. Features and operations are a key concept if you want to mix stores in order to execute operations against different repositories.

PicketLink provides a Java enum, called FeatureGroup, in which are defined all supported features. The table bellow summarize them:


The features are a determinant factor when choosing an identity store to execute a specific operation. For example, if an identity store is configured with FeatureGroup.user we're saying that all User operations should be executed by this identity store. The same goes for FeatureGroup.credential, we're just saying that credentials can also be updated and validated using the identity store.

Beside that, provide only the feature is not enough. We must also tell the identity store which operations are supported by a feature. For example, we can configure a identity store to support only read operations for users, which is very common when using the LDAP identity store against a read-only tree. Operations are also defined by an enum, called FeatureOperation, as follows:


During the configuration you can provide which features and operations should be supported using the Configuration API. You don't need to be forced to specify them individually, if you want to support all features and operations for a particular identity store you can use:

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();


builder
  .stores()
    .file()
      .supportAllFeatures();  
}

For a more granular configuration you can also use:

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();


builder
  .stores()
    .file()
      .supportFeature(
        FeatureGroup.agent, 
        FeatureGroup.user, 
        FeatureGroup.role, 
        FeatureGroup.group)  
}

The configuration above defines the features individually. In this case the configured features are also supporting all operations. If you want to specify which operation should be supported by a feature you can use:

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();


builder
  .stores()
    .file()
      .supportFeature(FeatureGroup.agent, FeatureOperation.read)
      .supportFeature(FeatureGroup.user, FeatureOperation.read))
      .supportFeature(FeatureGroup.role, FeatureOperation.create))
      .supportFeature(FeatureGroup.role, FeatureOperation.read))
      .supportFeature(FeatureGroup.role, FeatureOperation.update))
      .supportFeature(FeatureGroup.role, FeatureOperation.delete))  
      .supportFeature(FeatureGroup.group, FeatureOperation.create))
      .supportFeature(FeatureGroup.group, FeatureOperation.read))
      .supportFeature(FeatureGroup.group, FeatureOperation.update))
      .supportFeature(FeatureGroup.group, FeatureOperation.delete))  
}

For a more complex configuration evolving multiple identity stores with a different feature set, look at the example bellow:

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();


builder
  .stores()
    .ldap()
      .supportFeature(FeatureGroup.agent)
      .supportFeature(FeatureGroup.user)
      .supportFeature(FeatureGroup.credential)
    .jpa()
      .supportFeature(FeatureGroup.role)
      .supportFeature(FeatureGroup.group)
      .supportFeature(FeatureGroup.relationship)
}

The configuration above shows how to use LDAP to store only agents, users and credentials and database for roles, groups and relationships.

Note

Remember that identity stores must have their features and operations configured. If you don't provide them you won't be able to build the configuration.

The JPA identity store uses a relational database to store identity state. The configuration for this identity store provides control over which entity beans are used to store identity data, and how their fields should be used to store various identity-related state. The entity beans that store the identity data must be configured using the annotations found in the org.picketlink.jpa.annotations package. All identity configuration annotations listed in the tables below are from this package.

The Identity class is the entity bean that is used to store the record for users, roles and groups. It should be annotated with @IdentityType and declare the following field values:


The following code shows an example of an entity class configured to store Identity instances:


Sometimes you may need to configure how the EntityManager is provided to the JPAIdentityStore, like when your application is using CDI and you must run the operations in the scope of the current transaction by using a injected EntityManager instance.

In cases like that, you need to initialize the SecurityContext by providing a ContextInitializer implementation, as discussed in Security Context Configuration. The JPAContextInitializer is provided by PicketLink and can be used to initialize the security context with a specific EntityManager instance. You can always extend this class and provide your own way to obtain the EntityManager from your application's environment.

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();


builder
  .stores()
    .file()
      .addContextInitializer(new JPAContextInitializer(emf) {
        @Override
        public EntityManager getEntityManager() {
          // logic goes here
        }
      });
}

By default, the JPAContextInitializer creates a EntityManager from the EntityManagerFacatory provided when creating a new instance.

The LDAP identity store allows an LDAP directory server to be used to provide identity state. You can use this store in read-only or write-read mode, depending on your permissions on the server.

The LDAP identity store can be configured by providing the following configuration:

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();


builder
  .stores()
    .ldap()
      .baseDN("dc=jboss,dc=org")
      .bindDN("uid=admin,ou=system")
      .bindCredential("secret")
      .url("ldap://localhost:10389")
      .userDNSuffix("ou=People,dc=jboss,dc=org")
      .roleDNSuffix("ou=Roles,dc=jboss,dc=org")
      .groupDNSuffix("ou=Groups,dc=jboss,dc=org")
      .supportAllFeatures();

The following table describes all configuration options:


This identity store uses the file system to persist identity state. The configuration for this identity store provides control over where to store identity data and if the state should be preserved between initializatons.

Identity data is stored using the Java Serialization API.

In Java EE 6.0 and higher environments, basic configuration is performed automatically with a set of sensible defaults. During application deployment, PicketLink will scan all deployed entity beans for any beans annotated with @IDMEntity, and if found will use a configuration based on the JPAIdentityStore. If no entity beans have been configured for identity management and no other configuration is provided, a file-based identity store will be automatically configured to provide basic identity management features backed by the file system.

The org.picketlink.idm.IdentityManager interface provides access to the bulk of the IDM features supported by PicketLink. To get access to the IdentityManager depends on which environment you are using. The following two sections describe how to access the IdentityManager in both Java EE and Java SE environments.

PicketLink IDM provides a number of basic implementations of the identity model interfaces for convenience, in the org.picketlink.idm.model package. The following sections provide examples that show these implementations in action.

Relationships are used to model typed associations between two or more identities. All concrete relationship types must implement the marker interface org.picketlink.idm.model.Relationship:

The IdentityManager interface provides three standard methods for managing relationships:

  void add(Relationship relationship);

  void update(Relationship relationship);
  void remove(Relationship relationship);
  • The add() method is used to create a new relationship.

  • The update() method is used to update an existing relationship.

    Note

    Please note that the identities that participate in a relationship cannot be updated themselves, however the attribute values of the relationship can be updated. If you absolutely need to modify the identities of a relationship, then delete the relationship and create it again.

  • The remove() method is used to remove an existing relationship.

Note

To search for existing relationships between identity objects, use the Relationship Query API described later in this chapter.

Besides the above methods, IdentityManager also provides a number of convenience methods for managing many of the built-in relationship types. See the next section for more details.

PicketLink provides a number of built-in relationship types, designed to address the most common requirements of a typical application. The following sections describe the built-in relationships and how they are intended to be used. Every built-in relationship type extends the AbstractAttributedType abstract class, which provides the basic methods for setting a unique identifier value and managing a set of attribute values:

What this means in practical terms, is that every single relationship is assigned and can be identified by, a unique identifier value. Also, arbitrary attribute values may be set for all relationship types, which is useful if you require additional metadata or any other type of information to be stored with a relationship.

The GroupMembership and GroupRole relationships are used to represent a user's membership within a Group, and a user's role for a group, respectively.

Note

While the GroupRole relationship type extends GroupMembership, it does not mean that a member of a GroupRole automatically receives GroupMembership membership also - these are two distinct relationship types with different semantics.

A Group is typically used to form logical collections of users. Within an organisation, groups are often used to mirror the organisation's structure. For example, a corporate structure might consist of a sales department, administration, management, etc. This structure can be modelled in PicketLink by creating corresponding groups such as sales, administration, and so forth. Users (who would represent the employees in a corporate structure) may then be assigned group memberships corresponding to their place within the company's organisational structure. For example, an employee who works in the sales department may be assigned to the sales group. Specific application privileges can then be blanket assigned to the sales group, and anyone who is a member of the group is free to access the application's features that require those privileges.

The GroupRole relationship type should be used when it is intended for an identity to perform a specific role for a group, but not be an actual member of the group itself. For example, an administrator of a group of doctors may not be a doctor themselves, but have an administrative role to perform for that group. If the intent is for an individual identity to both be a member of a group and have an assigned role in that group also, then the identity should have both GroupRole and GroupMembership relationships for that group.

Let's start by looking at a simple example - we'll begin by making the assumption that our organization is structured in the following way:

The following code demonstrates how we would create the hypothetical Sales group which is displayed at the head of the above organisational chart:

  Group sales = new SimpleGroup("Sales");

  identityManager.add(sales);

We can then proceed to create its subgroups:

identityManager.add(new SimpleGroup("North America", sales);

identityManager.add(new SimpleGroup("EMEA", sales);
identityManager.add(new SimpleGroup("Asia", sales);
// and so forth

The second parameter of the SimpleGroup() constructor is used to specify the group's parent group. This allows us to create a hierarchical group structure, which can be used to mirror either a simple or complex personnel structure of an organisation. Let's now take a look at how we assign users to these groups.

The following code demonstrates how to assign an administrator group role for the Northeast sales group to user jsmith. The administrator group role may be used to grant certain users the privilege to modify permissions and roles for that group:

Role admin = identityManager.getRole("administrator");

User user = identityManager.getUser("jsmith");
Group group = identityManager.getGroup("Northeast");
identityManager.grantGroupRole(user, admin, group);

A group role can be revoked using the revokeGroupRole() method:

identityManager.revokeGroupRole(user, admin, group);

To test whether a user has a particular group role, you can use the hasGroupRole() method:

boolean isUserAGroupAdmin = identityManager.hasGroupRole(user, admin, group);

Next, let's look at some examples of how to work with simple group memberships. The following code demonstrates how we assign sales staff rbrown to the Northeast sales group:

User user = identityManager.getUser("rbrown");

Group group = identityManager.getGroup("Northeast");
identityManager.addToGroup(user, group);

A User may also be a member of more than one Group; there are no built-in limitations on the number of groups that a User may be a member of.

We can use the removeFromGroup() method to remove the same user from the group:

identityManager.removeFromGroup(user, group);

To check whether a user is the member of a group we can use the isMember() method:

boolean isUserAMember = identityManager.isMember(user, group);

Relationships can also be created via the add() method. The following code is equivalent to assigning a group role via the grantGroupRole() method shown above:

Role admin = identityManager.getRole("administrator");

User user = identityManager.getUser("jsmith");
Group group = identityManager.getGroup("Northeast");
GroupRole groupRole = new GroupRole(user, group, admin);
identityManager.add(groupRole);

One of the strengths of PicketLink is its ability to support custom relationship types. This extensibility allows you, the developer to create specific relationship types between two or more identities to address the domain-specific requirements of your own application.

Note

Please note that custom relationship types are not supported by all IdentityStore implementations - see the Identity Store section above for more information.

To create a custom relationship type, we start by creating a new class that implements the Relationship interface. To save time, we also extend the AbstractAttributedType abstract class which takes care of the identifier and attribute management methods for us:

  public class Authorization extends AbstractAttributedType implements Relationship {

        
  }

The next step is to define which identities participate in the relationship. Once we create our identity property methods, we also need to annotate them with the org.picketlink.idm.model.annotation.RelationshipIdentity annotation. This is done by creating a property for each identity type.

  private User user;

  private Agent application;
  
  @RelationshipIdentity
  public User getUser() {
    return user;
  }
  
  public void setUser(User user) {
    this.user = user;
  }
  
  @RelationshipIdentity
  public Agent getApplication() {
    return application;
  }
  
  public void setApplication(Agent application) {
    this.application = application;
  }
We can also define some attribute properties, using the @RelationshipAttribute annotation:
  private String accessToken;

  
  @RelationshipAttribute
  public String getAccessToken() {
    return accessToken;
  }
  
  public void setAccessToken(String accessToken) {
    this.accessToken = accessToken;
  }

Note

While the IDM module of PicketLink provides authentication features, for common use cases involving standard username and password based authentication in a Java EE environment, PicketLink provides a more streamlined method of authentication. Please refer to the authentication chapter of this documentation for more information.

PicketLink IDM provides an authentication subsystem that allows user credentials to be validated thereby confirming that an authenticating user is who they claim to be. The IdentityManager interface provides a single method for performing credential validation, as follows:

void validateCredentials(Credentials credentials);

The validateCredentials() method accepts a single Credentials parameter, which should contain all of the state required to determine who is attempting to authenticate, and the credential (such as a password, certificate, etc) that they are authenticating with. Let's take a look at the Credentials interface:

public interface Credentials {

    public enum Status {
        UNVALIDATED, IN_PROGRESS, INVALID, VALID, EXPIRED
    };
   Agent getValidatedAgent();
   
   Status getStatus();
   
   void invalidate();
}
  • The Status enum defines the following values, which reflect the various credential states:

    • UNVALIDATED - The credential is yet to be validated.

    • IN_PROGRESS - The credential is in the process of being validated.

    • INVALID - The credential has been validated unsuccessfully

    • VALID - The credential has been validated successfully

    • EXPIRED - The credential has expired

  • getValidatedAgent() - If the credential was successfully validated, this method returns the Agent object representing the validated user.

  • getStatus() - Returns the current status of the credential, i.e. one of the above enum values.

  • invalidate() - Invalidate the credential. Implementations of Credential should use this method to clean up internal credential state.

Let's take a look at a concrete example - UsernamePasswordCredentials is a Credentials implementation that supports traditional username/password-based authentication:

public class UsernamePasswordCredentials extends AbstractBaseCredentials {


    private String username;
    private Password password;
    public UsernamePasswordCredentials() { }
    public UsernamePasswordCredentials(String userName, Password password) {
        this.username = userName;
        this.password = password;
    }
    public String getUsername() {
        return username;
    }
    public UsernamePasswordCredentials setUsername(String username) {
        this.username = username;
        return this;
    }
    public Password getPassword() {
        return password;
    }
    public UsernamePasswordCredentials setPassword(Password password) {
        this.password = password;
        return this;
    }
    @Override
    public void invalidate() {
        setStatus(Status.INVALID);
        password.clear();
    }
}

The first thing we may notice about the above code is that the UsernamePasswordCredentials class extends AbstractBaseCredentials. This abstract base class implements the basic functionality required by the Credentials interface. Next, we can see that two fields are defined; username and password. These fields are used to hold the username and password state, and can be set either via the constructor, or by their associated setter methods. Finally, we can also see that the invalidate() method sets the status to INVALID, and also clears the password value.

Let's take a look at an example of the above classes in action. The following code demonstrates how we would authenticate a user with a username of "john" and a password of "abcde":

Credentials creds = new UsernamePasswordCredentials("john",

    new Password("abcde"));
identityManager.validate(creds);
if (Status.VALID.equals(creds.getStatus())) {
  // authentication was successful
}

We can also test if the credentials that were provided have expired (if an expiry date was set). In this case we might redirect the user to a form where they can enter a new password.

Credentials creds = new UsernamePasswordCredentials("john",

    new Password("abcde"));
identityManager.validate(creds);
if (Status.EXPIRED.equals(creds.getStatus())) {
  // password has expired, redirect the user to a password change screen
}

Updating user credentials is even easier than validating them. The IdentityManager interface provides the following two methods for updating credentials:

void updateCredential(Agent agent, Object credential);

void updateCredential(Agent agent, Object credential, Date effectiveDate, Date expiryDate);

Both of these methods essentially do the same thing; they update a credential value for a specified Agent (or User). The second overloaded method however also accepts effectiveDate and expiryDate parameters, which allow some temporal control over when the credential will be valid. Use cases for this feature include implementing a strict password expiry policy (by providing an expiry date), or creating a new account that might not become active until a date in the future (by providing an effective date). Invoking the first overloaded method will store the credential with an effective date of the current date and time, and no expiry date.

Note

One important point to note is that the credential parameter is of type java.lang.Object. Since credentials can come in all shapes and sizes (and may even be defined by third party libraries), there is no common base interface for credential implementations to extend. To support this type of flexibility in an extensible way, PicketLink provides an SPI that allows custom credential handlers to be configured that override or extend the default credential handling logic. Please see the next section for more information on how this SPI may be used.

PicketLink provides built-in support for the following credential types:

Warning

Not all built-in IdentityStore implementations support all credential types. For example, since the LDAPIdentityStore is backed by an LDAP directory server, only password credentials are supported. The following table lists the built-in IdentityStore implementations that support each credential type.


Let's take a look at a couple of examples. Here's some code demonstrating how a password can be assigned to user "jsmith":

User user = identityManager.getUser("jsmith");

identityManager.updateCredential(user, new Password("abcd1234"));

This example creates a digest and assigns it to user "jdoe":

User user = identityManager.getUser("jdoe");

Digest digest = new Digest();
digest.setRealm("default");
digest.setUsername(user.getLoginName());
digest.setPassword("abcd1234");        
identityManager.updateCredential(user, digest);

For IdentityStore implementations that support multiple credential types, PicketLink provides an optional SPI to allow the default credential handling logic to be easily customized and extended. To get a better picture of the overall workings of the Credential Handler SPI, let's take a look at the sequence of events during the credential validation process when validating a username and password against JPAIdentityStore:

  • 1 - The user (or some other code) first invokes the validateCredentials() method on IdentityManager, passing in the Credentials instance to validate.

  • 1.1 - After looking up the correct IdentityStore (i.e. the one that has been configured to validate credentials) the IdentityManager invokes the store's validateCredentials() method, passing in the SecurityContext and the credentials to validate.

  • 1.1.1 - In JPAIdentityStore's implementation of the validateCredentials() method, the SecurityContext is used to look up the CredentialHandler implementation that has been configured to process validation requests for usernames and passwords, which is then stored in a local variable called handler.

  • 1.1.2 - The validate() method is invoked on the CredentialHandler, passing in the security context, the credentials value and a reference back to the identity store. The reference to the identity store is important as the credential handler may require it to invoke certain methods upon the store to validate the credentials.

The CredentialHandler interface declares three methods, as follows:

public interface CredentialHandler {

    void setup(IdentityStore<?> identityStore);
    void validate(SecurityContext context, Credentials credentials, 
                  IdentityStore<?> identityStore);
    void update(SecurityContext context, Agent agent, Object credential, 
                IdentityStore<?> identityStore, Date effectiveDate, Date expiryDate);
}

The setup() method is called once, when the CredentialHandler instance is first created. Credential handler instantiation is controlled by the CredentialHandlerFactory, which creates a single instance of each CredentialHandler implementation to service all credential requests for that handler. Each CredentialHandler implementation must declare the types of credentials that it is capable of supporting, which is done by annotating the implementation class with the @SupportsCredentials annotation like so:

@SupportsCredentials({ UsernamePasswordCredentials.class, Password.class })

public class PasswordCredentialHandler implements CredentialHandler {

Since the validate() and update() methods receive different parameter types (validate() takes a Credentials parameter value while update() takes an Object that represents a single credential value), the @SupportsCredentials annotation must contain a complete list of all types supported by that handler.

Similarly, if the IdentityStore implementation makes use of the credential handler SPI then it also must declare which credential handlers support that identity store. This is done using the @CredentialHandlers annotation; for example, the following code shows how JPAIdentityStore is configured to be capable of handling credential requests for usernames and passwords, X509 certificates and digest-based authentication:

@CredentialHandlers({ PasswordCredentialHandler.class,

          X509CertificateCredentialHandler.class, DigestCredentialHandler.class })
public class JPAIdentityStore implements IdentityStore<JPAIdentityStoreConfiguration>, 
                                         CredentialStore {

For IdentityStore implementations that support multiple credential types (such as JPAIdentityStore and FileBasedIdentityStore), the implementation may choose to also implement the CredentialStore interface to simplify the interaction between the CredentialHandler and the IdentityStore. The CredentialStore interface declares methods for storing and retrieving credential values within an identity store, as follows:

public interface CredentialStore {

   void storeCredential(SecurityContext context, Agent agent, 
                        CredentialStorage storage);
   <extends CredentialStorage> T retrieveCurrentCredential(SecurityContext context, 
                                                 Agent agent, Class<T> storageClass);
   <extends CredentialStorage> List<T> retrieveCredentials(SecurityContext context, 
                                                 Agent agent, Class<T> storageClass);
}

The CredentialStorage interface is quite simple and only declares two methods, getEffectiveDate() and getExpiryDate():

public interface CredentialStorage {

   @Stored Date getEffectiveDate();
   @Stored Date getExpiryDate();
}

The most important thing to note above is the usage of the @Stored annotation. This annotation is used to mark the properties of the CredentialStorage implementation that should be persisted. The only requirement for any property values that are marked as @Stored is that they are serializable (i.e. they implement the java.io.Serializable interface). The @Stored annotation may be placed on either the getter method or the field variable itself. Here's an example of one of a CredentialStorage implementation that is built into PicketLink - EncodedPasswordStorage is used to store a password hash and salt value:

public class EncodedPasswordStorage implements CredentialStorage {


    private Date effectiveDate;
    private Date expiryDate;
    private String encodedHash;
    private String salt;
    @Override @Stored
    public Date getEffectiveDate() {
        return effectiveDate;
    }
    public void setEffectiveDate(Date effectiveDate) {
        this.effectiveDate = effectiveDate;
    }
    @Override @Stored
    public Date getExpiryDate() {
        return expiryDate;
    }
    public void setExpiryDate(Date expiryDate) {
        this.expiryDate = expiryDate;
    }
    @Stored
    public String getEncodedHash() {
        return encodedHash;
    }
    public void setEncodedHash(String encodedHash) {
        this.encodedHash = encodedHash;
    }
    @Stored
    public String getSalt() {
        return this.salt;
    }
    public void setSalt(String salt) {
        this.salt = salt;
    }
}

This section describes each of the built-in credential handlers, and any configuration parameters that may be set for them. Specific credential handler options can be set when creating a new IdentityConfiguration. Configured options are always specific to a particular identity store configuration, allowing different options to be specified between two or more identity stores. The IdentityStoreConfiguration interface provides a method called getCredentialHandlersConfig() that provides access to a Map which allows configuration options to be set for the identity store's credential handlers:

public interface IdentityStoreConfiguration {

    Map<String, Object> getCredentialHandlerProperties();
}

To gain access to the IdentityStoreConfiguration object before PicketLink is initialized, there are a couple of options. The first option is to provide an IdentityConfiguration object itself via a producer method.



      
      

PicketLink has been designed from the ground up to support a system of partitioning, allowing the users, groups and roles of an application to be divided into Realms and Tiers.

A Realm is used to define a discrete set of users, groups and roles. A typical use case for realms is the segregation of corporate user accounts within a multi-tenant application, although it is not limited this use case only. As all identity management operations must be performed within the context of an active partition, PicketLink defines the concept of a default realm which becomes the active partition if no other partition has been specified.

A Tier is a more restrictive type of partition than a realm, as it only allows groups and roles to be defined (but not users). A Tier may be used to define a set of application-specific groups and roles, which may then be assigned to groups within the same Tier, or to users and groups within a separate Realm.

In terms of API, both the Realm and Tier classes implement the Partition interface, as shown in the following class diagram:

Selecting the specific partition that the identity management operations are performed in is controlled by specifying the partition when creating the IdentityManager via the IdentityManagerFactory's overloaded createIdentityManager() methods:

  IdentityManager createIdentityManager();

    IdentityManager createIdentityManager(Partition partition);

The first method (without parameters) will create an IdentityManager instance for the default realm. The second parameter allows a Partition object to be specified. Once the IdentityManager has been created, any identity management methods invoked on it will be performed within the selected partition. To look up the partition object, the IdentityManagerFactory provides two additional methods:

  Realm getRealm(String id);

    Tier getTier(String id);

Here's an example demonstrating how a new user called "bob" is created in a realm called acme:

  Realm acme = identityManagerFactory.getRealm("acme");

    IdentityManager im = identityManagerFactory.createIdentityManager(acme);
    im.add(new SimpleUser("bob"));