JBoss.orgCommunity Documentation

Chapter 6. Identity Management - Configuration

6.1. Configuration
6.1.1. Architectural Overview
6.1.2. Programmatic Configuration
6.1.3. Security Context Configuration
6.1.4. Identity Store Feature Set
6.1.5. IDM configuration from XML file

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.

Actually it's possible to configure IDM with XML configuration. This possibility is good especially in case when you want Picketlink IDM to be part of bigger system and your users won't have a possibility to change source code and so they can't configure it programmatically with the Builder API. So they will just need to change the configuration in XML file instead of doing some changes directly in source code.

Whole Picketlink project provides unified format of configuration file, so that you can configure federation and IDM in same file.


<PicketLink xmlns="urn:picketlink:identity-federation:config:2.1">

  <PicketLinkIDP xmlns="urn:picketlink:identity-federation:config:1.0"
                 ServerEnvironment="tomcat" BindingType="POST" SupportsSignatures="true">
    <!-- SAML2 IDP configuration is here -->
  </PicketLinkIDP>

  <Handlers xmlns="urn:picketlink:identity-federation:handler:config:2.1">
     <!-- Configuration of SAML2 handlers is here -->
  </Handlers>

  <PicketLinkSTS xmlns="urn:picketlink:identity-federation:config:1.0"
                 STSName="Test STS" TokenTimeout="7200" EncryptToken="true">
    <!-- Configuration of Picketlink STS is here -->
  </PicketlinkSTS>

  <PicketLinkIDM>
    <!-- IDM configuration is here -->
  </PicketLinkIDM>

</PicketLink>

Note that if you don't want to use Picketlink Federation, you can omit it's configuration and use just IDM.

XML configuration is leveraging Builder API and Java reflection during it's parsing, so names of XML elements are actually same like names of particular Builder methods.

For example, let's assume that you want to use FileIdentityStore and your programmatic configuration looks like this:

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();


builder
    .named(SIMPLE_FILE_STORE_CONFIG)
        .stores()
            .file()
            .preserveState(false)
            .supportGlobalRelationship(Relationship.class)
            .supportAllFeatures();

Same XML configuration to configure IDM with FileIdentityStore will look like this:


<PicketLink xmlns="urn:picketlink:identity-federation:config:2.1">

  <PicketLinkIDM>

    <named value="SIMPLE_FILE_STORE_CONFIG">
      <stores>
        <file>
          <preserveState value="false" />
          <supportGlobalRelationship value="org.picketlink.idm.model.Relationship" />
          <supportAllFeatures />
        </file>
      </stores>
    </named>

  </PicketLinkIDM>

</PicketLink>

You can take a look at testsuite to see more examples.