PicketLink 3 Subsystem

Version 14

    Introduction


    This article is a overview about what was done so far regarding the PicketLink Subsystem. The design and implementation was done considering the following requirements:

     

    • Provide support for all PicketLink projects
      • For now, focus on the Identity Management Services and the Authentication and Authorization services provided by PicketLink Core
      • Already provides support for Federation (SAML) deployments + Access through the PicketLink Console.
    • Allow a minimal configuration for deployments in order to use the available services
    • Support JBoss EAP 6.1.Alpha1 (AS 7.2.0.Alpha1-redhat-4)

     

    The following sections will describe each of the available services.

     

     

    The subsystem provides a domain model that allows you to configure the PicketLink Identity Management service using the standalone/domain.xml. Basically, what the subsystem does is parse the configuration, automatically build a org.picketlink.idm.IdentityManager and expose it via JNDI for further access.

     

    With the subsystem you can :

     

    • Define multiple configuration for identity management services.
    • Expose the IdentityManager via JNDI for further access.
    • If using CDI, inject the configured IdentityManager instances using the @Resource

    Deployment Configuration

     

    To use the Identity Management services you should always configure your deployment with a jboss-deployment-structure.xml as follows:

     

    <jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.1">
              <deployment>
                   <dependencies>
                        <module name="org.picketlink.idm" />
                   </dependencies>
              </deployment>
    </jboss-deployment-structure>
    

     

    Using the Identity Management subsystem domain model

     

    <subsystem xmlns="urn:jboss:picketlink:1.0">
    
           <identity-management jndi-url="picketlink/FileBasedCompleteIdentityManager" alias="file-based-complete">
               <file-store realms="default" working-dir="/tmp/pl-idm-complete"
                                  always-create-files="true" async-write="true"
                                  async-write-thread-pool="10">
                   <features supportsAll="true" />
               </file-store>
            </identity-management>
    
            <identity-management jndi-url="picketlink/DevIdentityManager" alias="development">
                <jpa-store data-source="jboss/datasources/ExampleDS" realms="default,staging,production" tiers="web,service,data">
                    <features>
                        <feature group="user" group-operation="read,create,delete,update" />
                        <feature group="role" group-operation="read,create,delete,update" />
                        <feature group="agent" group-operation="read,create,delete,update" />
                        <feature group="group" group-operation="read,create,delete,update" />
                        <feature group="credential" group-operation="validate,update" />
                    </features>
                    <relationships>
                        <relationship class="org.picketlink.idm.model.Authorization" />
                    </relationships>
                </jpa-store>
              </identity-management>
    
    </subsystem>
    

     

    Most of the configuration are known if you are familiar with the PicketLink IDM configuration. But the domain model provides some additional configuration in order to allow deployments to access the configured identity management services.

     

    Basically, each configuration must have a:

     

    • jndi-ur, that defines where the org.picketlink.idm.IdentityManager should be published in the JNDI tree for further access.
    • alias, an alias for the configuration to allow other subsystems to inject the Identity Management Services using the MSC injection infrastructure.

     

    If your application is CDI-based, you can use the @Resource annotation to automatically inject the configured IdentityManager instances:

     

        @Resource (mappedName="picketlink/DevIdentityManager")
        private IdentityManager devIdentityManager;
    
        @Test       
        public void testIdentityManager() {
            SimpleUser user = new SimpleUser("john");
    
            this.devIdentityManager.add(user);
    
            Password password = new Password("mypassWd");
    
            this.devIdentityManager.updateCredential(user, password);
    
            UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(user.getLoginName(), password);
    
            this.devIdentityManager.validateCredentials(credentials);
    
            Assert.assertEquals(Status.VALID, credentials.getStatus());
        }
    

     

    JPA-based Identity Store Configuration

     

    The JPA identity store configuration can be done in two ways:

     

    • Providing a DataSource JNDI url using the data-source attribute.
    • Providing a EntityManagerFactory JNDI url using the entity-manager-factory attribute.

     

    You should choose one or another depending on your requirements.

     

    If you want to use the built-in schema (JPA entities) provided by PicketLink IDM, you should use the data-source attribute. In this case the subsystem will automatically create a JPA Persistence Unit and use it. This is a good way to quick start using the JPA store given that the only thing you need to configure is a data source in your application server.

     

    But sometimes you may need more control over the JPA Persistence Unit configuration. In this case you can use the entity-manager-factory attribute. The example bellow shows a persistence unit configuration that exposes the EntityManagerFactory via JNDI:

     

    <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
        version="2.0">
    
        <persistence-unit name="emf-jndi-persistence" transaction-type="JTA">
            <jta-data-source>java:jboss/datasources/ExampleDS2</jta-data-source>
    
            <class>org.picketlink.idm.jpa.schema.IdentityObject</class>
            <class>org.picketlink.idm.jpa.schema.PartitionObject</class>
            <class>org.picketlink.idm.jpa.schema.RelationshipObject</class>
            <class>org.picketlink.idm.jpa.schema.RelationshipIdentityObject</class>
            <class>org.picketlink.idm.jpa.schema.RelationshipObjectAttribute</class>
            <class>org.picketlink.idm.jpa.schema.IdentityObjectAttribute</class>
            <class>org.picketlink.idm.jpa.schema.CredentialObject</class>
            <class>org.picketlink.idm.jpa.schema.CredentialObjectAttribute</class>
    
            <exclude-unlisted-classes>true</exclude-unlisted-classes>
    
            <properties>
                <property name="jboss.entity.manager.factory.jndi.name" value="java:jboss/TestingIDMEMF"/> <!-- Exposes the EMF via JNDI -->
                <property name="hibernate.hbm2ddl.auto" value="update" />
                <property name="hibernate.show_sql" value="false" />
                <property name="hibernate.format_sql" value="true" />
                <property name="hibernate.listeners.envers.autoRegister" value="false" />
                <property name="javax.persistence.validation.mode" value="none" />
            </properties>
        </persistence-unit>
    
    </persistence>
    
    

     

    Using a @Qualifier to inject IdentityManager instances

     

    When using CDI you can benefit from the use of qualifiers to get the IdentityManager instances injected.

     

    @Qualifier
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD, ElementType.METHOD})
    public @interface IdentityManagerConfig {
      
        String value();
    
    }
    

     

    Now you only need to create some producers in order to produce the IdentityManager instances with the proper qualifier:

     

    @ApplicationScoped
    public class MyIdentityManagerProducer {
    
        @Resource (mappedName="picketlink/DevIdentityManager")
        @Produces
        @IdentityManagerConfig ("dev")
        private IdentityManager devIdentityManager;
    
        @Resource (mappedName="picketlink/StagingIdentityManager")
        @Produces
        @IdentityManagerConfig ("staging")
        private IdentityManager stagingIdentityManager;
    
        @Resource (mappedName="picketlink/FileBasedCompleteIdentityManager")
        @Produces
        @IdentityManagerConfig ("production")
        private IdentityManager fileCompleteIdentityManager;
    
        @Resource (mappedName="picketlink/FileBasedSimpleIdentityManager")
        @Produces
        @IdentityManagerConfig ("test")
        private IdentityManager fileSimpleIdentityManager;
    
    }
    

     

    And inject those instances in any CDI bean for use:

     

        @Inject
        @IdentityManagerConfig ("dev")
        private IdentityManager devIdentityManager;
    
        @Inject
        @IdentityManagerConfig ("staging")
        private IdentityManager stagingIdentityManager;
    
        @Inject
        @IdentityManagerConfig ("production")
        private IdentityManager productionIdentityManager;
    
        @Inject
        @IdentityManagerConfig ("test")
        private IdentityManager testIdentityManager;
    
        @Test
        public void testDevIdentityManager() throws Exception {
            SimpleUser user = new SimpleUser("john");
    
            this.devIdentityManager.add(user);
    
            Password password = new Password("mypassWd");
    
            this.devIdentityManager.updateCredential(user, password);
    
            UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(user.getLoginName(), password);
    
            this.devIdentityManager.validateCredentials(credentials);
    
            Assert.assertEquals(Status.VALID, credentials.getStatus());
        }
    
        @Test
        public void testStagingIdentityManager() throws Exception {
            SimpleUser user = new SimpleUser("mary");
    
            this.stagingIdentityManager.add(user);
    
            Password password = new Password("mypassWd");
    
            this.stagingIdentityManager.updateCredential(user, password);
    
            UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(user.getLoginName(), password);
    
            this.stagingIdentityManager.validateCredentials(credentials);
    
            Assert.assertEquals(Status.VALID, credentials.getStatus());
        }
    
        @Test
        public void testTestIdentityManager() throws Exception {
            SimpleUser user = new SimpleUser("mary");
    
            this.testIdentityManager.add(user);
    
            Password password = new Password("mypassWd");
    
            this.testIdentityManager.updateCredential(user, password);
    
            UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(user.getLoginName(), password);
    
            this.testIdentityManager.validateCredentials(credentials);
    
            Assert.assertEquals(Status.VALID, credentials.getStatus());
        }
    
        @Test
        public void testProductionIdentityManager() throws Exception {
            SimpleUser user = new SimpleUser("mary");
    
            this.productionIdentityManager.add(user);
    
            Password password = new Password("mypassWd");
    
            this.productionIdentityManager.updateCredential(user, password);
    
            UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(user.getLoginName(), password);
    
            this.productionIdentityManager.validateCredentials(credentials);
    
            Assert.assertEquals(Status.VALID, credentials.getStatus());
        }
    
    

     

    Using a @Producer method to provide an IdentityConfiguration


    You can also use the Identity Management services by providing your own configuration instead of having to define it using the subsystem domain model. To do that, you must provide a @Producer method to produce a org.picketlink.idm.config.IdentityConfiguration with your own configuration.

     

    @ApplicationScoped
    public class MyIdentityConfigurationProducer {
    
        @Produces
        public IdentityConfiguration produceIdentityConfiguration() {
            IdentityConfiguration identityConfiguration = new IdentityConfiguration();
    
            identityConfiguration
                .fileStore()
                    .addRealm("default")
                    .supportAllFeatures();
    
            return identityConfiguration;
        }
    
    }
    

     

    The code above show a simple @ApplicationScoped bean that produces a IdentityConfiguration instance with a File-based identity store configured.

     

    Now you can inject the IdentityManager anywhere using the @Inject annotation:

     

        @Inject
        private IdentityManager identityManager;
    
        @Test
        public void testIdentityManager() throws Exception {
            SimpleUser user = new SimpleUser("jonhy");
    
            this.identityManager.add(user);
    
            Password password = new Password("mypassWd");
    
            this.identityManager.updateCredential(user, password);
    
            UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(user.getLoginName(), password);
    
            this.identityManager.validateCredentials(credentials);
    
            Assert.assertEquals(Status.VALID, credentials.getStatus());
        }
    

     

     

    PicketLink Core is basically a set of a CDI extension that provides authentication and authorization functionality for applications, using the IDM (optional) for identity storage and credential validation.

     

    Deployment Configuration

     

    To enable PicketLink Core for your deployment you only need to configure your jboss-deployment-structure.xml with the following dependency:

     

    <jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.1">
        <deployment>
            <dependencies>
                <module name="org.picketlink.core" /> <!-- PicketLink Core module dependency -->
            </dependencies>
        </deployment>
    </jboss-deployment-structure>
    

     

    The configuration above will tell the subsystem to enable the PicketLink Core CDI extensions to your deployment.

     

    But PicketLink Core has a deep integration with the PicketLink IDM services. So you may choose between two configuration options in order to provide the right Identity Manager for your deployment:

     

    • You can use the PicketLink IDM subsystem configuration to provide a Identity Manager instance that will be accessible via JNDI.
    • Or you can provide your own Identity Manager configuration using the PicketLink Core CDI API.

     

     

    The PicketLink IDM subsystem allows multiple configurations for Identity Management services. When deploying your application you need to tell which configured Identity Management service should be used.

     

    This is done by providing a resource in your web.xml that maps to one of the configured Identity Management services:

     

    <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    
              <resource-ref>
                    <description>Development IdentityManager</description>
                     <res-ref-name>java:picketlink/DevIdentityManager</res-ref-name>
                     <res-type>org.picketlink.idm.IdentityManager</res-type>
              </resource-ref>
    
    </web-app>
    

     

     

    Let's suppose you want to provide provide a Identity Manager configuration using a JPA Identity Store. For that you only need to produce a EntityManager instance with the @PicketLink qualifier.

     

    public class Resources {
    
        @Produces
        @PicketLink
        @PersistenceContext
        private EntityManager picketLinkEntityManager;
    
    }
    

     

    How to use in your application

     

    To use the authentication and identity management services you only need to inject the org.picketlink.Identity and org.picketlink.idm.IdentityManager.

     

       @Inject
        private Identity identity;
    
        @Inject
        private IdentityManager identityManager;
     
        public void testAuthentication() throws Exception {
            SimpleUser user = new SimpleUser("paul");
      
            this.identityManager.add(user);
    
            Password password = new Password("mypassWd");
    
            this.identityManager.updateCredential(user, password);
    
            UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(user.getLoginName(), password);
    
            this.identityManager.validateCredentials(credentials);
        }
    

     

    Examples

     

    [Provide some examples/quickstarts]

     

    Next steps

     

    • Move the code to the picketlink organization on github
    • Identity Management Services
      • Change the subsystem to use the IdentityManagerFactory instead of providing the IdentityManager directly
    • Review messages/exceptions and logging
    • Provide examples/quickstarts