Chapter 11. Securing Seam applications

This chapter covers

  • Developing an authentication routine
  • Enforcing role-based authorization
  • Writing permission rules with Drools
  • Adding a CAPTCHA challenge to a form

While winding down after a round of golf, I came across a magazine ad for Microsoft Visual Studio 2005 that serves as an example of how not to treat security. The ad shows side-by-side shots of a software development scene in which two developers are discussing a web application, one before the product is introduced and one after. The developer paraphernalia and the to-do list on the whiteboard reflect the state of the project, with the before scene being far more cluttered and laden with stress. But the contrast reveals a critical oversight in the after scene. An outstanding item on the to-do list reads “TEST CODE FOR SECURITY!!” The items crossed off are personalization features, consistency review of UI, accessibility, and breadcrumbs. At least the application will look pretty while it’s being hacked.

Just because this chapter is in the final part of this book doesn’t mean you should wait until the last minute to implement or test for security. A common misconception is that security can be tacked onto the application when it’s ready to be sent off to QA, or worse, production, as if it’s just polish. A complex application can’t be made secure after the fact. Security must be present from the beginning and weaved into every layer of the application, from the view down to the database. That’s why security is such an integral part of Seam.

Seam helps you secure your application without spending a lot of time on the details. In this chapter, you’ll learn how to implement an authentication routine and how to protect areas of the application from unauthorized access across all layers. The foundation of Seam’s security model is a role-based system that ensures a quick start. Obviously, some applications require a more granular approach. To meet these needs, Seam builds on this foundation by leveraging the Drools rule engine to support contextual, rule-based permissions. The possibilities opened up by a rules system are limitless. Seam 2.1 introduces an identity and permissions management module to make administering security even easier. The best part of Seam’s security model is that you don’t have to stomach a single line of XML, a breath of fresh air for those who have used other Java security frameworks.

Seeing is believing, so I want to start by showing you the quickest way to secure your application after first defining some basic security terminology and how a user’s identity is represented. As the chapter progresses, the security becomes more sophisticated.

11.1. Authentication jump-start

Why do you think security often gets the back seat in the design and development process? My reasoning is that security frameworks, such as the Java Authentication and Authorization Service (JAAS) and Spring Security (formally Acegi), are just too hard to implement. The first is too cryptic and, let’s face it, primitive, and the second buries you deep in XML Hell.

The goal of a security layer is to prevent hackers, nonprivileged users, and rogue client endpoints from accessing sensitive areas of the application, and not scare off developers from implementing it. Given its importance, security should be easy to configure and use, and it should be integrated into the core of the application framework rather than split off as an extension. Both are true in Seam. What’s more important is that this ease of use is accomplished without compromising the ability of Seam’s security model to scale in accordance with security requirements. I start by presenting authentication, the foundation of security, and show you how to tie it into a Seam application in three simple steps.

11.1.1. Giving the user an identity

Authentication is just a fancy way of saying “login.” The login routine, which you are about to implement, prefaces nearly every action we perform in today’s online world. But it’s not just about making you ransack the piles of papers on your desk to find the scribbled characters that get you past the login challenge. Authentication is about giving an anonymous user a face, as depicted in figure 11.1.

Figure 11.1. By authenticating (signing in), the user is revealing his or her identity to the application.

Show me your face

The transformation in figure 11.1 symbolizes the user establishing an identity. During authentication, Seam assembles an instance of Subject from the JAAS API and associates it with the user’s session, allowing the server to recognize the user until the session expires or the user explicitly logs out.

The Subject instance is a digital representation of the user. It consists of a collection of principals, filling in the user’s facial features. A principal implements the Principal interface from the JAAS API. There can be an unbounded set of principals, but Seam limits it to just two. The first principal holds the username of the authenticated user and is called the user principal. The second principal, known as the roles group, implements the Group interface and holds a collection of roles. Each role is a Principal as well, though distinct from the user principal and roles group and appropriately referred to as a role principal.

But the details about the structure of the Subject don’t really matter because Seam provides a simple abstraction layer that you use to establish a user’s principals during authentication and to access those principals when performing authorization checks.

JAAS a la carte

Seam uses JAAS, but only select parts of it. Don’t panic when you see that four-letter word because, by and large, Seam’s JAAS integration is completely transparent to you. Behind the scenes, Seam relies on JAAS to handle the authentication handoff, which delegates to one of your components to give the approval, and also uses the identity portion of the API as mentioned above. The only time you even come in contact with JAAS is to assert a user’s role using Servlet security (e.g.,isUserInRole()). Seam ignores the permission and policy piece of JAAS, instead offering its own multifaceted authorization strategy. We cover authorization after we finish with authentication. Let’s take it one A at a time.

Locating the User Accounts

To implement authentication, you need to decide where the user accounts for your application are stored. Seam leaves this task entirely up to you. If you do want Seam to help out, you have the option in Seam 2.1 of letting the identity manager consult your database or LDAP to find an account, though it requires that you follow a standard, yet flexible, model.

In Open 18, the accounts are mapped to a table in the database through the Member entity and retrieved using the EntityManager. The Member entity, introduced in chapter 4, has fields to store the member’s username and hashed password, but doesn’t have a field to hold the member’s roles. Though not mandatory, you may want to assign roles to the user during authentication, which are pulled from the database. First, you need a Role entity:

  @Entity
  @Table(name = "ROLE", uniqueConstraints =
    @UniqueConstraint(columnNames = "name"))
  public class Role implements Serializable {
      private Long id;
      private String name;

      @Id @GeneratedValue
      public Long getId() { return id; }
      public void setId(Long id) { this.id = id; }

      public String getName() { return name; }
      public void setName(String name) { this.name = name; }
  }

Next, you add a Role collection to the Member entity, related through a join table:

  private Set<Role> roles = new HashSet<Role>();

  @ManyToMany(fetch = FetchType.LAZY)
  @JoinTable(name = "MEMBER_ROLE",
    joinColumns = @JoinColumn(name = "member_id"),
    inverseJoinColumns = @JoinColumn(name = "role_id"))
  public Set<Role> getRoles() { return this.roles; }
  public void setRoles(Set<Role> roles) { this.roles = roles; }

While roles are optional for authentication, they are essential when it comes to implementing authorization. In a sense, authentication is the binary part of security: The user is either authenticated or the user is not authenticated. Only after establishing the user’s identity can we begin talking about authorization. Let’s find out, in three steps, how a 0 becomes a 1.

11.1.2. Implementing authentication in three steps

The three steps for setting up authentication in Seam are as follow:

  1. Switch on authentication by configuring an authentication method.
  2. Verify the user’s credentials in the authentication method.
  3. Create a JSF login form.

When these steps are complete, your application will support form-based authentication. Later on, you’ll learn that in Seam 2.1, you can let Seam’s identity manager handle the second step for you. If you would rather not bother with the login form, you can plug in Seam’s support for HTTP authentication, in which case the credentials are negotiated by the browser. Let’s put these alternatives aside for now and continue with the steps laid out here.

Step 0: Zero prerequisites

The first step isn’t a step at all but simply a fact. You don’t need any extra libraries to implement authentication and role-based security in Seam. Only when you branch out to Seam’s rule-based security, covered in section 11.4, are additional dependencies needed.

In fact, projects created using the seam-gen tool already have the authentication routine configured. It just leaves out one critical detail. The default configuration accepts any username and password. To heighten security (to put it lightly) and keep out the imposters, the user’s login credentials need to be validated against the database of registered members.

Step 1: Switching on Authentication

Enabling security in Seam is a bit of a misnomer. Security is enabled by default, unless you purposely disable it (perhaps in a test). However, out of the box, there’s no way for users to authenticate themselves. To make that possible, you first need to tell Seam which method handles the authentication logic (i.e., the authentication delegate). This method is provided by one of your components. Three requirements must be met by the authentication method:

  • It must take no arguments.
  • It must return a boolean indicating whether the credentials could be verified.
  • It must be accessible via the EL (which isn’t much of a problem in Seam).

The authentication method can have any name, it can reside on any class, and that class doesn’t have to implement any special security interfaces. Seam plugs your authentication method into JAAS, but hides the complexity of JAAS in the SeamLoginModule. Internally, JAAS invokes your authentication method and adds the appropriate principals to the security subject if the method returns true. This authentication routine is activated through Seam’s identity component, freeing you from having to interact with the colossus that lies beneath.

The built-in Identity component, named identity, maintains a reference to the authentication method as a method-binding expression. The identity component lives in the component namespace http://jboss.com/products/seam/security, declared in the component descriptor using the prefix identity. The authentication method can be assigned to the identity component using component configuration:

  <security:identity
    authenticate-method="#{authenticationManager.authenticate}"/>

Here, the authentication method is provided by the authenticationManager component. The next step is to implement this method.

Step 2: Authoring an Authentication Method

As I mentioned earlier, the authentication method can reside on any class. We use a JavaBean component in this example. Here’s a naïve, but valid, implementation of the method:

  @Name("authenticationManager")
  public class AuthenticationManager {
      public boolean authenticate() {
          return true;
      }
  }

To get serious, we need credentials to validate. If the authenticate() method doesn’t take any arguments, where do the credentials come from? One of the roles of the identity component, which is scoped to the session context and instantiated when the user’s session begins, is to capture the credentials being challenged. The credentials are stored in the username and password properties on this component, and are typically populated by a JSF form. Thus, you get to the credentials by obtaining a reference to the identity component.

 

Note

If your authentication routine requires additional credentials, you can extend Seam’s security infrastructure to capture them. In Seam 2.0, you extend the Identity class and register it using the component name org.jboss.seam.security.identity. In Seam 2.1, you extend the Credentials class and register it using the component name org.jboss.seam.security.credentials. The credentials component was introduced in Seam 2.1 to hold the credentials. Although the credentials can still be accessed using the identity component, the credentials component is the preferred means of access.

 

To pull the credentials into the authentication method, you simply inject identity (or credentials in Seam 2.1) into the authentication component using @In:

  @Name("authenticationManager")
  public class AuthenticationManager {
      @Logger private Log log;
      @In private Identity identity;

      public boolean authenticate() {
          log.info("username: #0, password: #1",
              identity.getUsername(), identity.getPassword());
          identity.addRole("member");
          return true;
      }
  }

As you see here, the role of the identity component in the authentication method is twofold. It delivers the login credentials and it’s used to store a set of roles. In this implementation, we assign all users the member role. JAAS transfers the roles to the security subject during the post login routine. Let’s consider how that works.

When the authentication method is called, the user principal and roles group haven’t yet been established on the Subject instance (because the user hasn’t been authenticated). That initialization takes place inside the JAAS login module after the authentication method returns true. In the interim, the identity component provides temporary storage for roles—appended using the addRoles() method—that need to be transferred to the user’s group identity. During the post-authentication routine, Seam converts the role names into role principals and adds them to the roles group on the Subject instance.

 

Note

Seam doesn’t impose a naming convention for roles, so feel free to use your own scheme.

 

The authentication routine produces a standard Java security principal, meaning Servlet security just works. You can use the HttpServletRequest#isUserInRole() method to check if a user has been granted a role, and it enables transparent integration with libraries that depend on this method. To get all of this, you only have to write a couple lines of code (even counting the XML). That number grows when you add the authentication logic.

The authentication logic for Open 18 is presented in listing 11.1. The username on the identity component is used to look up a matching Member entity in the database using the EntityManager. If an instance is found, the password is validated by comparing its hash to the hashed password from the database. If both checks succeed, the roles are added and the method returns true, returning control to JAAS to establish the security principals. If either check fails, a return value of false sends the user back to the login page with a failure message. We examine the details of the failure scenario a bit later.

Listing 11.1. An authentication component that plugs into Seam’s JAAS login module
    package org.open18.action;
    import org.jboss.seam.security.Identity;
    import ...;

    @Name("authenticationManager")
    public class AuthenticationManager {
        @In private EntityManager entityManager;
        @In private Identity identity;
        @In private PasswordManager passwordManager;
        @Out(required = false) Golfer currentGolfer;

        @Transactional public boolean authenticate() {
            try {
                Member member = (Member) entityManager.createQuery(
                    "select m from Member m where m.username = :username")
                    .setParameter("username", identity.getUsername())
                    .getSingleResult();

                if (!validatePassword(identity.getPassword(), member)) {
                    return false;
                }

                identity.addRole("member");
                if (member.getRoles() != null) {
                    for (Role role : member.getRoles()) {
                        identity.addRole(role.getName());
                    }
                }

                if (member instanceof Golfer) {
                    currentGolfer = (Golfer) member;
                    identity.addRole("golfer");
                }
                return true;
            } catch (NoResultException e) {
                return false;
            }
        }
        public boolean validatePassword(String password, Member m) {
            return passwordManager.hash(password).equals(m.getPasswordHash());
        }
    }

If the member is a golfer, currentGolfer is outjected for convenience. To ensure it hangs around for the duration of the session, we define a role for it on the Golfer class:

  @Role(name = "currentGolfer", scope = ScopeType.SESSION)

All that’s left is to create a form for the user to enter credentials and attempt a login.

Step 3: Creating the Login Form

You’ll be thrilled to discover that the j_username and j_password request parameters and the /j_security_check servlet path, defined in the Servlet specification for implementing form-based logins, have finally been retired under Seam. And for those of you who have had to invent a custom handoff to get a JAAS login module to play nicely with JSF, you’ll be happy to know that you can use a native JSF form on the login page. The login form boils down to two value-binding expressions, #{identity.username} and #{identity.password}, which capture the user’s login credentials (in Seam 2.1, you use the credentials component for this purpose) and one method-binding expression, #{identity.login}, which invokes the built-in action method that kicks off the authentication routine. Here’s an example of a basic login form:

  <h:form id="login">
    <h:panelGrid columns="2">
      <h:outputLabel for="username">Username</h:outputLabel>
      <h:inputText id="username" value="#{identity.username}"/>
      <h:outputLabel for="password">Password</h:outputLabel>
      <h:inputSecret id="password" value="#{identity.password}"/>
    </h:panelGrid>
    <div class="actionButtons">
      <h:commandButton value="Login" action="#{identity.login}"/>
    </div>
  </h:form>

Each time an attempt is made to authenticate, the password is cleared before the login() method returns. If the login fails, this method returns null, causing the login page to be redisplayed. If the login succeeds, this method returns the value loggedIn, which you can plug into a navigation rule to redirect the user somewhere besides the login page:

  <navigation from-action="#{identity.login}">
    <rule if-outcome="loggedIn">
      <redirect view-id="/home.xhtml"/>
    </rule>
  </navigation>

This rule applies to when a user requests the login page directly. In section 11.2.2, you’ll learn how to configure Seam to restore the user’s original request if it was interrupted by a request to login.

That’s all there is to it! You can finally wipe your hands clean of low-level JAAS details, which Seam keeps hidden so the work you have to do is minimal. In fact, you don’t even need the JSF form to perform authentication. Let’s say that you want to authenticate the user automatically, perhaps following registration or in response to a remote method invocation. You just register the credentials on identity and invoke the login() method:

   @In private Identity identity;

   public String register() {
       ...
       identity.setUsername(newGolfer.getUsername());
       identity.setPassword(passwordBean.getPassword());
       identity.login();
       return "success";
   }

Don’t fear that as your requirements become more complex, you’ll outgrow Seam’s security model. Throughout this chapter, you’ll learn that Seam gives you all the power you need without compromising simplicity or extensibility.

Bonus round: Logging out

If authenticating the user is so easy, you probably expect the logout to be the same. You guessed right. Just as the #{identity.login} method-binding expression is attached to a UI command component to log in, the #{identity.logout} method-binding expression is used to log out. To create a login/logout control, you can use the value expression #{identity.loggedIn} to check whether the user is authenticated and, if so, you can personalize the page by displaying the username credential, which is retained by the session-scoped identity component:

  <h:outputText value="You are signed in as: #{identity.username}"
    rendered="#{identity.loggedIn}"/>
  <s:link view="/login.xhtml" value="Login"
    rendered="#{not identity.loggedIn}"/>
  <s:link view="/home.xhtml" action="#{identity.logout}" value="Logout"
    rendered="#{identity.loggedIn}"/>

Before calling the authentication process a done deal, we need to address what happens when authentication fails. As I mentioned earlier, the user is returned to the login page and presented with a failure message. As it turns out, a message is created either way. There are also a plethora of events surrounding authentication. Let’s explore this flurry of activity.

Authentication messages and events

While Seam assumes control of authentication, it doesn’t keep other parts of the application in the dark. This transparency is accomplished through the use of events. Table 11.1 lists the events that are most relevant. The first event listed signals when the user is being directed to the login page to authenticate, a process that we discuss in section 11.2.2.

Table 11.1. A list of events related to authentication

Event name

When it is raised

org.jboss.seam.security.notLoggedIn When a nonauthenticated user encounters a restriction
org.jboss.seam.security.preAuthenticate Prior to delegation to the JAAS login module
org.jboss.seam.security.postAuthenticate At the end of the authentication process, when the security subject is fully initialized
org.jboss.seam.security.loginFailed Before the login() method on the identity component returns and the authentication failed
org.jboss.seam.security.loginSuccessful Before the login() method on the identity component returns and the authentication was successful
org.jboss.seam.security.loggedOut Before the logout() method on the identity component returns, after the session has been invalidated
org.jboss.seam.security.initCredentials The first time the getUsername() method is called on the credentials component for a given session (Seam 2.1 only)
org.jboss.seam.security.quietLogin Before a restriction is checked to give an observer the opportunity to log in the user automatically (Seam 2.1 only)

In addition to raising events, Seam adds a global FacesMessage to the response whenever the user is directed to the login page or the user makes an authentication attempt.

Following multilingual best practices, Seam resolves the authentication message from a message key in the Seam resource bundle. Table 11.2 lists the message keys and severities for each event. The Seam resource bundle was covered in section 5.5.2 of Chapter 5. You render the authentication messages using the following component tag:

  <h:messages globalOnly="true"/>
Table 11.2. The message keys that Seam uses following an authentication event

Message key

FacesMessage severity

When it is used

org.jboss.seam.loginFailed SEVERITY_INFO Authentication fails
org.jboss.seam.loginSuccessful SEVERITY_INFO Authentication succeeds
org.jboss.seam.NotLoggedIn[a] SEVERITY_WARN Authentication is requested

a The “N” in the org.jboss.seam.NotLoggedIn key is intentionally uppercase.

In order for the user to see the login success message, this tag must be included on any page the user is taken to after login. In section 11.2.2, you’ll learn how to redirect the user back to the intercepted request, widening this pool of target pages. If you don’t want one of the messages to be used, just assign an empty value to the message key. With the user being scolded or praised accordingly, the authentication routine is complete. Looking back on the work you’ve done, the most complex part was implementing the authentication method, which involved locating an account, validating the password, and adding the roles. Granted, the authentication logic in your own application may be different. But if you are simply retrieving the accounts from the database using JPA or from LDAP, you can let Seam’s identity manager (introduced in Seam 2.1) handle this work for you. While both JPA and LDAP providers exist, only the JPA one is covered here. At the time of this writing, this feature was still under active development, so I won’t go into too much detail. However, the next section should be enough to get you started.

11.1.3. A glimpse at Seam’s identity management

Seam’s new identity management module, introduced in 2.1, stretches Seam’s declarative services to cover authentication. The module consists of a handful of annotations and a bunch of glue code that work collectively to authenticate the user and set up the user’s roles. All you have to do is put the annotations where they belong and do some component configuration. Once that is done, your authentication method becomes history.

The first step is creating entities to represent a user and a role. In Open 18, these entities are Member and Role, respectively. The next step is to identify the fields that store the user’s account information using the following annotations:

  • @UserPrincipal—Identifies the field on the user class that stores the username.
  • @UserPassword—Identifies the field on the user class that stores the password. Both plain-text and hashed passwords are supported. The hashing algorithm is specified in the hash attribute. The hashed password must be Base64 encoded.
  • @UserRoles—Identifies the collection field on the user class that stores the roles. The collection must map to a class that has a field annotated with @RoleName.
  • @RoleName—Identifies the field on the role class that stores the name of the role.

There are a couple additional annotations not listed here for tracking additional information about the user. Abbreviated versions of Member and Role are shown here with the identity management annotations applied:

  @Entity
  public class Member implements Serializable {
      @UserPrincipal
      public String getUsername() { return this.username; }
      @UserPassword(hash = "SHA")
      public String getPasswordHash() { return this.passwordHash; }
      @UserRoles
      public Set<Role> getRoles() { return this.roles; }
      ...
  }
  @Entity
  public class Role implements Serializable {
    @RoleName
    public void String getName() { return this.name; }
    ...
  }

Next, you need to configure an identity store in the component descriptor and indicate which classes represent the user and role. The authentication method on identity is no longer needed:

  <security:identity/>
  <security:jpa-identity-store
    user-class="org.open18.model.Member" role-class="org.open18.model.Role"/>

The identity manager uses the JPA identity store by default if it’s available. The JPA identity store assumes that the name of the Seam-managed persistence context is entityManager. If it’s not, you must assign it to the entityManager property using EL.

Using the identity component to authenticate the user is no different in this case. Behind the scenes, the SeamLoginModule delegates to the identity manager to validate the credentials. If you need to execute custom logic during authentication, you can use a method that observes the postauthentication event, listed in table 11.1.

Although you never interact with the identity manager directly during authentication, you can use its API to manage accounts. It supports full CRUD capabilities for users and roles and has additional methods for performing tasks such as changing a user’s password, enabling an account, and granting and revoking roles. To complement the identity manager, Seam 2.1 provides a permission manager for maintaining persistent user permissions, which is mentioned in section 11.4. Check out the seamspace example from the Seam distribution to see both in action.

Now that you know how to implement form-based authentication, let’s explore an alternative that leverages the browser’s ability to negotiate credentials with the server.

11.1.4. Even more “Basic” authentication

You mean that what you’ve seen so far isn’t basic enough for you? Okay, I understand. Sometimes you just want to block off an application from the public eye without making any changes to the user interface. To accomplish that, HTTP Basic or HTTP Digest (RFC 2617) authentication may be sufficient for your needs. Guess what? Seam has that covered. However, it doesn’t get you totally off the hook. You still need to implement and configure the authentication method as before. You just no longer need a login page (and you don’t have to worry about navigation, a concern addressed later on).

As you learned in Chapter 3, the Seam filter uses a delegation model to wrap each request in a chain of Seam-configured filters, each declared using the @Filter annotation. One of Seam’s built-in filters, AuthenticationFilter, handles HTTP authentication. However, this filter isn’t installed by default (i.e., @Install(false)). To use it, you must make sure it’s activated in the component descriptor. Seam’s built-in filters reside in the component namespace http://jboss.com/products/seam/web, prefixed as web. When activating the AuthenticationFilter, specify whether you want to use Basic or Digest (digest) authentication, controlled by the auth-type attribute. Let’s start with Basic:

  <web:authentication-filter url-pattern="*.seam" auth-type="basic"/>

Now, any URL that matches the value of the url-pattern attribute is protected by Basic authentication. To protect only JSF requests, the url-pattern attribute should match the pattern configured for the JSF servlet in web.xml. If the url-pattern attribute is excluded, the filter is applied to all requests captured by Seam’s main filter. During authentication, Seam invokes the authentication method you configured in steps 1 and 2 of section 11.1.2.

If you’re content with Basic authentication, your work is done. However, HTTP Basic authentication is extremely weak because the password is sent with each request only slightly obfuscated using the well-known Base64 encoding (i.e., not encrypted or hashed), making it easy to pick off by network sniffers. A better choice is Digest authentication, in which the browser hashes the credentials before sending them to the server.

Negotiating credentials using a message digest

Digest authentication is more secure than Basic authentication, but that security comes at the cost of some additional setup. Start by changing the value of the auth-type attribute on the <web:authentication-filter> element to digest. Then, add two additional properties: key and realm. The value of the key property can be any string. Its purpose is to reduce the predictability of the digest created (i.e., the salt). The value of the realm property is used in the prompt that captures the user’s credentials. A typical prompt reads something like this:

  Enter username and password for "Open 18" at http://localhost:8080

The realm is the text in quotes, which in this case is the title of the application. You can pull the realm from a message bundle using EL notation, making it i18n-friendly. The result of these configuration changes is shown here:

  <web:authentication-filter url-pattern="*.seam" auth-type="digest"
    key="g0!f15f#n" realm="#{messages['application.title']}"/>

Seam provides the DigestAuthentication base class to handle the digest computation and validation. Thus, the next step is to change the authentication component to extend DigestAuthentication and delegate the work of validating the digest to the inherited validatePassword() method. Listing 11.2 shows a simplified example. Unfortunately, to use Digest authentication, you must store the passwords in the database unhashed. This change is necessary because the authentication routine must generate a digest from the original password to compare it against the digest sent by the client.

Listing 11.2. An authentication component used for HTTP Digest authentication
  package org.open18.action;
  import org.jboss.seam.security.digest.DigestAuthenticator;
  import ...;
  @Name("authenticationManager")
  public class AuthenticationManager extends DigestAuthenticator {
      @In private EntityManager entityManager;
      @In private Identity identity;

      @Transactional public boolean authenticate() {
          try {
              Member member = (Member) entityManager.createQuery(
                  "select m from Member m where m.username = :username")
                  .setParameter("username", identity.getUsername())
                  .getSingleResult();
              return super.validatePassword(member.getPassword());
          } catch (NoResultException e) {
              return false;
          }
      }
  }

Having seen the configuration for both form-based and HTTP authentication, you’ll likely agree that the form-based approach is no more difficult to implement in Seam than its HTTP counterpart. Given the fact that the gap in effort has been eliminated, three limitations of HTTP authentication now make it a less desirable option:

  • The login prompt doesn’t appear to be part of the application.
  • The user isn’t given an alternative when the login prompt appears (it is modal).
  • There is no standard way for the user to log out.

Of the three, the lack of a standard logout mechanism is its most significant downfall. This fact is acknowledged by the W3C in the User Agent Authentication Forms specification:

HTTP Authentication has the additional problem that there is no mechanism available to the server to cause the browser to “logout”; that is, to discard its stored credentials for the user.

However, in HTTP authentication, the credentials are sent by the browser with each request, so it’s really the browser’s responsibility to provide a logout button that lets the user signal when to stop sending the credentials. Unfortunately, no major browser supports this feature natively. One hack developers have discovered is that sending a 401 response causes some browsers to clear the authentication cache for the realm. But this trick isn’t reliable.

In light of all these complications, I strongly recommend using form-based authentication. The only catch with form-based authentication is that you have to worry about navigation (unless authentication is done using Ajax). In the next section, you’ll learn how to implement basic page security and how navigation to the login page gets tied into it.

11.2. Securing pages

The most common form of security in a web application is page-level security. Even when we get into securing components later on, the web layer still needs to be involved to direct the user to the login page or an error page when the user is denied access to a resource. In this section, you’ll learn why page-level security has traditionally been so difficult to enforce in JSF and the solution that Seam offers. You’ll then explore a couple of Seam’s page-level security features and how to use them to protect pages and serve them securely.

11.2.1. The challenge with JSF security

The biggest challenge in dealing with security in JSF is that there isn’t any. JSF has absolutely no notion of security anywhere in its design. Presumably, a decision was made that security is the concern of another layer, such as the EJB container or a servlet filter. This stance has made the task of implementing security (specifically page-level security) in a JSF application a real pain, again opening the door for Seam to step in and provide a solution. Seam has both page- and component-level security covered. In fact, you could even argue that security is Seam’s most significant and compelling enhancement to Java EE.

Why servlet filters don’t work

At first glance, a servlet filter appears to be perfect for implementing page-level security. It can trap an incoming request and make a decision about whether to let the request through or divert the user to another page. The main limitation with this approach is that it operates at too high a level, unable to track what’s going on inside the JSF life cycle. While the high-level view may work brilliantly for some applications, others require more insight.

When a URL is requested initially, the default behavior in JSF is to render the template for the corresponding view ID (ignore page actions for right now). So you set up the security filter to restrict access to a URL based on some rule. That restriction works as designed. Let’s say that the page has a JSF form with a UI command component that calls an action method. When the user clicks the button, the same URL is requested using a postback and the same restriction is evaluated. After the action method is called, JSF invokes a navigation event, which may result in a different view ID being rendered. The security framework has absolutely no knowledge of this switch and therefore can’t verify whether the user should be allowed access to the destination page. Figure 11.2 illustrates how the security filter is kept out of the loop when the navigation occurs.

Figure 11.2. The security filter isn’t aware of a navigation event that happens inside the JSF life cycle.

If you’re using JSP as the view handler, the navigation is performed using an internal forward handled by the servlet request dispatcher. It’s possible to get the filter involved by wrapping the filter around internal forwards, configured in the web.xml descriptor as follows:

  <filter-mapping>
    <filter-name>Third-party security filter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
  </filter-mapping>

However, this mapping only applies to view handlers that use the servlet request dispatcher. Facelets, for instance, uses its own mechanism for selecting the next view to render. Therefore, if your application uses Facelets, the security filter is once again left in the dark.

A contextual security wrapper

Another limitation of a third-party security filter is that it’s not part of the JSF application and thus doesn’t have insight into the application’s context. We talked about this problem back in Chapter 3 with regard to the stateless navigation model. The same limitation exists here. Can you decide if users should be granted access to a page based on the URL? Where did they come from, where are they going, and what is the current state of their session? These are questions you need to know the answers to in order to make a viable decision. The only way you’re going to get all of this information is if the security framework is integrated into JSF. That’s exactly what Seam offers.

Instead of operating before and after each request, like a security filter, Seam applies page-level security before and after the two page-oriented phases in the JSF life cycle, Restore View and Render Response. Thus, Seam can apply restrictions directly to a view ID (as opposed to a URL). When access to a resource is denied, Seam can route the user to an error page, but not before giving the user the opportunity to authenticate, a process we look at next.

11.2.2. Requiring authentication

If a nonauthenticated user tries to access a protected resource, Seam redirects the user to the login page, giving the user a chance to reveal his or her identity. Once the user authenticates, the authorization check is evaluated to determine if the user can access the protected resource. That describes how the application will work once you complete this section. For any of this to work, you first have to tell Seam where to direct a nonauthenticated user when a login is required.

Singling out the login page

One way a user can initiate the authentication routine is by navigating directly to the login page. While some users may navigate to this page willingly, most of the time you have to give them a little push. In Seam, you can protect a page (i.e., view ID) by requiring a nonauthenticated user to log in before accessing it, a security feature I like to call binary authorization. It’s like hanging signs throughout the application that read “Members Only.” This prerequisite is declared by adding the login-required attribute to the <page> node in the corresponding page descriptor:

  <page login-required="true"/>

Only Seam isn’t going to know where to direct a nonauthenticated user when this page is requested because you haven’t specified a login page. If a login page hasn’t been set, Seam throws a NotLoggedInException. We get to this exception shortly. For now, it can be avoided by simply configuring a login page. The view ID that hosts the login form is designated using the login-view-id attribute on the root node of the global page descriptor:

  <pages login-view-id="/login.xhtml"/>

Some applications require the user to be authenticated before doing anything else, in which case the login page is the home page. To accommodate this scenario, you can blanket the application with the authentication prerequisite by using the login-required attribute on a <page> node that matches all of the view IDs (though I don’t recommend it):

  <pages login-view-id="/login.xhtml">
    <page view-id="*" login-required="true"/>
  </pages>

There’s at least one page you do want to serve to nonauthenticated users: the login page, which you declare in the login-view-id attribute on the <pages> node. Seam understands the function of a login page and automatically excludes it from the list of restricted pages. It’s very important that you specify it to ensure the user always has a way to log in.

Even with the login page accessible, I don’t recommend declaring the site-wide login requirement using a wildcard because it can break the delivery of resources (e.g., JavaScript or CSS) sent through the JSF servlet. It’s far better to restrict only what’s necessary, perhaps grouping views into directories and securing them as needed (e.g., /admin/*).

Please show some identification

Instead of requiring the user to start at the login page, applications can use deferred authentication as a way to be more welcoming to new users and to avoid scaring them off by throwing up the dreaded login page too soon. In this model, the user is allowed to poke around the application anonymously until running into a protected resource. When that happens, the user is escorted to the login page and asked to show some identification before being allowed to proceed. This describes how the native JAAS login routine works. Seam, on the other hand, supports both up-front and deferred authentication.

You’ve already learned how to configure Seam to protect a page from being accessed by a nonauthenticated user, in which case Seam redirects the user to the login page. However, there are other ways to restrict access to resources, which you’ll learn about in the next two major sections. If a nonauthenticated user encounters any of the following scenarios, Seam throws the NotLoggedInException as a way to require the user to log in:

  • A view ID with a restriction is requested.
  • A method with a restriction is invoked.
  • A view ID that requires a login is requested (and the login view ID isn’t set).

As experience tells you, throwing an exception is certainly not going to bring up a login page—that is, unless, you handle the exception. Deferred authentication relies on the use of Seam’s exception-handling facility, covered in Chapter 3, to route the user to the appropriate view ID. The following exception handler catches the NotLoggedInException and mimics the behavior of Seam when protecting pages that require login:

  <exception class="org.jboss.seam.security.NotLoggedInException">
    <redirect view-id="/login.xhtml">
      <message
        severity="warn">#{messages['org.jboss.seam.NotLoggedIn']}</message>
    </redirect>
  </exception>

Now, any time a nonauthenticated user encounters a restricted resource, the user is escorted to the login page. You can raise the NotLoggedInException in your own code to require the user to authenticate.

 

Warning

When Facelets is running in development mode, it intercepts exceptions thrown in the Render Response phase. In that case, Seam’s exception-based routing will not kick in.

 

The only danger in all of this redirecting is that it risks losing the user’s spot in the application. Whenever you design an application that uses deferred authentication, you want to keep the disruption caused by asking the user to authenticate to a minimum. One of the most obvious ways of ensuring minimal disruption is to redirect the user back to the page that was originally requested once authentication is successful.

Keeping the interruption to a minimum

To get the user back to the page that was originally requested, you have to capture the current URL before the user is directed to the login page. After successful authentication, you need to retrieve the saved URL and redirect the user back to it. Given that Seam manages the authentication interlude, you may wonder how you’re going to get in there and change the course of action. The key is observing the authentication events.

Seam has a built-in component, named redirect, that is capable of capturing the current view (as well as the request parameters) and redirecting back to that view. You just need to connect this component to the event that’s raised when the user is being directed to the login page and the event that’s raised when authentication is successful. You can register the redirect component to observe these events in the component descriptor:

  <event type="org.jboss.seam.security.notLoggedIn">
    <action execute="#{redirect.captureCurrentView}"/>
  </event>
  <event type="org.jboss.seam.security.postAuthenticate">
    <action execute="#{redirect.returnToCapturedView}"/>
  </event>

You may ask yourself how the redirect component manages to store the captured view ID while the user is authenticating. The little-known secret is that redirect, which is a conversation-scoped component, begins a long-running conversation when the captureCurrentView() method is called, if one isn’t already active. That conversation is ended when the returnToCapturedView() method is called, if it was started by the redirect component. Thus, the captured view is preserved in a long-running conversation that surrounds the login process. See the accompanying sidebar for another important use of authentication events.

 

Keeping memory consumption in check

One of the boldest claims that Seam makes is that it helps eliminate the problem of HTTP sessions consuming excessive memory. However, this statement doesn’t account for the fact that too many sessions, no matter how small, can cause leaks. If your site receives a lot of anonymous traffic, you end up paying for each visit long after the visitor has left.

Fortunately, there’s an elegant solution using events. Start by making the default session timeout very short. This value is configured (in minutes) in the web.xml descriptor:

     <session-config>
      <session-timeout>10</session-timeout>
     </session-config>

Then, grant authenticated users a more comfortable timeout period in an action or method that observes the postlogin event. To change the timeout, you explicitly assign a new value (in seconds) to the active HTTP session object, shown here in an event action:

     <factory name="currentSession" scope="stateless"
       value="#{facesContext.externalContext.request.session}"/>
     <event type="org.jboss.seam.security.loginSuccessful">
       <action execute="#{currentSession.setMaxInactiveInterval(3600)}"/>
     </event>

Always restrict anonymous users from consuming system resources in excess. After all, they may not even be valid guests.

 

Another way to make the login page less of an annoyance is to remember the user on subsequent visits. Authenticating is a chore, so you want to make it as pleasant as possible.

Recognizing the regulars with “remember me”

If you visit the same coffee shop every morning on your commute to work, after a while, the baristas start remembering you. The really good ones just say “Hi,” perhaps calling you by your name, and start brewing your drink of choice. You can do the same for the users of your application by using a feature known as Remember Me.

Remember Me is the check box that often accompanies the username and password fields on login forms. Although the developer’s intention for putting it there is to help the user, it often causes confusion. I’ll admit that it has confused me at times. The source of this confusion is the fact that applications interpret this check box in two very different ways:

  • Username only— Populate the username field with the value from the previous visit.
  • Auto-login— Automatically authenticate the user with the credentials last used.

Both implementations work by storing a persistent cookie in the user’s browser. (A persistent cookie is a cookie that isn’t removed when the browser closes.) The cookie is assigned a value during the postauthentication routine, and that value is read in the next time the user is sent to the login page. In the username only scenario, the username is stored in the cookie and is used to populate the username field whenever the login page is displayed. In the auto-login scenario, an authentication token is stored in the cookie, which is used to log in the user quietly, thus bypassing the login page altogether.

Seam 2.0 provides the username only implementation out of the box. You enable it by setting the rememberMe property on the identity component to true, as shown here:

   <security:identity
     authenticate-method="#{authenticationManager.authenticate}"
     remember-me="true"/>

By default, the Remember Me cookie is set to expire after one year of inactivity. You can assign an override value, in seconds, on the built-in facesSecurityEvents component:

   <security:faces-security-events cookie-max-age="604800"/>

Seam 2.1 provides both Remember Me implementations and also makes it easy to implement your own solution. The “Remember Me” switch was moved to the rememberMe component. This component has a property named mode that controls which implementation is used. The possible values are usernameOnly and autoLogin, with usernameOnly being the default. Here’s the same configuration as shown earlier for Seam 2.1:

  <security:rememberMe enabled="true" cookie-max-age="604800"/>

To implement your own solution, you observe the postauthentication event and the org.jboss.seam.security.quietLogin event. The latter event is raised just before the user is sent to the login page. If the security principal is established by a method observing this event, the user won’t be taken to the login page (and hence won’t be disrupted).

Once the cookie has been created, regardless of implementation, the last username that the user entered is always accessible from the username property on the identity component (and the credentials component in Seam 2.1). Since the username field on the login form is bound to this property (e.g., #{identity.username}), this explains how this field gets autopopulated. You may question how this helps the user, since most browsers offer to fill in the credentials anyway. To see the benefit, you must think outside the login page, so to speak. Knowing the last username entered lets you pull nonsecure information out of the database, such as the user’s preferences. If the user tries to perform a secure action, you can make them log in at that time.

 

The danger of using auto-login

Although convenient for the user, auto-login using a persistence cookie is dangerous. Any cross-site scripting (XSS) vulnerability in the application can be exploited by an attacker to send the user’s authentication token outside of the application. The attacker can then use this token to authenticate as the user. A bigger risk with auto-login is cross-site request forgery (XSRF). In this case, the attacker knows that the user is always logged in and can “remote control” the user’s session by getting the user to request a URL that performs an action on the site. In neither case is the application aware of the trickery.

Browser vendors recognized the danger of application-initiated logins and the motivation for using them, so they introduced a feature known as “Remember Passwords.” In this case, the browser takes care of remembering the username and password credentials for a given website and fills out the login form for the user automatically. This approach is almost as convenient as auto-login but it’s inherently much safer because the browser’s keychain is not accessible to XSS or XSRF attacks, nor can it be read by local users.

In general, auto-login is a bad practice and you should avoid the temptation of using it. The username only Remember Me implementation doesn’t pose this risk.

 

Regardless of which implementation you use, my advice is to choose a label that fits. If you’re using username only, make the label “Remember my username” and if you’re using auto-login, make the label “Don’t make me log in again.” That should clear up the confusion.

There’s another dimension of security to consider. In addition to protecting pages from being accessed by nonauthenticated users, you may want to secure the communication channel to protect a request from network sniffers. In production applications, you almost always want to serve the login page securely, and perhaps other pages as well. Seam can ensure the proper switch is made on a page-by-page basis or across the whole application.

11.2.3. Serving pages securely

When a high-ranking official makes a top-secret call to the Pentagon, it’s not done over a regular telephone line. Instead, the official makes a request to get a “secure line.” The equivalent in web applications is an HTTPS request. The HTTPS protocol encrypts the traffic sent to and from the server using the Secure Sockets Layer (SSL).

In development it’s easy to forget about SSL security. Developers tend to stick with the HTTP protocol when testing locally since SSL is typically only configured in the production environment (hopefully this section encourages you to make the effort). However, just as the high-ranking official doesn’t want to risk leaking information over a nonsecure telephone line, you don’t want the users of your application to expose their sensitive data over an insecure web transmission. Failure to capture users’ credentials over the HTTPS protocol makes them vulnerable to sniffing and jeopardizes the security of your application.

Getting a secure line

In some infrastructures, the entire application is served over HTTPS and the protocol is handled by the web server. If that’s the case, you can safely skip over this section. However, if your application uses a mixed environment, and it’s up to the application to decide when to switch between secure and nonsecure requests, you need to pay attention here.

The URL prefix, which determines whether the request is secure (https) or nonsecure (http), is known as the scheme. The scheme is configured at the page level by specifying the scheme attribute on a <page> node in the page descriptor. The acceptable values are http and https. You can configure Seam to serve the login page over HTTPS as follows:

  <page view-id="/login.xhtml" scheme="https"/>

Now, when authentication is necessary, Seam routes the user to the secure URL for the login page. If the user requests the login page directly using the HTTP protocol, Seam issues a redirect to the HTTPS equivalent URL. You can configure additional pages this way. Note that Seam’s UI command components and the page descriptor’s redirect rule are also aware of the scheme setting and will build the URL for the target view ID accordingly.

Finding the right port

If a scheme isn’t specified for a view ID, Seam sticks with the scheme from the previous request. Therefore, if you mark the login page to be served securely, and no other pages have a scheme set, the user becomes permanently stuck in HTTPS after a trip to the login page. If your application doesn’t require SSL across the board, it’s better for performance to revert back to HTTP to serve low-risk pages. Since Seam issues a redirect to the scheme defined for a page if the wrong scheme is requested, this setting can be used to switch back to a nonsecure line. Use the following configuration to have all pages without a scheme defined served over HTTP:

  <page view-id="*" scheme="http"/>

You may wonder how Seam knows how to modify the URL. In the default configuration, it’s quite simple. Going from nonsecure to secure, Seam simply changes the beginning of the URL from http to https, and vice versa. That works as long as the server uses the standard scheme-to-port mapping (port 80 for HTTP and port 443 for HTTPS). If you’re using different ports, then you need to let Seam know what the port numbers are. First, add the component namespace http://jboss.com/products/seam/navigation, prefixed as navigation, to the component descriptor. Next, configure the built-in component named pages to set the ports:

  <navigation:pages http-port="8080" https-port="8443"/>

As one final measure, if the data in your application is particularly sensitive, you may want to consider invalidating the HTTP session when the scheme changes. This feature is controlled by the built-in component named session in the web namespace setup earlier. You configure this component to invalidate the session when the scheme is changed as follows:

  <web:session invalidate-on-scheme-change="true"/>

Keep in mind that if you destroy the session on a scheme change, you also terminate any of the user’s conversations or session-scoped data. This setting is designed to be used when the security requirements are stringent and loss of state is acceptable.

You’ve seen how to implement authentication, how to keep nonauthenticated users from accessing protected pages by requiring them to log in, and how to send data over a secure channel. But there’s plenty more to learn about securing your application. Next up, you’ll learn to enforce restrictions based on roles from the user’s identity to determine where the user can go in the application and what actions the user can perform. Restricting access according to role membership is known as role-based authorization.

11.3. Role-based authorization

Authentication and authorization are easily confused with each other. Authentication is about establishing the user’s identity. Authorization, the second A, is about checking to see whether the user is permitted to access a restricted resource or perform a restricted action. The restriction is based on a fact. In the previous section, you used binary authorization, which separates the members from the guests. In that case, the fact reads “the user is authenticated.” If that fact can be verified, the user is permitted access. However, once the user is authenticated, you need more facts to check (otherwise, everyone would be an administrator).

If you recall, one of the user’s principals is a collection of roles. So you can create facts that separate users that have a role from those that don’t, known as role-based authorization. In this case, the fact reads “the user has role X.” Once again, if the fact can be verified, the user is permitted access. For instance, you can require that a user be a member of the admin role in order to enter the administration section of the application. In section 11.4, you’ll learn about rule-based authorization, which consists of fine-grained, contextual facts of arbitrary complexity. Rule-based authorization can be used in cases where roles alone are too broad.

In this section, you’ll learn how to express a restriction and add it to a page, component, or method. Seam takes a declarative approach to security by using annotations, and it relies heavily on the EL, a constant theme throughout this book and throughout the framework. The focus of this section is on role-based authorization, though the infrastructure presented here also applies to rule-based authorization.

11.3.1. Expressing restrictions

It should come as no surprise to you that the universal way of checking whether a user has been granted a role in Seam is to use an EL value expression. What you may find puzzling, though, is how to perform such a check using EL notation. That’s because to check if the user has a role, you have to pass in the role name as an argument. In standard EL notation, a value expression can only access JavaBean properties of a context variable. But you learned in Chapter 4 that Seam incorporates the JBoss EL, which allows you to invoke methods with parameters. Brilliant! So you can seek out the component in Seam that performs security checks and invoke it using a parameterized value expression. Ah, but Seam has yet another solution based on EL functions. Let’s start there first.

Seam’s EL security functions

The Unified EL, introduced into Java EE through the JSP 2.1 specification and the foundation of Seam’s EL support, provides a function mapper capable of linking an EL function name to a static method on a Java class. Seam registers two security-related EL functions: s:hasRole and s:hasPermission. The s:hasRole function is used to perform role-based checks, and the s:hasPermission function performs a rule-based check using the Drools rule engine, as shown in figure 11.3. The prefix s is a hardcoded namespace that prevents naming conflicts with other EL functions. The syntax of EL functions resembles that of JSP functions, but there’s no dependency on JSP. You don’t even have to do anything to enable them—Seam takes care of that for you. In fact, these functions can be used anywhere in Seam that EL is accepted. However, they merely delegate to equivalently named methods on the identity component, so you may want to consider just using the parameterized EL to invoke this component directly to remain consistent with the rest of your codebase.

Figure 11.3. Seam supports two authorization models. Role-based authorization compares role names to the user’s role principals. Rule-based authorization delegates the decision to Drools.

You’ll learn how to use rule-based security in the next section. For now, we focus on the role-based check, the left-hand path in figure 11.3. The mechanics of how these two functions are treated from the security API is consistent.

 

Note

In Seam 2.0, if you attempt to perform an authorization check using s:hasPermission and the Drools library is not available on the classpath, the check will return false. Even with Drools on the classpath, a user won’t be granted permission if the corresponding rule is missing. Seam 2.1 relaxes the coupling with Drools by making the permission resolver pluggable.

 

The function s:hasRole maps to the static method defined here. This method uses the identity component to verify whether the authenticated user is a member of the given role:

  public static boolean hasRole(String name) {
      return Identity.instance().hasRole(name);
  }

The function s:hasPermission has a similar mapping. Given that the work is delegated to the identity component, you could just invoke its hasRole() method directly:

  #{identity.hasRole('admin')}

But to save a couple of keystrokes, you can use the s:hasRole function instead:

  #{s:hasRole('admin')}

Internally, both expressions invoke the hasRole() method on the identity component, which returns a boolean indicating whether the supplied role is present in the user’s group identity. You can use either form, but again, the first choice has the benefit of being consistent with the rest of your codebase.

 

Note

The delimiter in s:hasRole is a colon (:) not a dot (.), signaling a function call.

 

The s:hasRole function can be used multiple times in the same expression or combined with other EL logic. This expression checks two roles to verify the administrator is a golfer:

  #{s:hasRole('admin') and s:hasRole('golfer')}

Unlike other security checks in Seam, the s:hasRole function doesn’t throw an exception, raise an event, or attempt to redirect the user when the verification fails. Thus, it’s perfect for rendering UI elements or controlling navigation according to what roles the user is granted.

Role-based decisions

One way to use the s:hasRole function is to tie it to the rendered attribute of a UI component as a way to hide page elements that unauthorized users should not see:

  <s:link view="/admin/GolferList.xhtml" value="Administer Golfers"
    rendered="#{s:hasRole('admin')}"/>

It can also be used to redact sensitive data, using it as the condition in a ternary operation:

  <h:outputText value="#{s:hasRole('admin') ? golfer.emailAddress : ';XXX'}"/>

You can also use it to route the user according to role, a form of contextual navigation:

  <navigation from-action="home">
    <rule if="#{s:hasRole('admin')}">
      <redirect view-id="/admin/home.xhtml"/>
    </rule>
    <rule if="#{not s:hasRole('admin')}">
      <redirect view-id="/home.xhtml"/>
    </rule>
  </navigation>

Optionally, you can move this navigation logic to the action method, injecting the identity component and invoking it directly:

  @In Identity identity;

  public String action() {
      if (identity.hasRole('admin')) return "/admin/home.xhtml";
      else return "/home.xhtml";
  }

That should stir up your thinking about how to use the security-related functions and their counterparts on the identity component. But so far, we’ve only determined whether or not the user has a role. To get the actual authorization part, you need to place this check somewhere that Seam can use it to enforce the restriction and take appropriate action when the criteria isn’t met. For that, Seam provides declarative restrictions.

11.3.2. Declaring role-based restrictions

Restrict declarations are like having security guards at various places in your application. To pass by, the user must be authenticated and must satisfy the restriction. If the user is denied access, the user is either sent to the login page or an exception is thrown.

Seam provides two restriction labels: one for securing JSF views and one for securing components. They are both capable of performing a role-based authorization check as well as a rule-based one. While this section focuses on role-based security, keep in mind that these labels are designed for any type of restriction.

Securing JSF views

Hiding a link that leads to a restricted page keeps the user from wandering down the wrong path, but we need to do more than hide links to secure pages. You learned earlier that you can require a user to log in before accessing a page. To get page-level security for authenticated users, you add the <restrict> element to a <page> node in the page descriptor. Seam enforces the authorization criteria in the <restrict> element prior to restoring (JSF postback) and prior to rendering (both an initial request and postback) the view ID(s) defined in the <page> node. If a restriction fails, both an event and an exception are raised. Table 11.3 lists the event and exceptions that are used, depending on whether or not the user is authenticated at the time the restriction fails.

Table 11.3. The event and exception that Seam raises when an authorization restriction fails

Authenticated?

Event raised

Exception raised org.jboss.seam.security.*

No org.jboss.seam.security.notLoggedIn NotLoggedInException
Yes org.jboss.seam.security.notAuthorized AuthorizationException

The AuthorizationException can be handled using a similar configuration as earlier to direct the user to an error page. The following exception configuration catches authorization errors and directs the user to a security error page:

  <exception class="org.jboss.seam.security.AuthorizationException">
    <redirect view-id="/securityError.xhtml">
      <message
        severity="warn">You've been denied access to the resource.</message>
    </redirect>
  </exception>

Let’s continue with the example of securing the administration section of the Open 18 application to demonstrate use of the <restrict> tag. To keep nonadministrators away from pages in the /admin/ folder, you declare a <restrict> tag as follows:

   <page view-id="/admin/*">
     <restrict>#{s:hasRole('admin')}</restrict>
   </page>

Notice that the body of the <restrict> tag contains an EL expression. That EL expression must return a boolean value, but otherwise it can be as complex as you need it to be, as explained in the previous section. If the body of the <restrict> tag is empty, Seam performs a rule-based permission check following a convention covered in section 11.4.4. The rule-based permission check has the benefit of being able to distinguish between the restore phase and the render phase. A rule is capable of checking for a role, so you can use a rule as a way to get both flavors of security. Either way, to have Seam apply authorization at the page level, whether it be role- or rule-based, you have to use the <restrict> tag.

Securing pages is only half the battle. If, by some chance, the user finds his or her way through the page-level restrictions or accesses the component through some other channel (i.e., web service), you want to be sure that your classes and methods are also locked down. Seam allows for declarative restrictions on all Seam components.

Securing components

Components are secured by adding the @Restrict annotation at the class or method level. The restriction is expressed using an EL string in the value of the annotation, which can accept the s:hasRole and s:hasPermission functions, or just check that the user is authenticated using #{identity.loggedIn}. Once again, if the value is omitted, Seam performs a rule-based check following the convention covered in section 11.4.4. If the check fails, the same event and exception are thrown as with the <restrict> tag.

Let’s assume that there is an action method that is called in the administration area that grants pro status to a golfer. The @Restrict annotation can enforce that only users with the admin role can invoke this method:

  @Restrict("#{s:hasRole('admin')}")
  public void grantProStatus() { ... }

Having to specify this restriction for every method of a class can be tedious, so you can opt to blanket the component by specifying the restriction at the class level:

  @Name("golferAdminAction")
  @Restrict("#{s:hasRole('admin')}")
  public class GolferAdminAction {
      public void grantProStatus() { ... }
      public void anotherAdminAction() { ... }
  }

Any method that doesn’t have a @Restrict annotation will inherit the requirement from the class-level annotation. You can override the class-level restriction by applying the @Restrict annotation to an individual method. One way to restore anonymous access is to use an EL expression that invariably returns true:

  @Restrict("#{true}")
  public void safeToExecute() { ... }

Table 11.4 gives an overview of the @Restrict annotation.

Table 11.4. The @Restrict annotation

Name:

Restrict

Purpose:

Used to define a security restriction that is enforced before a method or all methods on a component can be invoked

Target:

TYPE (class), METHOD

Attribute Type Function
value String (EL) An EL value expression that is evaluated to determine whether the user has permission to execute the method. The EL functions s:hasRole and s:hasPermission are typically used to build the restriction. If the expression evaluates to false, Seam directs an anonymous user to the login page or throws an authorization exception if the user is authenticated. Default: an automatic rule-based permission check.

In addition to being used on Seam components, the @Restrict annotation can be used on entity classes and enforced prior to persistence events, which is covered later.

Asserting an authorization

When Seam enforces a restriction, it does more than just give a thumbs up or thumbs down. As I mentioned, Seam takes action if the check fails. The declarative restrictions we just covered delegate to the checkRestriction() method on the identity component. For example:

  Identity.instance().checkRestriction("#{s:hasRole('admin')}");

This is one of several methods on the identity component that assert a condition, rather than just check for it, where assert means to take action if the check fails. The other two methods that assert are checkRole() and checkPermission(), which complement the hasRole() and hasPermission() functions, respectively. You can assert that the user has a role in Java as follows:

  Identity.instance().checkRole("admin");

Whether you decide to check or assert depends on whether you’re prepared to turn over control to Seam if the condition fails. For instance, you might want a chance to record the incident if the user isn’t authorized. In that case, you just want to check, not assert:

  if (!Identity.instance() .hasRole("admin")) {
      AuthorizationViolation violation =
          new AuthorizationViolation(Golfer.class, currentGolfer.getId());
      entityManager.persist(violation);
      throw new AuthorizationException(
          "You must have the admin role to perform this action. " +
          "This incident has been reported.");
  }

Even if you aren’t in control of the authorization, you can record a failed restriction in a method that observes the org.jboss.seam.security.notAuthorized event. The downside is that you lose the context in which it was raised.

King for a thread

After defining a restriction on a method, you may have a scenario where that method needs to be used even though the user doesn’t have the appropriate authorities. The goal here is not to restrict the user, per se, but to secure a component so that it can only be invoked by another component under a known set of circumstances (i.e., a use case).

One way to solve this problem is to use contextual security rules, which are covered in the next section. But when role-based security is all you have, you can execute a method using elevated privileges. Seam supports this feature through the RunAsOperation class. For those of you who know Unix, this is the equivalent of the sudo command. You instantiate the class and override the execute() method, which is where you put the privileged code that you want to run, then pass the instance to Identity.runAs() to be executed. You can set a new Subject, Principal, or set of roles to set the identity of the “current” user. If you don’t specify a subject or roles, then the code executes as if the user isn’t authenticated. In Seam 2.1, you can set the code to run as a system operation, which bypasses all restrictions. Here’s an example that grants the admin role for the operation:

  new RunAsOperation() {
      public String[] getRoles() {
          return new String[] { "admin" };
      }
      public void execute() {
          facilityAction.setOwner(golfer);
      }
  }

As this section has demonstrated, role-based authorization is straightforward to implement in Seam. However, it’s also very coarse. That’s great for securing entire sections of the site, such as the administration section, but it becomes difficult to meet all the security requirements of a multiuser application. These requirements demand more sophisticated authorizations based on contextual rules.

11.4. Rule-based authorization using Drools

This section covers authorization decisions made by the right-hand path in figure 11.3. Following this path, a permission is fed into a rule to decide whether to grant access. A permission consists of a target and the action to be performed on that target. In that sense, permissions are like access control lists (ACLs). Unlike with role-based authorization, Seam doesn’t just inspect the user’s identity to decide whether to allow access. Instead, logic of arbitrary complexity “crunches the context” and out comes the verdict. In this section, you’ll learn how to define a permission and how to implement the logic to support it using Drools rules. Let’s first look at how rule-based authorization differs from role-based authorization.

11.4.1. Rules vs. roles

Averting security is a common theme in action movies. Getting past the security guards is never much of a problem for the assailants. A mere bump on the head is usually enough to thwart that obstruction. What hangs up the intruders is a dense network of lasers crisscrossing the room that hosts the artifact they’re after. Maneuvering through this web of barricades would require breaking the laws of physics.

Role-based authorization can be equated to the defense provided by the feeble security guards. It’s predictable and naïve. It performs its rounds, giving attention to known weak spots, but it’s completely unaware of the threat that looms. Although this line of defense has its uses, it can’t measure up to complex security requirements. Rule-based authorization, on the other hand, provides much tighter security. It knows exactly what it’s protecting and can activate itself under a certain set of conditions. It’s also known as contextual security.

Consider the security requirement that a record can only be modified by the user that created it. Role-based authorization isn’t sufficient in this case since the best the restriction can do is ensure that the current user has permissions to modify records (e.g., the edit role). If the system holds records from different customers, and the role-based authorization granted the user access, that user might be allowed to modify a record owned by a different customer. Yikes! The role restriction can’t make a decision based on the relationship between the user and the record. It’s blind to the context.

Naturally, the first thing that comes to mind is to craft custom logic that verifies the owner of the record is the current user before granting access. Although you may not realize it, by doing so, you are implementing rule-based authorization (it’s just not pretty). The problem with this custom approach is that the restrictions are no longer declarative. They’re hardcoded into the business logic. You want to be able to assign rule-based restrictions in the same way as you specify the role requirements. That’s exactly what Seam’s rule-based authorization allows you to do.

11.4.2. Setting up Drools

In Seam, rule-based authorization checks are handled by Drools. Drools is a rule engine that supports declarative programming. You define a set of rules that must be matched in order for the user to have access to a resource. These rules allow for separation of restriction logic and business logic. More importantly, the restrictions can be modified without affecting the business logic. They can even be swapped out dynamically at runtime if set up appropriately.

 

Note

The name Drools is derived from the term “dynamic rules.” The rules are dynamic because they’re compiled and interpreted at runtime.

 

Seam’s rule-based authorization is considered the advanced security mode in its security model. The reason for this label is that it requires a few extra libraries and configuration. It also requires knowledge of how to author rules using the Drools rule language. The fact that basic security mode can be implemented without any add-ons or special knowledge is an important aspect of Seam’s security model. Regardless, the power of rule-based authorization is worth the extra effort.

 

Permission management in Seam

The design of Seam’s authorization mechanism was improved considerably in Seam 2.1. While the rule-based authorization covered in this section remains, it’s not the only way to implement permissions. Permission checks are now delegated to a chain of resolvers, one of which is a new persistent permission resolver that reads permissions from a database and another to support rule-based permissions. You can also implement and register your own resolver.

Storing permissions in the database is convenient because they can easily be granted and revoked from the user interface. To help manage these permissions, Seam provides a permission manager, complementing the identity manager introduced earlier. A permission is mapped to the database using annotations on entity classes, allowing Seam to search them. The focus of this chapter is on rules, but feel free to explore persistent permissions as an alternative to writing a rule.

Both the permission manager and identity manager require that you have the permissions to invoke the methods in either API.

 

The good news is that if you used seam-gen to prepare your project, there’s no additional work you must do to set up rule-based security using Drools. You’re all set! If you’re going at it alone, you first need to get the required libraries, drools-core.jar, droolscompiler.jar, core.jar (Eclipse JDT), antlr-runtime.jar, janino.jar, and mvel14.jar. The second step is to activate the set of security rules. Don’t worry about the actual rules right now. We get to them once the configuration is in place.

Security rules are registered using the built-in RulesBase component, named rulesBase, specified as one or more rules files. Typically, the rules are defined in a file named security.drl at the root of the classpath. First, add the component namespace for Drools to the component descriptor, http://jboss.com/products/seam/drools, prefixed as drools. Then, declare the securityRules component:

  <drools:rules-base name="securityRules" rules-file="/security.drl"/>

The identity component assumes that the RuleBase for security is named securityRules, so its best to use this name.

You’re now ready to start adding rules! There’s only one problem, though. How do you write rules? It’s time for your crash course in Drools. I promise that it’s a lot simpler than it first appears, and you’ll be glad you invested the time to learn it because it’s so powerful.

11.4.3. Creating rules with Drools

I don’t know about you, but I like crash courses. Who wants to go through a whole semester when you can learn it all in one sitting? The good news is that rules are such a simple concept that you can probably learn how to write them in the time it takes to drink your coffee. Which cup are you on now?

The motivation for creating rules

Let’s start with the end in mind. That way, we’ll know when we’ve covered just enough to get by. I alluded earlier to the fact that the complement to the s:hasRole EL function is s:hasPermission and that it’s capable of performing rule-based permission checks. You can use s:hasPermission in lieu of s:hasRole in both the <restrict> page descriptor tag and the @Restrict annotation. However, it’s more than just changing the function name. The s:hasPermission function consults the security rules to determine whether the executing thread should be granted permission to perform the action on the given target.

The s:hasPermission function takes three arguments. The first is a resource name, the second is an action, and the third is a context variable that is inserted into the working memory. (If you invoke the hasPermission() method on identity directly, you can pass any number of additional arguments, which get added to the working memory.) These values define the permission that needs to be verified. The permission is sent off to the rule engine, which looks for a matching rule that will grant the user access. If a match isn’t found, Seam presumes that the current user doesn’t have access and takes the appropriate action.

Let’s consider an example scenario, which can be used to understand the purpose of a rule. Private golf facility owners are particular about the information that’s made available about their facility. Therefore, they ask that only administrators be able to modify private facilities (e.g., country clubs). The first thing we want to do is place this restriction on the update method of FacilityHome using the @Restrict annotation:

  @Restrict(
      "#{s:hasPermission('facilityHome', 'update', facilityHome.instance}")
  public String update() {
      return super.update();
  }

The restriction is examined in figure 11.4. This permission check attempts to determine whether an instance of Facility can be modified by the current user.

Figure 11.4. The set of conditions that are passed into a rule-based authorization check

s:hasPermission accepts the conditions. The decision is handled by the rule engine. That brings us to rule engines and how they process conditions to arrive at a decision.

Drools 101

Drools is an inference engine. It matches conditions against facts. When a matching condition is located, the rule is activated and action is taken. This type of rule processing is known as a forward-chaining. Facts are checked to arrive at a conclusion. You can think of it as a glorified if-then statement. The crucial distinction is that rules aren’t executed sequentially. The order of the rule processing is optimized using the Rete algorithm.[1] That’s part of what makes evaluating them so efficient. As a general rule, it’s a good idea not to count on the rules firing in any particular order and try and author the rules without worrying about a particular “flow.” (But don’t be afraid to have lots of rules because they are cheap.)

1 The Rete algorithm was designed by Dr. Charles L. Forgy. See http://en.wikipedia.org/wiki/Rete_algorithm.

Facts are the objects present in the working memory. You can think of the working memory as similar to the persistence context. It holds data in a runtime cache. You can query (match conditions) and perform operations (execute actions) on the objects that it maintains. The working memory is also referred to as a session.

As with EJB components, Drools supports both stateful and stateless sessions. A stateful session is maintained over multiple invocations of the rules, whereas a stateless session is discarded after the rules are done firing. An invocation ends when all of the permutations of each rule have been tested.

Seam uses a stateful session and populates it with the security principals, which are the primary principal and the set of roles associated with the current user. Since the primary principal and the principals that represent the roles are both instances of the same class, Principal, Seam inserts the roles into the working memory as Role objects to distinguish them. Seam also inserts objects into the working memory specific to the permission check, which it later cleans up when the check is complete.

So far, you know that there’s a working memory that stores objects, that rules are fired in some optimized order to match facts in the working memory, and that the purpose of the rules is to execute actions as the result of drawing a conclusion. The next step is to understand the anatomy of a rule.

Creating a permission check rule

A rule consists of two parts: a premise and a conclusion. The premise is known as the left-hand side (LHS) and the conclusion is known as the right-hand side (RHS). To support these two sides, Drools uses a custom syntax for defining rules, known as the Drools Rule Language (DRL). Its syntax is reminiscent of Java. In fact, the code in the conclusion is Java. The premise uses a shorthand that’s focused on matching, though it may look confusing the first time you see it.

Each rule must be given a unique name (for a given session). The name you choose is arbitrary and isn’t referenced anywhere in the code. Let’s begin creating a rule by assigning it a name:

  rule ModifyPrivateFacility
  ...

Next, we need to define a premise. Each line in the when statement consists of a condition. All of the conditions must evaluate to true for the rule to fire. For this rule, we want to ensure that if the golf facility is private, the current user has the admin role. However, we don’t just want this rule to fire at any old time. We only want it to fire when the restriction #{s:hasPermission('facilityHome', 'update', facilityHome.instance)} is consulted. How do we know which restriction check kicked off the rules? Seam creates an instance of the PermissionCheck class and stores it in the working memory prior to executing the rules. The PermissionCheck holds the first two arguments to s:hasPermission the target and the action. It also maintains a flag that determines whether permission should be granted, which starts off as false. The purpose of the rules is to determine whether permission should be granted. The final argument to s:hasPermission, the context variable, is inserted directly into the working memory. Continuing to build out our rule incrementally, the condition clause can now be added:

  rule ModifyPrivateFacility
  when
    $perm: PermissionCheck(name == "facilityHome", action in ("update","remove"), granted == false)
    Role(name == "admin")
    Facility(type == "PRIVATE")
  ...

Before you reach to scratch your head, I’ll help you make sense of this syntax. Recall that a rule is intended to match facts in the working memory. The class names in the previous snippet are being used to locate objects of those types in the working memory. What looks like a constructor is actually shorthand for running the instanceof operator against all objects in the session. The arguments used between the brackets is another shorthand for checking property values against expected values. The name on the left side of the operator is the name of the bean property. The value of the property is being compared against the test value on the right-hand side of the operator. Unlike with Java, two objects are equal when compared with the == operator as long as their equals() and hashCode() methods return the same value. The Java syntax equivalent of Role(name == "admin") is as follows:

   objectInWorkingMemory instanceof Role &&
     "admin".equals(((Role) objectInWorkingMemory).getName())

This check is performed against every object in the working memory. I’m sure you’ll agree that the rule DRL syntax is simpler. You may be wondering what the prefix $perm: at the start of the first condition is all about. Any time you see a name followed by a colon, it’s a declaration. The purpose of a declaration is to create an alias. The name of the alias can be any valid Java variable name. The dollar sign ($) is not specific to Drools—it’s a legal character. Beginning alias names with $ is a convention used to help distinguish between aliases and bean property names.

An alias establishes a back reference. It’s created either for use in subsequent conditions or in the action of the rule. In the case of the PermissionCheck, an alias must be created so that the grant() method can be called on it if the rule’s condition is true. With that said, let’s conclude the rule:

   ...
   then
     $perm.grant();
   end

You can put any Java code you want in the conclusion (it gets compiled at runtime). Here, it’s limited to granting permission. We aren’t done just yet. Any time you modify an object in the working memory, rule execution stops and all the rules are fired again. To prevent the rule from being evaluated more than once, you can add the no-loop operator. The no-loop operator, highlighted in bold, is placed in the rule’s options section:

  rule ModifyPrivateFacility
    no-loop
  when
  ...

Since all users are denied access until granted permission by a rule, only users with the admin role can modify private facilities. However, at this point, nobody is allowed to modify a nonprivate facility. Time to add some context!

Contextual rules

While the private facility owners have asked us to only allow administrators to modify private facilities, we don’t want to lock facility owners out from their own facilities, and we want to grant access to any member to modify nonprivate facilities. Thus, in one case, the rule must be aware of the absence of an object in the working memory, and in the other, it must be able to correlate objects in the working memory to the authenticated user. The rules for these two cases are shown in listing 11.3:

Listing 11.3. Rules that allow a user to modify a facility under certain conditions
  rule ModifyNonPrivateFacility
    no-loop
  when
    $perm: PermissionCheck(name == "facilityHome", action in ("update", "remove"), granted == false)
    Role(name == "member")         
    not Facility(type == "PRIVATE")     
  then
    $perm.grant();
  end

  rule OperateOnOwnFacility
    no-loop
  when
    $perm: PermissionCheck(name == "facilityHome", granted == false)    
    Role(name == "member")
    Principal($username: name)     
    Facility($golfer: owner)     
    Golfer(username == $username) from $golfer     
  then
    $perm.grant();     
  end

There are a couple of things to point out in these two new rules. The ModifyNonPrivateFacility rule is almost identical to the ModifyPrivateFacility rule except that the role is downgraded to member and the check for the private facility type is negated .

The second rule is more interesting. The PermissionCheck doesn’t look at the action property so this condition applies to any permission triggered from the facilityHome component. The relationship between Principal and Facility is interesting. The Principal condition merely checks for the presence of Principal in the working memory. If present, the value of its name property is aliased to $username . The Facility condition verifies that a Facility instance is present in the working memory and creates the alias $golfer that references the owner of the facility . In the final statement, the $username alias is compared to the username of the owner using a nested condition . If all of the checks pass, the current user is assumed to be the owner of the facility .

Now we just need to get the rules to compile. DRL files are just like Java source files in that they can have a package declaration and import classes.

 

Warning

Be sure to import any class that’s referenced by a rule definition. Otherwise, the rules won’t compile and therefore won’t be fired (and no error will be displayed in the UI). You can get instant validation using the Drools plug-in for Eclipse or by writing unit tests. This step prevents security holes caused by syntax errors.

 

The complete DRL file for the example in this section is shown in listing 11.4.

Listing 11.4. Security rule definitions for securing access to Facility entities
  package org.open18.permissions;

  import java.security.Principal;
  import org.jboss.seam.security.PermissionCheck;
  import org.jboss.seam.security.Role;
  import org.open18.model.Facility;
  import org.open18.model.Golfer;

  rule ModifyPrivateFacility
    no-loop
  when
    $perm: PermissionCheck(name == "facilityHome", action in ("update", "remove"), granted == false)
      Role(name == "admin")
      Facility(type == "PRIVATE")
    then
      $perm.grant();
    end

    rule ModifyNonPrivateFacility
      no-loop
    when
      $perm: PermissionCheck(name == "facilityHome", action in ("update", "remove"), granted == false)
      Role(name == "member")
      not Facility(type == "PRIVATE")
    then
      $perm.grant();
    end

    rule OperateOnOwnFacility
      no-loop
    when
      $perm: PermissionCheck(name == "facilityHome", granted == false)
      Role(name == "member")
      Principal($username: name)
      Facility($golfer: owner)
      Golfer(username == $username) from $golfer
    then
      $perm.grant();
    end

If your rules file becomes too large, you can break it up into multiple files. You register the additional files to the <drools:rule-base> component declaration.

Using rule-based authorization in Seam is simple because you don’t have to concern yourself with managing the RuleBase or the working memory. You just author the rules file, plug it into the securityRules component, and the laser beams light up the room. For more examples of security rules, check out the seamspace example in the Seam distribution.

With your newfound ability to write and understand rules, let’s return to the restrict clauses and learn how to cut out some of the work.

11.4.4. Automatic context detection

One of the reasons that Seam’s rule-based security is so powerful is that, through the use of interceptors, Seam is able to automatically detect the context at the spot of the permission check. Earlier, you defined the restriction using an explicit call to either s:hasRole or s:hasPermission. However, all this manual work takes away the benefit of using declarative security. Seam allows you to use these two declarations in their no-arguments form and creates and evaluates an implicit s:hasPermission check.

When the <restrict> tag or @Restrict annotation is used without a value, a default permission is created. The format of the permission is always name:action. When used with the @Restrict annotation, the name part is the component’s name and the action part is the name of the method. When dealing with page-based security, the name is the view ID and the action is the page-oriented JSF life cycle phase—either render or restore. These mappings are shown in table 11.5.

Table 11.5. The mapping defining how a rule-based restriction is interpreted

Source

Applies to

Name part

Action part

<restrict> JSF view IDs View ID JSF life cycle phase (restore or render)
@Restrict Methods Component name Method name

The downside of using the restrict declarations without arguments is that they don’t allow you to put a context variable into the working memory. As you saw in the example rules, knowing which instance of Facility is in scope is critical for deciding whether to let the user perform the action. But don’t be discouraged. There is a way to have this information passed on. To have that happen, you need to use entity restrictions.

Securing entities

When you want to restrict CRUD operations, you can generalize the restriction to securing the entity that’s being persisted. Seam leverages the entity life-cycle annotations to apply rule-based security at the spot of the operation. If the @Restrict annotation is added at the class level, the restriction will be enforced on each CRUD operation. If you want to secure an individual operation, you can use it alongside one of the entity life-cycle annotations.

The first argument to s:hasPermission is always the context variable name of the entity class, if it has been assigned one, or the fully qualified class name. The name of the action corresponds to the CRUD operation being performed. The names of the actions that Seam uses as the second argument to the s:hasPermission call are mapped to the entity life-cycle annotations in table 11.6.

Table 11.6. The mapping between the entity life-cycle event and the permission action

Entity life cycle annotation

Action

When applied

@PostLoad read After the entity instance is loaded from the database
@PrePersist insert Before a transient instance is persisted to the database
@PreUpdate update Before a dirty instance is flushed to the database
@PreRemove delete Before a managed instance is deleted from the database

The final argument is always the current entity instance that’s the subject of the CRUD operation. What that means is that the entity instance will be available in the working memory when the rule is fired.

Let’s assume that we only want to allow users to update or delete their own rounds. The first step is to secure the update and remove operations of the Round entity class:

  @Entity
  @Table(name ="ROUND")
  @Name("round")
  public class Round implements Serializable {
      ...

      @PreUpdate @PreRemove
      @Restrict
      public void restrict() {}
  }

The name of the method on which the @PreUpdate, @PreRemove, and @Restrict annotations are applied is arbitrary. It’s a means to an end. The next step is to put a rule in place in the security.drl file:

  rule ModifyOwnRound
      no-loop
  when
      $check: PermissionCheck(name == "round", action in ("update", "delete"), granted == false)
      Role(name == "member")
      Principal($username: name)
      Round($golfer: golfer)
      Golfer(username == $username) from $golfer
  then
      $check.grant();
  end

That’s all there is to enforcing the restriction! One last bit of configuration remains: the security framework must be hooked into the life cycle of the entity.

Registering the security listener

Like many other cross-cutting features provided by Seam, entity-level security is applied using a life-cycle listener. However, Seam doesn’t have the same level of control over entities that it does over the other components in its container. The entities call the persistence manager their master. To allow Seam to tap into the life-cycle events of JPA entities, it’s necessary to register Seam’s entity security listener with the persistence unit using the following META-INF/orm.xml descriptor:

  <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
      http://java.sun.com/xml/ns/persistence/orm
      http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
    version="1.0">
    <persistence-unit-metadata>
      <persistence-unit-defaults>
        <entity-listeners>
          <entity-listener
            class="org.jboss.seam.security.EntitySecurityListener"/>
        </entity-listeners>
      </persistence-unit-defaults>
    </persistence-unit-metadata>
  </entity-mappings>

If you’re using Hibernate natively, you don’t have to reach for your text editor. Seam automatically registers an equivalent listener with the Hibernate SessionFactory.

You can now add either role-based or rule-based restrictions to pages (view IDs), component methods, and entity operations. Your application is locked down. Or is it? What about those public-facing pages? They should get some attention as well, not so much from humans but from malicious computers. In the final section of this chapter, you’ll learn how to protect your public-facing pages from abuse.

11.5. Separating the computers from the humans

Most of the time when we talk about security, the focus is on keeping intruders out of the application. But public resources can also be abused. An evildoer could write a bot that uses a public registration form on your site to register random users, filling up your database with bogus records and potentially leading to denial of service for genuine users. How do you tell computers apart from humans to keep out the bots? That’s the purpose of CAPTCHA.

11.5.1. An overview of CAPTCHA

CAPTCHA stands for C ompletely A utomated P ublic T uring Test to Tell C omputers and H umans A part.[2] It is a challenge-response system that attempts to determine whether the user operating on the client side is a human or a computer. There are a variety of ways this test can be conducted, but the general idea is as follows. The application poses a challenge, typically rendered as an image. The user is then required to solve the challenge and send the response back to the server. Only when the response is correct is the remaining data in the form submitted. While the challenge only requires basic comprehension for the human to solve, even the most sophisticated computer algorithms get stopped dead in their tracks. Thus, a user who enters a correct response is assumed to be human.

2 CAPTCHA is a trademark of Carnegie Mellon University.

If you have never implemented CAPTCHA before, your first reaction might be to say, “Forget it. This seems too hard. Image generation? Eek!” On the contrary, implementing CAPTCHA using Seam is so easy, it’s just wrong not to use it.

11.5.2. Adding a CAPTCHA challenge to forms

Seam implements CAPTCHA by rendering a basic arithmetic problem using a Java 2D image. The complete interaction is managed by Seam, so you don’t have to do anything other than add it to your form. The image is served using the SeamResourceServlet, so be sure to have that installed. You can find information on how to set it up in Chapter 3, section 3.1.3.

Let’s return to the registration form example from Chapter 4 to protect it from being abused. All you need to do to secure your form using CAPTCHA is render the dynamically generated CAPTCHA image at /seam/resource/captcha and provide the user with a text field, bound to the #{captcha.response} property, in which to enter a response. The rest is up to Seam. Here’s an example of a CAPTCHA field:

  <s:decorate id="verifyCaptchaField" template="layout/edit.xhtml">
     <ui:define name="label">Security check</ui:define>
     <h:graphicImage value="/seam/resource/captcha"/>
     <h:inputText value="#{captcha.response}" required="true"/>
  </s:decorate>

Seam’s built-in component named captcha handles the logistics of the challenge. Validation of the response is enforced by the Hibernate Validator annotation @CaptchaResponse, which is defined on this component’s response property. The @CaptchaResponse validator uses the following message when the response is incorrect:

  input characters did not match

Believe it or not, that’s all there is to it! No XML configuration files, custom components, or custom servlets. You simply incorporate the captcha component into your JSF form. If you want to customize the challenge, create a custom component named captcha that extends org.jboss.seam.captcha.Captcha. Seam can still assume the responsibility of proctoring the challenge. Consult the Seam reference documentation for details.

CAPTCHA security helps to keep the abusers away today, but in general, the fight against wrongdoers is a perpetual battle. But, at the time of this writing, CAPTCHA is still holding the lines, so it’s worth adding to all of your public-facing forms—it provides the protection you need from bogus data and denial-of-service attacks.

11.6. Summary

The more accessible the security API is, the more likely you will be to use it—that’s Seam’s stance. Security is critical to an application’s success, and implementing security in Seam is easy. There’s no prize for putting a single restriction in place that covers the security requirements for the entire application. You should put security in the view, in your components, and in your entities. That way, when the front line comes down, the next line can pick up the swords and maintain the guard.

I used the word “easy” quite liberally in this chapter, but each time I backed it up with proof. You started off by adding authentication to the application with little more than a single method on a POJO. That method validates the credentials provided by the identity component. Seam and JAAS work together to handle the low-level details of establishing the user’s identity. You learned that the identity component can accept roles that are transferred to the user’s identity. This gave you two forms of authorization, binary and role-based, which brought you a long way toward locking down the application. Where role-based authorization falls short is in enforcing contextual restrictions. That led you to Seam’s rule-based authorization powered by Drools. I provided a crash course in Drools, then showed you how to create rules to make permission decisions based on facts that Seam places in the working memory. Finally, you learned that securing public-facing forms with CAPTCHA is just as important as protecting internal ones and implemented a CAPTCHA challenge in Seam in just one step.

With security in place, it’s safe to move on to Ajax and JavaScript remoting, both of which are exciting parts of the Web 2.0 movement and benefit from security as a prerequisite. In the next chapter, you’ll see the “easy” theme extend to Ajax, giving your application a rich upgrade without the effort typically associated with this modern approach to web interactions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.183.210