Product SiteDocumentation Site

Chapter 7. Identity Management - Configuration

7.1. Configuration

7.1.1. Architectural Overview

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 PartitionManager instance passing the previously created configuration. The PartitionManager can then be used to create Partition and IdentityManager instances.
The IdentityConfiguration is usually created using a Configuration Builder API, which provides a rich and fluent API for every single aspect of PicketLink configuration.

7.1.2. Default Configuration

If you'd like to get up and running with IDM quickly, the good news is that PicketLink will provide a default configuration that stores your identity data on the file system if no other configuration is available. This means that if you have the PicketLink libraries in your project, you can simply inject the PartitionManager, IdentityManager or RelationshipManager beans into your own application and start using them immediately:
@Inject PartitionManager partitionManager;
@Inject IdentityManager identityManager;
@Inject RelationshipManager relationshipManager;

Note

The default configuration is very useful for developing and testing purposes, as you don't need a database or a LDAP server to start managing your identity data.

7.1.3. Providing a Custom Configuration

In certain cases the default configuration may not be enough to your application. You can easily provide your own configuration by observing a specific IdentityConfigurationEvent:
public class IdentityManagementConfiguration {

  public void observeIdentityConfigurationEvent(@Observes IdentityConfigurationEvent event) {
    IdentityConfigurationBuilder builder = event.getConfig();

    // use the builder to provide your own configuration
  }
}
You can also provide your own configuration by producing one or more IdentityConfiguration instances using a @Producer annotated method:
public class IdentityManagementConfiguration {

  @Produces
  public IdentityConfiguration produceJPAConfiguration() {
      IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();

      builder
          .named("jpa.config")
              .stores()
                  .jpa()
                    .supportAllFeatures()

      return builder.build();
  }

  @Produces
  public IdentityConfiguration produceLDAPConfiguration() {
      IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();

      builder
          .named("ldap.config")
              .stores()
                  .ldap()
                      // configure the LDAP store

      return builder.build();
  }
}
The example above produces two distinct configurations: one using a JPA store and another using the LDAP store. During the startup PicketLink will resolve both configurations and initialize the IDM subsystem with them. You can also provide a single configuration.

Warning

When producing IdentityConfiguration instances the produced bean must be dependent and not normal-scoped.
For last, you can also build your own PartitionManager instance if you want more control.
@ApplicationScoped
public static class PicketLinkConfiguration {

  @PicketLink
  @Produces
  public PartitionManager producePartitionManager() {
      IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();

      builder
          .named("produced.partition.manager.config")
              .stores()
                  .jpa()
                      .supportAllFeatures();

      PartitionManager partitionManager = new DefaultPartitionManager(builder.build());

			// in this case you must initialize the identity store with a default partition
      Partition defaultPartition = new Realm(Realm.DEFAULT_REALM);

      partitionManager.add(defaultPartition); // creates the default partition

      return partitionManager;
  }

}
The example above allows you to produce your own PartitionManager instance. Note that the producer method is annotated with the PicketLink annotation.

Important

When producing your own PartitionManager is that you must manually create the partitions before start producing IdentityManager instances (eg.: the default partition)
For last, you can also provide the configuration by observing the SecurityConfigurationEvent as follows:
public class IdentityManagementConfiguration {

    public void configureIdentityManagement(@Observes SecurityConfigurationEvent event) {
        SecurityConfigurationBuilder builder = event.getBuilder();

        builder
            .idmConfig()
                .named("default.config")
                    .stores()
                        .jpa()
                            .supportAllFeatures();
    }
}
You may be asking which approach is recommended, right ? We always recommend to use the last approach, observe the SecurityConfigurationEvent. The reason is because from this event you have access to not only the IDM config but for others features provided by PicketLink.
However you may want to provide your own PartitionManager instance, instead of relying on PicketLink to create one for you. In this case you can produce a PartitionManager by your own. Or if you want different methods for each IDM config you may want to produce IdentityConfiguration instances instead.

7.1.4. Initializing the PartitionManager

You may need to initialize the PartitionManager with some data before your application starts to produce partition manager instances. PicketLink provides a specific event called PartitionManagerCreateEvent, which can be used to provide any initialization logic right after a PartitionManager instance is created and before it is consumed by any injection point in your application.
public class MyPartitionManagerInitializer {

    public void init(@Observes PartitionManagerCreateEvent event) {
        // retrieve the recently created partition manager instance
        PartitionManager partitionManager = event.getPartitionManager();

        // retrieve all the configuration used to build the instance
        Collection configurations = partitionManager.getConfigurations();
    }
}
One important thing to keep in mind when providing a observer for PartitionManagerCreateEvent is that if any partition is created during the initialization, PicketLink won't try to create the default partition.

Note

Apache TomEE users should always provide an observer for PartitionManagerCreateEvent in order to initialize the partition manager properly. Specially if using the JPA store and if no active transaction exists when the PartitionManager is being created.

7.1.5. Programmatic Configuration Overview

The Identity Management configuration 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, from 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 PartitionManager, from where we can create Partition and IdentityManager instances:
IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();
        
builder
  .named("default")
    .stores()
      .file()
        .supportAllFeatures();

DefaultPartitionManager partitionManager = new DefaultPartitionManager(builder.buildAll());

Realm defaultRealm = new Realm(Realm.DEFAULT_REALM);

// let's add the partition using the default configuration.
partitionManager.add(defaultRealm);

// if no partition is specified to createIdentityManager, defaults to the default Realm.
IdentityManager identityManager = partitionManager.createIdentityManager();

User john = new User("john");

// let's add john to the default partition
identityManager.add(user);

7.1.6. Providing Multiple Configurations

A PartitionManager can be built considering multiple configurations. This is a very powerful feature given that you can manage your identity data between different partitions each one using a specific configuration.
As discussed before, each configuration has a name. The name can be used to identify a configuration set as well to tell PicketLink the configuration that should be used to manage a specific Partition.
Let's take a more close look how you can use multiple configurations:
IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();

builder
    .named("ldap.config")
        .stores()
            .ldap()
              // specific configuration options for the LDAP store
              .supportAllFeatures();
    .named("jpa.config")
        .stores()
            .jpa()
              // specific configuration options for the JPA store
              .supportAllFeatures();

DefaultPartitionManager partitionManager = new DefaultPartitionManager(builder.buildAll());

Realm internalPartition = new Realm("internal");

// the 'internal' partition will use the 'ldap.config' configuration
partitionManager.add(internalPartition, "ldap.config");

// we create an IdentityManager for the LDAP managed partition
IdentityManager internalIdentityManager = partitionManager.createIdentityManager(internalPartition);

User john = new User("john");

// john will be added to the LDAP
internalIdentityManager.add(john);

Realm externalPartition = new Realm("external");

// the 'external' partition will use the 'jpa.config' configuration
partitionManager.add(externalPartition, "jpa.config");

User mary = new User("mary");

// we create an IdentityManager for the JPA managed partition
IdentityManager externalIdentityManager = partitionManager.createIdentityManager(externalPartition);

// mary will be added to the database
externalIdentityManager.add(mary);
The example above is just one of the different things you can do with PicketLink. The code above defines two partitions: one for internal users and another one for external users. Each partition is associated with one of the provided configurations where the internal partition will use LDAP to store users whether the external partition will use JPA.
When you create a IdentityManager for one of those partitions, all identity management operations will be done considering the configuration associated with the current partition. In other words, considering the example above, the user 'john' will be stored in the LDAP and 'mary' in a Database.

7.1.7. Providing Multiple Stores for a Configuration

It is also possible to use multiple IdentityStore configurations in a single named configuration. This can be very useful when your identity data is distributed in different stores or even if a specific store have any kind of limitation that can be provided by another one.
For instance, the LDAP store have some limitations and does not support all features provided by PicketLink. One of those unsupported features is the ability to handle ad-hoc attributes. When using LDAP you're tied with a schema that usually is very hard to change in order to support all your needs.
In this cases, PicketLink allows you to combine in a single configuration the LDAP and the JPA store, for example. Where you can use LDAP for users, roles and groups and use the JPA store for relationships.
IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();

builder
  .named("default")
    .stores()
      .jpa()
        // configuration options for the jpa store
        .supportGlobalRelationship(Relationship.class)
        .supportAttributes(true)
      .ldap()
        // configuration options for the ldap store
        .supportType(IdentityType.class)
The example above defines a single configuration with two stores: LDAP and JPA. For the LDAP store configuration we define that only IdentityType types should be supported. In other words, we're only storing users, roles and groups. For the JPA store configuration we define that only Relationship types should be supported. In other words, we're only storing relationships such as Grant, GroupMembership, etc.
You may also notice that the JPA store is configured to support attributes too. What means that we can now use ad-hoc attributes for all the supported types.

7.1.8. Configuring Credential Handlers

Each IdentityStore may support a different set of credential handlers. This documentations describes the built-in credential handlers provided by PicketLink, but sometimes you may want to provide your own implementations.
When you write your custom credential handler you need to tell PicketLink the identity store that will support it. This is done by the following code:
 IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();

 builder
   .named("default")
     .stores()
       .jpa()
         // other JPA configuration
         .addCredentialHandler(UserPasswordCredentialHandler.class)
         .supportAllFeatures();
The example above shows how to configure a credential handler for a JPA-based store using the addCredentialHandler method.

7.1.8.1. Passing parameters to Credential Handlers

Some credential handlers support a set of configuration options to configure their behavior. These options can be specified as follows:
 IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();

 builder
   .named("default")
     .stores()
       .jpa()
         // other JPA configuration
         .setCredentialHandlerProperty(PasswordCredentialHandler.PASSWORD_ENCODER, new BCryptPasswordEncoder(4))
         .supportAllFeatures();
The example above shows how to set a property for the PasswordCredentialHandler using the setCredentialHandlerProperty method.

7.1.9. Identity Context Configuration

The IdentityContext plays an important role in the PicketLink IDM achitecture. 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, id generator for AttributedType 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. Eg.: Realm or Tier.
  • 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 to external resources, provided before the operation is executed and initialized by a ContextInitializer.

7.1.9.1. Initializing the IdentityContext

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 the JPAIdentityStore 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 IdentityContext 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(IdentityContext 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 IdentityContext for a specific IdentityStore.
The configuration is also very simple:
IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();

builder
  .named("default")
    .stores()
      .file()
        .supportAllFeatures();
        .addContextInitializer(new MyContextInitializer());
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.

7.1.10. IDM configuration from XML file

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.

7.1.10.1. Unified XML configuration

Whole Picketlink project provides unified format of configuration file, so that you can configure Section 14.1, “Overview” 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.

7.1.10.2. XML configuration format

XML configuration is leveraging Section 7.1.5, “Programmatic Configuration Overview” 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.

7.1.10.3. Bootstrap IDM from XML file

So to initialize Picketlink IDM from XML you can use the code like this:
// Replace with your own configuration file
String configFilePath = "config/embedded-file-config.xml";

ClassLoader tcl = Thread.currentThread().getContextClassLoader();
InputStream configStream = tcl.getResourceAsStream(configFilePath);
XMLConfigurationProvider xmlConfigurationProvider = new XMLConfigurationProvider();
IdentityConfigurationBuilder idmConfigBuilder =
  xmlConfigurationProvider.readIDMConfiguration(configStream);
Now you can bootstrap IDM from idmConfigBuilder in same way, like it's done in Section 7.1.5, “Programmatic Configuration Overview” . Note that you can initialize builder from XML file and then you can do some additional programmatic configuration. For example, you may need to programmatically add JPAContextInitializer in case that you are using JPA, because you will need access to JPA EntityManager.