PicketLink part3

This article is about picketlink-authorization-idm-jpa.
Original code is here.
This example shows using JPA.

Previous articles
PicketLink
PicketLink part2

Execution environment
PicketLink 2.7.0.Final
WildFly 8.2.0.Final

Condition
CDI 1.0
JPA 2.0
JSF 2.0

Copyright

JBoss, Home of Professional Open Source
Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual
contributors by the @authors tag. See the copyright.txt in the 
distribution for a full listing of individual contributors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,  
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

picketlink-authorization-idm-jpa.war
A war file is as follows.
picketlink-jpa-war

/WEB-INF/beans.xml

<!-- Marker file indicating CDI should be enabled -->
<beans 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/beans_1_0.xsd">
</beans>

beans.xml is same previous article.

/WEB-INF/faces-config.xml

<?xml version="1.0"?>
<!-- Marker file indicating JSF should be enabled -->
<faces-config version="2.0" xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xi="http://www.w3.org/2001/XInclude"
 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-facesconfig_2_0.xsd">

</faces-config>

faces-config.xml is same previous article.

/WEB-INF/picketlink-quickstart-ds.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- This is an unmanaged datasource. It should be used for proofs of concept 
    or testing only. It uses H2, an in memory database that ships with JBoss 
    AS. -->
<datasources xmlns="http://www.jboss.org/ironjacamar/schema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.jboss.org/ironjacamar/schema http://docs.jboss.org/ironjacamar/schema/datasources_1_0.xsd">
    <!-- The datasource is bound into JNDI at this location. We reference 
        this in META-INF/persistence.xml -->
    <datasource jndi-name="java:jboss/datasources/PicketLinkQuickstartDS"
        pool-name="picketlink-quickstart" enabled="true"
        use-java-context="true">
        <connection-url>jdbc:h2:mem:picketlink-quickstart;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1</connection-url>
        <driver>h2</driver>
        <security>
            <user-name>sa</user-name>
            <password>sa</password>
        </security>
    </datasource>
</datasources>

This is data source configuration file for WildFly. This example uses H2 Database Engine that embedded WildFly. I recommend you use H2 Database Engine for test purpose only.
This configuration file is only for WildFly and JBoss.

/META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
   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">
   <persistence-unit name="picketlink-default">
      <!-- If you are running in a production environment, add a managed 
         data source, this example data source is just for development and testing! -->
      <!-- The datasource is deployed as WEB-INF/picketlink-quickstart-ds.xml, you
         can find it in the source at src/main/webapp/WEB-INF/picketlink-quickstart-ds.xml -->
      <jta-data-source>java:jboss/datasources/PicketLinkQuickstartDS</jta-data-source>

      <class>org.picketlink.idm.jpa.model.sample.simple.AttributedTypeEntity</class>
      <class>org.picketlink.idm.jpa.model.sample.simple.AccountTypeEntity</class>
      <class>org.picketlink.idm.jpa.model.sample.simple.RoleTypeEntity</class>
      <class>org.picketlink.idm.jpa.model.sample.simple.GroupTypeEntity</class>
      <class>org.picketlink.idm.jpa.model.sample.simple.IdentityTypeEntity</class>
      <class>org.picketlink.idm.jpa.model.sample.simple.RelationshipTypeEntity</class>
      <class>org.picketlink.idm.jpa.model.sample.simple.RelationshipIdentityTypeEntity</class>
      <class>org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity</class>
      <class>org.picketlink.idm.jpa.model.sample.simple.PasswordCredentialTypeEntity</class>
      <class>org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity</class>

      <properties>
         <!-- Properties for Hibernate -->
         <property name="hibernate.hbm2ddl.auto" value="create-drop" />
         <property name="hibernate.show_sql" value="false" />
      </properties>
   </persistence-unit>
</persistence>

This is a configuration file for JPA.
Line 7, it is configured name as picketlink-default.
Line 12, jta-data-source pairs with jndi-name of datasource at picketlink-quickstart-ds.xml(line 10).

Resources.java

package org.jboss.as.quickstarts.picketlink.authorization.idm.jpa;

import org.picketlink.annotations.PicketLink;

import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.faces.context.FacesContext;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

/**
 * This class uses CDI to alias Java EE resources, such as the {@link FacesContext}, to CDI beans
 * 
 * <p>
 * Example injection on a managed bean field:
 * </p>
 * 
 * <pre>
 * &#064;Inject
 * private FacesContext facesContext;
 * </pre>
 */
public class Resources {

    @PersistenceContext(unitName = "picketlink-default")
    private EntityManager em;

    /*
     * Since we are using JPAIdentityStore to store identity-related data, we must provide it with an EntityManager via a
     * producer method or field annotated with the @PicketLink qualifier.
     */
    @Produces
    @PicketLink
    public EntityManager getPicketLinkEntityManager() {
        return em;
    }

    @Produces
    @RequestScoped
    public FacesContext produceFacesContext() {
        return FacesContext.getCurrentInstance();
    }
}

Line 25, unitName pairs with name of persistence-unit at persistence.xml(line 7).
A getPicketLinkEntityManager method is used by PicketLink.
A produceFacesContext method returns a FacesContext.

IdentityManagementConfiguration.java

package org.jboss.as.quickstarts.picketlink.authorization.idm.jpa;

import org.picketlink.idm.config.IdentityConfiguration;
import org.picketlink.idm.config.IdentityConfigurationBuilder;

import javax.enterprise.inject.Produces;

/**
 * This bean produces the configuration for PicketLink IDM
 * 
 * 
 * @author Shane Bryzak
 *
 */
public class IdentityManagementConfiguration {

    /**
     * This method uses the IdentityConfigurationBuilder to create an IdentityConfiguration, which
     * defines how PicketLink stores identity-related data.  In this particular example, a
     * JPAIdentityStore is configured to allow the identity data to be stored in a relational database
     * using JPA.
     */
    @Produces IdentityConfiguration produceIdentityManagementConfiguration() {
        IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();

        builder
            .named("default")
            .stores()
            .jpa()
                // Specify that this identity store configuration supports all features
            .supportAllFeatures();

        return builder.build();
    }

}

This class defines how PicketLink stores identity-related data.
A named method is used in order to name for configuration. Here, the name is default.
A stores method is used in order to get a IdentityStoresConfigurationBuilder.
A IdentityStoresConfigurationBuilder is used in order to configure identity store.
A jpa method is used in order to get a JPAStoreConfigurationBuilder.
A JPAStoreConfigurationBuilder is used in order to configure identity store for JPA.
A supportAllFeatures method enables default feautures.

SecurityInitializer.java

package org.jboss.as.quickstarts.picketlink.authorization.idm.jpa;

import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.PartitionManager;
import org.picketlink.idm.RelationshipManager;
import org.picketlink.idm.credential.Password;
import org.picketlink.idm.model.basic.Group;
import org.picketlink.idm.model.basic.Role;
import org.picketlink.idm.model.basic.User;

import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Inject;

import static org.picketlink.idm.model.basic.BasicModel.addToGroup;
import static org.picketlink.idm.model.basic.BasicModel.grantGroupRole;
import static org.picketlink.idm.model.basic.BasicModel.grantRole;

/**
 * This startup bean creates a number of default users, groups and roles when the application is started.
 * 
 * @author Shane Bryzak
 */
@Singleton
@Startup
public class SecurityInitializer {

    @Inject
    private PartitionManager partitionManager;

    @PostConstruct
    public void create() {

        // Create user john
        User john = new User("john");
        john.setEmail("john@acme.com");
        john.setFirstName("John");
        john.setLastName("Smith");

        IdentityManager identityManager = this.partitionManager.createIdentityManager();

        identityManager.add(john);
        identityManager.updateCredential(john, new Password("demo"));

        // Create user mary
        User mary = new User("mary");
        mary.setEmail("mary@acme.com");
        mary.setFirstName("Mary");
        mary.setLastName("Jones");
        identityManager.add(mary);
        identityManager.updateCredential(mary, new Password("demo"));

        // Create user jane
        User jane = new User("jane");
        jane.setEmail("jane@acme.com");
        jane.setFirstName("Jane");
        jane.setLastName("Doe");
        identityManager.add(jane);
        identityManager.updateCredential(jane, new Password("demo"));

        // Create role "manager"
        Role manager = new Role("manager");
        identityManager.add(manager);

        // Create application role "superuser"
        Role superuser = new Role("superuser");
        identityManager.add(superuser);

        // Create group "sales"
        Group sales = new Group("sales");
        identityManager.add(sales);

        RelationshipManager relationshipManager = this.partitionManager.createRelationshipManager();

        // Make john a member of the "sales" group
        addToGroup(relationshipManager, john, sales);

        // Make mary a manager of the "sales" group
        grantGroupRole(relationshipManager, mary, manager, sales);

        // Grant the "superuser" application role to jane
        grantRole(relationshipManager, jane, superuser);
    }
}

This class is used in order to cinfigure user information.
Line 35-60, Users john, mary, jane are set.
And line 62-72, manager role, superuser role, sales group are created.
Line 77, makes john a member of the sales group.
Line 80, makes mary a manager role of the sales group.
Line 83, grants a superuser role to jane.

/index.html

<!-- Plain HTML page that kicks us into the app -->

<html>
<head>
<meta http-equiv="Refresh" content="0; URL=home.jsf">
</head>
</html>

It redirects to /home.jsf(called /home.xhtml).

/home.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">

  <p>
    This quickstart demonstrates how we can use PicketLink IDM's groups and roles to provide authorization checks within an application.
  </p>
  
  <p>
    The identity management configuration is based on PicketLink's JPAIdentityStore, which uses a database to store the application's
    users, groups and roles, and the relationships between them.
  </p>

<ui:fragment rendered="#{identity.loggedIn}">
    <div>Congratulations! You are currently logged in as: <b>#{identity.account.loginName}</b></div>

    <ui:fragment rendered="#{authorizationChecker.hasApplicationRole('superuser')}">
      <div>You have been granted the 'superuser' application role.</div>
    </ui:fragment>
    
    <ui:fragment rendered="#{authorizationChecker.isMember('sales')}">
      <div>You are a member of the 'sales' group.</div>
    </ui:fragment>
    
    <ui:fragment rendered="#{authorizationChecker.hasGroupRole('manager', 'sales')}">
      <div>You have been granted the 'manager' role in the 'sales' group.</div>
    </ui:fragment>

    <h:form>
        <h:commandButton id="logout" value="Log out" action="#{identity.logout}"/>
    </h:form>
</ui:fragment>

<h:form id="loginForm" rendered="#{not identity.loggedIn}">
    <h:messages globalOnly="true"/>

    <div class="loginRow">
        <h:outputLabel for="name" value="Username" styleClass="loginLabel"/>
        <h:inputText id="name" value="#{loginCredentials.userId}"/>
    </div>

    <div class="loginRow">
        <h:outputLabel for="password" value="Password" styleClass="loginLabel"/>
        <h:inputSecret id="password" value="#{loginCredentials.password}" redisplay="true"/>
    </div>

    <div class="loginRow">

    </div>

    <div class="buttons">
        <h:commandButton id="login" value="Login" action="#{loginController.login}" styleClass="loginButton"/>
    </div>

    <p>
      Tip: you can login with any of the following username/password combinations:
      <div>john/demo</div>
      <div>mary/demo</div>
      <div>jane/demo</div>
    </p>
    
    <p>
      Each of these accounts has different privileges assigned to them.
    </p>

</h:form>

<br style="clear:both"/>

</html>

Line 35, #{not identity.loggedIn} is status of login. A identity is defined by PicketLink.
When a user did not login it displays line 35-67.
picketlink-jpa-home
Line 40 and 45, loginCredentials is DefaultLoginCredentials class defined by PicketLink(DefaultLoginCredentials class is added @Named(“loginCredentials”)).
When a user inputed username and password these are set into loginCredentials.userId and loginCredentials.password.
Line 53, when a user clicked [Login] login method of LoginController.java will be executed. Here combinations of username and password are john/demo, mary/demo, jane/demo that configured SecurityInitializer.java.
When a user failed login message at line 36 will be displayed.
picketlink-jpa-home-failed
When a user logged in successfully a fragment at line 15-33 will be displayed because #{identity.loggedIn} at line 15 becomes true.
picketlink-jpa-home-success
Line 16, #{identity.account.loginName} is login name.
Line 18, #{authorizationChecker.hasApplicationRole(‘superuser’)} calls hasApplicationRole method of AuthorizationChecker.java. If a login user granted a superuser role this fragment will be displayed.
Line 22, #{authorizationChecker.isMember(‘sales’)} calls isMember method of AuthorizationChecker.java. If a login user is menber of sales group this fragment will be displayed.
Line 26, #{authorizationChecker.hasGroupRole(‘manager’, ‘sales’)} calls hasGroupRole method of AuthorizationChecker.java. If a login user granted manager role of sales group this fragment will be displayed.
When a user clicked [Log out] button logout will be executed.

LoginController.java

package org.jboss.as.quickstarts.picketlink.authorization.idm.jpa;

import javax.ejb.Stateless;
import javax.enterprise.context.RequestScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;

import org.picketlink.Identity;
import org.picketlink.Identity.AuthenticationResult;

/**
 * We control the authentication process from this action bean, so that in the event of a failed authentication we can add an
 * appropriate FacesMessage to the response.
 * 
 * @author Shane Bryzak
 * 
 */
@Stateless
@Named
public class LoginController {

    @Inject
    private Identity identity;

    @Inject
    private FacesContext facesContext;

    public void login() {
        AuthenticationResult result = identity.login();
        if (AuthenticationResult.FAILED.equals(result)) {
            facesContext.addMessage(
                    null,
                    new FacesMessage("Authentication was unsuccessful.  Please check your username and password "
                            + "before trying again."));
        }
    }
}

Line 28, FacesContext is injected from produceFacesContext method of Resources.java.
When a user clicked [Login] button at home.xhtml login method will be executed.
Line 31, login is executed. A username and a password are already set to loginCredentials.userId and loginCredentials.password.
When a user failed login FacesMessage will be created.

AuthorizationChecker.java

package org.jboss.as.quickstarts.picketlink.authorization.idm.jpa;

import org.picketlink.Identity;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.RelationshipManager;
import org.picketlink.idm.model.basic.BasicModel;
import org.picketlink.idm.model.basic.Group;
import org.picketlink.idm.model.basic.Role;

import javax.inject.Inject;
import javax.inject.Named;

import static org.picketlink.idm.model.basic.BasicModel.*;

/**
 * This is a utility bean that may be used by the view layer to determine whether the
 * current user has specific privileges. 
 * 
 * @author Shane Bryzak
 *
 */
@Named 
public class AuthorizationChecker {
    
    @Inject
    private Identity identity;
    
    @Inject 
    private IdentityManager identityManager;

    @Inject
    private RelationshipManager relationshipManager;

    public boolean hasApplicationRole(String roleName) {
        Role role = getRole(this.identityManager, roleName);
        return hasRole(this.relationshipManager, this.identity.getAccount(), role);
    }

    public boolean isMember(String groupName) {
        Group group = getGroup(this.identityManager, groupName);
        return BasicModel.isMember(this.relationshipManager, this.identity.getAccount(), group);
    }

    public boolean hasGroupRole(String roleName, String groupName) {
        Group group = getGroup(this.identityManager, groupName);
        Role role = getRole(this.identityManager, roleName);
        return BasicModel.hasGroupRole(this.relationshipManager, this.identity.getAccount(), role, group);
    }
}

Line 34, hasApplicationRole method is called from #{authorizationChecker.hasApplicationRole(‘superuser’)} at home.xhtml. This method examines whether a logged in user has role. A getRole method and a hasRole method are defined by Picketlink. Here if a logged in user has superuser role, this method will return true.
Line 39, isMember method is called #{authorizationChecker.isMember(‘sales’)} at home.xhtml. This method examines whether a logged in user is a member of group. A getGroup method and a BasicModel.isMember method are defined Picketlink. Here if a logged in user is a member of sales group, this method will return true.
Line 44, hasGroupRole method is called from #{authorizationChecker.hasGroupRole(‘manager’, ‘sales’)} at home.xhtml. This method examines whether a logged in user has role in group. BasicModel.hasGroupRole method is defined by Picketlink. Here if a logged in user has manager role in sales group, this method will return true.