SeamFramework.orgCommunity Documentation

Seam Security


1. Security - Introduction
1.1. Overview
1.1.1. Authentication
1.1.2. Identity Management
1.1.3. External Authentication
1.1.4. Authorization
1.2. Configuration
1.2.1. Maven Dependencies
1.2.2. Enabling the Security Interceptor
1.2.3. Third Party Dependencies
2. Security - Authentication
2.1. Basic Concepts
2.2. Built-in Authenticators
2.3. Which Authenticator will Seam use?
2.4. Writing a custom Authenticator
3. Security - Authorization
3.1. Basic Concepts
3.1.1. IdentityType
3.1.2. User
3.1.3. Group
3.1.4. Role
3.1.5. RoleType
3.2. Role and Group-based authorization
3.3. Typesafe authorization
3.3.1. Creating a typesafe security binding
3.3.2. Creating an authorizer method
3.3.3. Applying the binding to your business methods
3.3.4. Built-in security binding annotations
4. Security - Identity Management
4.1. Overview
4.2. Configuring Seam to use Identity Management
4.3. Using the Identity Management Features
4.3.1. Managing Users and Groups
4.3.2. Managing Relationships
4.3.3. Managing Roles
4.4. JpaIdentityStore
4.4.1. Recommended database schema
4.4.2. Configuring JpaIdentityStore
4.4.3. Configuring your Entities
5. Security - External Authentication
5.1. Introduction
5.1.1. Configuration
5.2. OpenID
5.2.1. Overview
5.2.2. Enabling OpenID for your application
5.2.3. Choosing which OpenID provider to use
5.2.4. Managing the OpenID authentication process

The Seam Security module provides a number of useful features for securing your Java EE application, which are briefly summarised in the following sections. The rest of the chapters contained in this documentation each focus on one major aspect of each of the following features.

The Maven artifacts for all Seam modules are hosted within the JBoss Maven repository. Please refer to the Maven Getting Started Guide for information about configuring your Maven installation to use the JBoss repository.

To use Seam Security within your Maven-based project, it is advised that you import the Seam BOM (Bill of Materials) which declares the versions for all Seam modules. First declare a property value for ${seam.version} as follows:


<properties>
  <seam.version>3.0.0.Final</seam.version>
</properties>

You can check the JBoss Maven Repository directly to determine the latest version of the Seam BOM to use.

Now add the following lines to the list of dependencies within the dependencyManagement section of your project's pom.xml file:


<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>seam-bom</artifactId>
  <version>${seam.version}</version>
  <type>pom</type>
  <scope>import</scope>
</dependency>

Once that is done, add the following dependency (no version is required as it comes from seam-bom):


<dependency>
  <groupId>org.jboss.seam.security</groupId>
  <artifactId>seam-security</artifactId>
</dependency>

It is also possible to import the security module as separate API and implementation modules, for situations where you may not want to use the default implementation (such as testing environments where you may wish to substitute mock objects instead of the actual implementation). To do this, the following dependencies may be declared instead:


<dependency>
  <groupId>org.jboss.seam.security</groupId>
  <artifactId>seam-security-api</artifactId>
</dependency>

<dependency>
  <groupId>org.jboss.seam.security</groupId>
  <artifactId>seam-security-impl</artifactId>
</dependency>

If you wish to use the external authentication module in your application to allow authentication using OpenID or SAML, then add the following dependency also:


<dependency>
  <groupId>org.jboss.seam.security</groupId>
  <artifactId>seam-security-external</artifactId>
</dependency>

The majority of the Security API is centered around the Identity bean. This bean represents the identity of the current user, the default implementation of which is a session-scoped, named bean. This means that once logged in, a user's identity is scoped to the lifecycle of their current session. The two most important methods that you need to know about at this stage in regard to authentication are login() and logout(), which as the names suggest are used to log the user in and out, respectively.

As the default implementation of the Identity bean is named, it may be referenced via an EL expression, or be used as the target of an EL action. Take the following JSF code snippet for example:

  <h:commandButton action="#{identity.login}" value="Log in"/>    

This JSF command button would typically be used in a login form (which would also contain inputs for the user's username and password) that allows the user to log into the application.

The other important bean to know about right now is the Credentials bean. Its' purpose is to hold the user's credentials (such as their username and password) before the user logs in. The default implementation of the Credentials bean is also a session-scoped, named bean (just like the Identity bean).

The Credentials bean has two properties, username and credential that are used to hold the current user's username and credential (e.g. a password) values. The default implementation of the Credentials bean provides an additional convenience property called password, which may be used in lieu of the credential property when a simple password is required.

The Identity bean has an authenticatorClass property, which if set will be used to determine which Authenticator bean implementation to invoke during the authentication process. This property may be set by configuring it with a predefined authenticator type, for example by using the Seam Config module. The following XML configuration example shows how you would configure the Identity bean to use the com.acme.MyCustomerAuthenticator bean for authentication:


<beans xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:s="urn:java:ee" 
   xmlns:security="urn:java:org.jboss.seam.security"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://jboss.org/schema/cdi/beans_1_0.xsd">
       
   <security:IdentityImpl>
      <s:modifies/>      
      <security:authenticatorClass>com.acme.MyCustomAuthenticator</security:authenticatorClass>
   </security:IdentityImpl>      
</beans>

Alternatively, if you wish to be able to select the Authenticator to authenticate with by specifying the name of the Authenticator implementation (i.e. for those annotated with the @Named annotation), the authenticatorName property may be set instead. This might be useful if you wish to offer your users the choice of how they would like to authenticate, whether it be through a local user database, an external OpenID provider, or some other method.

The following example shows how you might configure the authenticatorName property with the Seam Config module:


<beans xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:s="urn:java:ee" 
   xmlns:security="urn:java:org.jboss.seam.security"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://jboss.org/schema/cdi/beans_1_0.xsd">
   <security:IdentityImpl>
      <s:modifies/>
      <security:authenticatorName>openIdAuthenticator</security:authenticatorName>
   </security:IdentityImpl>      
</beans>

If neither the authenticatorClass or authenticatorName properties are set, then the authentication process with automatically use a custom Authenticator implementation, if the developer has provided one (and only one) within their application.

If neither property is set, and the user has not provided a custom Authenticator, then the authentication process will fall back to the Identity Management API to attempt to authenticate the user.

All Authenticator implementations must implement the org.jboss.seam.security.Authenticator interface. This interface defines the following methods:

public interface Authenticator {

  void authenticate();   
  void postAuthenticate();   
  User getUser();   
  AuthenticationStatus getStatus();
}

The authenticate() method is invoked during the authentication process and is responsible for performing the work necessary to validate whether the current user is who they claim to be.

The postAuthenticate() method is invoked after the authentication process has already completed, and may be used to perform any post-authentication business logic, such as setting session variables, logging, auditing, etc.

The getUser() method should return an instance of org.picketlink.idm.api.User, which is generally determined during the authentication process.

The getStatus() method must return the current status of authentication, represented by the AuthenticationStatus enum. Possible values are SUCCESS, FAILURE and DEFERRED. The DEFERRED value should be used for special circumstances, such as asynchronous authentication as a result of authenticating against a third party as is the case with OpenID, etc.

The easiest way to get started writing your own custom authenticator is to extend the org.jboss.seam.security.BaseAuthenticator abstract class. This class implements the getUser() and getStatus() methods for you, and provides setUser() and setStatus() methods for setting both the user and status values.

To access the user's credentials from within the authenticate() method, you can inject the Credentials bean like so:

@Inject Credentials credentials;

Once the credentials are injected, the authenticate() method is responsible for checking that the provided credentials are valid. Here is a complete example:

public class SimpleAuthenticator extends BaseAuthenticator implements Authenticator {

   @Inject Credentials credentials;
   
   @Override
   public void authenticate() {
      if ("demo".equals(credentials.getUsername()) && 
            credentials.getCredential() instanceof PasswordCredential &&
            "demo".equals(((PasswordCredential) credentials.getCredential()).getValue()))  {
         setStatus(AuthenticationStatus.SUCCESS);
         setUser(new SimpleUser("demo"));
      }
   }
}

In the above code, the authenticate() method checks that the user has provided a username of demo and a password of demo. If so, the authentication is deemed as successful and the status is set to AuthenticationStatus.SUCCESS, and a new SimpleUser instance is created to represent the authenticated user.

Seam Security provides a number of facilities for restricting access to certain parts of your application. As mentioned previously, the security API is centered around the Identity bean, which is a session-scoped bean used to represent the identity of the current user.

To be able to restrict the sensitive parts of your code, you may inject the Identity bean into your class:

@Inject Identity identity;

Once you have injected the Identity bean, you may invoke its methods to perform various types of authorization. The following sections will examine each of these in more detail.

The security model in Seam Security is based upon the PicketLink API. Let's briefly examine a few of the core interfaces provided by PicketLink that are used in Seam.

This is the simplest type of authorization, used to define coarse-grained privileges for users assigned to a certain role or belonging to a certain group. Users may belong to zero or more roles and groups, and inversely, roles and groups may contain zero or more members.

The Identity bean provides the following two methods for checking role membership:

boolean hasRole(String role, String group, String groupType);
void checkRole(String role, String group, String groupType);

These two methods are similar in function, and both accept the same parameter values. Their behaviour differs when an authorization check fails. The hasRole() returns a value of false when the current user is not a member of the specified role. The checkRole() method on the other hand, will throw an AuthorizationException. Which of the two methods you use will depend on your requirements.

The following code listing contains a usage example for the hasRole() method:

   if (identity.hasRole("manager", "Head Office", "OFFICE")) {
      report.addManagementSummary();
   }

Groups can be used to define a collection of users that meet some common criteria. For example, an application might use groups to define users in different geographical locations, their role in the company, their department or division or some other criteria which may be significant from a security point of view. As can be seen in the above class diagram, groups consist of a unique combination of group name and group type. Some examples of group types may be "OFFICE", "DEPARTMENT", "SECURITY_LEVEL", etc. An individual user may belong to many different groups.

The Identity bean provides the following methods for checking group membership:

boolean inGroup(String name, String groupType);
void checkGroup(String group, String groupType);

These methods are similar in behaviour to the role-specific methods above. The inGroup() method returns a value of false when the current user isn't in the specified group, and the checkGroup() method will throw an exception.

Seam Security provides a way to secure your bean classes and methods by annotating them with a typesafe security binding. Each security binding must have a matching authorizer method, which is responsible for performing the business logic required to determine whether a user has the necessary privileges to invoke a bean method. Creating and applying a security binding is quite simple, and is described in the following steps.

Identity Management is a feature that allows you to manage the users, groups and roles in your application. The Identity Management features in Seam Security are provided by PicketLink IDM. The best place to find more information about PicketLink IDM is the reference documentation, available here.

PicketLink provides two identity store implementations to allow you to use Hibernate or LDAP to store identity-related data (please refer to the PicketLink IDM documentation for details on configuring these). Seam Security provides an additional implementation called JpaIdentityStore, which allows you to store your identity data using JPA.

Note

In a Seam-based application it probably makes more sense to use the standards-based JpaIdentityStore rather than HibernateIdentityStore, as you will most likely be running in an Java EE container that supports JPA.

The Identity Management features are provided by a number of manager objects, which can be access from an IdentitySession. The IdentitySession may be injected directly into your beans like so:

import org.picketlink.idm.api.IdentitySession;


public @Model class IdentityAction {
  @Inject IdentitySession identitySession;
  
  // code goes here...
}

Once you have the IdentitySession object, you can use it to perform various identity management operations. You should refer to the PicketLink documentation for a complete description of the available features, however the following sections contain a brief overview.

JpaIdentityStore is an implementation of the PicketLink IdentityStore interface, provided by Seam Security. This identity store allows you to store your identity model inside a relational database, accessible via JPA. It provides an immense amount of flexibility in the way you define your identity model, and in most cases should be compatible with existing database schemas.

Seam provides a configuration bean called JpaIdentityStoreConfiguration, which can be used to configure which entity bean classes will be used by JpaIdentityStore to store identity-related data.

PropertyDescription
identityClassEntity class that contains identity objects such as users and groups
credentialClassEntity class that contains credentials, such as passwords
relationshipClassEntity class that contains relationships between identity objects
roleTypeClassEntity class that contains the names of all role types
attributeClassEntity class that contains additional identity object attributes

The following example shows how JpaIdentityStoreConfiguration may be configured using the Seam Config module:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:s="urn:java:ee" 
   xmlns:plidm="urn:java:org.jboss.seam.security.management.picketlink"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://jboss.org/schema/cdi/beans_1_0.xsd">
      
   <plidm:JpaIdentityStoreConfiguration>
      <s:replaces/>
      <plidm:identityClass>com.acme.model.IdentityObject</plidm:identityClass>
      <plidm:credentialClass>com.acme.model.IdentityObjectCredential</plidm:credentialClass>
      <plidm:relationshipClass>com.acme.model.IdentityObjectRelationship</plidm:relationshipClass>
      <plidm:roleTypeClass>com.acme.model.IdentityRoleName</plidm:roleTypeClass>
      <plidm:attributeClass>com.acme.model.IdentityObjectAttribute</plidm:attributeClass>
   </plidm:JpaIdentityStoreConfiguration>

Seam Security provides a single annotation, IdentityProperty, which can be used to configure your entity beans for use with JpaIdentityStore. This annotation declares two values, value and attributeName:

package org.jboss.seam.security.annotations.management;


public @interface IdentityProperty {
   PropertyType value();
   String attributeName() default "";
}

The value() member is of type PropertyType, which is an enum that defines the following values:

public enum PropertyType {

  NAME, TYPE, VALUE, RELATIONSHIP_FROM, RELATIONSHIP_TO, CREDENTIAL, 
  CREDENTIAL_TYPE, ATTRIBUTE }

By placing the IdentityProperty annotation on various fields of your entity beans, JpaIdentityStore can determine how identity-related data must be stored within your database tables.

In the following sections we'll look at how each of the main entities are configured.

Let's start by looking at identity object. This entity class is configured as the identityClass property of JpaIdentityStoreConfiguration. In the recommended database schema, the IDENTITY_OBJECT table is responsible for storing objects such as users and groups. This table may be represented by the following entity bean:

@Entity

  public class IdentityObject implements Serializable {  
     @Id @GeneratedValue private Long id;
     
     @IdentityProperty(PropertyType.NAME)
     private String name;
     
     @ManyToOne @IdentityProperty(PropertyType.TYPE)
     @JoinColumn(name = "IDENTITY_OBJECT_TYPE_ID")
     private IdentityObjectType type;
     // snip getter and setter methods
  }

In the above code both the name and type fields are annotated with @IdentityProperty. This tells JpaIdentityStore that these two fields are significant in terms of identity management-related state. By annotating the name field with @IdentityProperty(PropertyType.NAME), JpaIdentityStore knows that this field is used to store the name of the identity object. Likewise, the @IdentityProperty(PropertyType.TYPE) annotation on the type field indicates that the value of this field is used to represent the type of identity object.

The IdentityObjectType entity is simply a lookup table containing the names of the valid identity types. The field representing the actual name of the type itself should be annotated with @IdentityProperty(PropertyType.NAME):

@Entity

public class IdentityObjectType implements Serializable {   
   @Id @GeneratedValue private Long id;
   @IdentityProperty(PropertyType.NAME) private String name;        
   
   // snip getter and setter methods
}

The relationship table stores associations between identity objects, and is configured as the relationshipClass property of JpaIdentityStoreConfiguration. Here's an example of an entity bean that has been configured to store identity object relationships:

@Entity

public class IdentityObjectRelationship implements Serializable
{   
   @Id @GeneratedValue private Long id;
   
   @IdentityProperty(PropertyType.NAME)
   private String name;
   
   @ManyToOne @IdentityProperty(PropertyType.TYPE) @JoinColumn(name = "RELATIONSHIP_TYPE_ID")
   private IdentityObjectRelationshipType relationshipType;
   
   @ManyToOne @IdentityProperty(PropertyType.RELATIONSHIP_FROM) @JoinColumn(name = "FROM_IDENTITY_ID")
   private IdentityObject from;
   
   @ManyToOne @IdentityProperty(PropertyType.RELATIONSHIP_TO) @JoinColumn(name = "TO_IDENTITY_ID")
   private IdentityObject to;        
   
   // snip getter and setter methods
}

The name property is annotated with @IdentityProperty(PropertyType.NAME) to indicate that this field contains the name value for named relationships. An example of a named relationship is a role, which uses the name property to store the role type name.

The relationshipType property is annotated with @IdentityProperty(PropertyType.TYPE) to indicate that this field represents the type of relationship. This is typically a value in a lookup table.

The from property is annotated with @IdentityProperty(PropertyType.RELATIONSHIP_FROM) to indicate that this field represents the IdentityObject on the from side of the relationship.

The to property is annotated with @IdentityProperty(PropertyType.RELATIONSHIP_TO) to indicate that this field represents the IdentityObject on the to side of the relationship.

The IdentityObjectRelationshipType entity is a lookup table containing the valid relationship types. The @IdentityProperty(PropertyType.NAME) annotation is used to indicate the field containing the relationship type names:

@Entity

public class IdentityObjectRelationshipType implements Serializable {   
   @Id @GeneratedValue private Long id;
   
   @IdentityProperty(PropertyType.NAME)
   private String name;
   
   // snip getter and setter methods
}

OpenID allows the users of your application to authenticate without requiring them to create an account. When using OpenID, your user is temporarily redirected to the web site of their OpenID provider so that they can enter their password, after which they are redirected back to your application. The OpenID authentication process is safe - at no time is the user's password seen by any site besides their OpenID provider.

The external authentication module provides support for OpenID based on OpenID4Java, an open source OpenID library (licensed under the Apache v2 license) with both Relying Party and Identity Provider capabilities. This feature allows your application to authenticate its users against an external OpenID provider, such as Google or Yahoo, or to turn your application into an OpenID provider itself.

Note

To see the OpenID features in action, take a look at the openid-rp example included in the Seam Security distribution.

To use OpenID in your own application, you must configure Seam Security to use OpenIdAuthenticator, an Authenticator implementation that performs authentication against an OpenID provider. This authenticator is a named, session-scoped bean, with the following declaration:

public @Named("openIdAuthenticator") @SessionScoped class OpenIdAuthenticator

Seam provides built-in support for a number of well-known OpenID providers. The OpenIdAuthenticator bean may be configured to select which OpenID provider will be used to process an authentication request. Each concrete provider implements the following interface:

public interface OpenIdProvider {

   String getCode();
   String getName();
   String getUrl();
}

The following table lists the providers that come pre-packaged in Seam:

ProviderCodeNameURL
CustomOpenIdProvidercustomGoogle 
GoogleOpenIdProvidergoogleGooglehttps://www.google.com/accounts/o8/id
MyOpenIdProvidermyopenidMyOpenIDhttps://myopenid.com
YahooOpenIdProvideryahooYahoohttps://me.yahoo.com

To select one of the built-in providers to use for an authentication request, the providerCode property of the OpenIdAuthenticator bean should be set to one of the Code values from the above table. The OpenIdAuthenticator bean provides a convenience method called getProviders() that returns a list of all known providers. This may be used in conjunction with a radio group to allow the user to select which OpenID provider they wish to authenticate with - see the following JSF snippet for an example:


<h:selectOneRadio value="#{openIdAuthenticator.providerCode}">
  <f:selectItems value="#{openIdAuthenticator.providers}" var="p" itemValue="#{p.code}" itemLabel="#{p.name}"/>
</h:selectOneRadio>

Your application must provide an implementation of the OpenIdRelyingPartySpi interface to process OpenID callback events. This interface declares the following methods:

public interface OpenIdRelyingPartySpi {

   void loginSucceeded(OpenIdPrincipal principal, ResponseHolder responseHolder);
   void loginFailed(String message, ResponseHolder responseHolder);      
      

The implementation is responsible for processing the response of the OpenID authentication, and is typically used to redirect the user to an appropriate page depending on whether authentication was successful or not.

There are two API calls that must be made in the case of a successful authentication. The first one should notify the OpenIdAuthenticator that the authentication attempt was successful, and pass it the OpenIdPrincipal object:

openIdAuthenticator.success(principal);

Secondly, a DeferredAuthenticationEvent must be fired to signify that a deferred authentication attempt has been completed:

deferredAuthentication.fire(new DeferredAuthenticationEvent());

After making these two API calls, the implementation may perform whatever additional logic is required. The following code shows a complete example:

import java.io.IOException;


import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.servlet.ServletContext;
import org.jboss.seam.security.events.DeferredAuthenticationEvent;
import org.jboss.seam.security.external.api.ResponseHolder;
import org.jboss.seam.security.external.openid.OpenIdAuthenticator;
import org.jboss.seam.security.external.openid.api.OpenIdPrincipal;
import org.jboss.seam.security.external.spi.OpenIdRelyingPartySpi;
public class OpenIdRelyingPartySpiImpl implements OpenIdRelyingPartySpi {
   @Inject private ServletContext servletContext;
   @Inject OpenIdAuthenticator openIdAuthenticator;   
   @Inject Event<DeferredAuthenticationEvent> deferredAuthentication;
   public void loginSucceeded(OpenIdPrincipal principal, ResponseHolder responseHolder) {
      try {
         openIdAuthenticator.success(principal);
         deferredAuthentication.fire(new DeferredAuthenticationEvent());
         
         responseHolder.getResponse().sendRedirect(servletContext.getContextPath() + "/UserInfo.jsf");
      } catch (IOException e) {
         throw new RuntimeException(e);
      }
   }
   public void loginFailed(String message, ResponseHolder responseHolder) {
      try {
         responseHolder.getResponse().sendRedirect(servletContext.getContextPath() + "/AuthenticationFailed.jsf");
      } catch (IOException e) {
         throw new RuntimeException(e);
      }
   }
}