Authorization




Authorization

Once a user is authenticated by a vendor-specific mechanism, he must be checked to see if he is allowed to invoke a particular EJB method. Authorization is performed in Java EE and EJB by associating one or more roles with a given user and then assigning method permissions based on that role. While an example of a user might be "Scott" or "Gavin," roles are used to identify a group of usersfor instance, "administrator," "manager," or "employee." In EJB, you assign access control on a per-method basis. You do not assign these permissions on a per-user basis, but rather, on a per-role basis.

The roles used to describe authorization are considered logical roles because they do not directly reflect users, groups, or any other security identities in a specific operational environment. EJB security roles are mapped to real-world user groups and users when the bean is deployed. This mapping allows a bean to be portable; every time the bean is deployed in a new system, the roles can be mapped to the users and groups specific to that operational environment.

Unlike authentication, authorization is something that the EJB specification clearly defines. You begin by declaring the roles that are accessed programmatically in your code base. Then you assign permissions for each method in your class. This is done declaratively through Java annotations or through the ejb-jar.xml deployment descriptor. To illustrate, let's secure access to the ProcessPayment EJB defined in Chapter 11.

Assigning Method Permissions

Titan Cruises must be very careful when determining to whom it will grant access to the ProcessPayment EJB. This bean allows users to charge money against a customer's credit card, so it is in Titan's best interest to make sure its customers feel safe giving out their credit card numbers. Only users who are authorized travel agents will be allowed to process payments at Titan Cruises. Furthermore, only authorized agents with an automatic check fraud-detection system for their bank accounts will be allowed to make check payments. However, any valid user will be allowed to make cash payments.

To assign method permissions to an EJB's methods, use the @javax.annotation.security.RolesAllowed annotation:

package javax.annotation.security;

@Target({TYPE, METHOD}) @Retention(RUNTIME)
public @interface RolesAllowed {
   String[] value( );
}

This annotation defines one or more logical roles that are allowed to access the method. When placed on the bean class, the @RolesAllowed annotation specifies the default set of roles that are permitted to access bean methods. Each EJB method can override this behavior by using the same annotation.

The @javax.annotation.security.PermitAll annotation specifies that any authenticated user is permitted to invoke the method. As with @RolesAllowed, you can use this annotation on the bean class to define the default for the entire bean class, or you can use it on a per-method basis. @PermitAll is also the default value if no default or explicit security metadata is provided for a method. This means that if you do not use any security annotations on your bean class, every user is granted unlimited access.

Let's apply these annotations to ProcessPaymentBean using the permissions we discussed earlier:

package com.titan.processpayment;

import com.titan.domain.*;

import javax.ejb.*;
import javax.annotation.Resource;
import javax.annotation.security.*;

@Stateless
@RolesAllowed("AUTHORIZED_TRAVEL_AGENT")
public class ProcessPaymentBean implements ProcessPaymentRemote,
                                           ProcessPaymentLocal
{
...
@PermitAll
   public boolean byCash(Customer customer, double amount)
      throws PaymentException
   {
      ...
   }
@RolesAllowed("CHECK_FRAUD_ENABLED")
   public boolean byCheck(Customer customer, CheckDO check, double amount)
      throws PaymentException
   {
      ...
   }

   public boolean byCredit(Customer customer, CreditCardDO card,
                           double amount) throws PaymentException
   {
      ...
   }
   private boolean process(int customerID, double amount, String type,
                           String checkBarCode, int checkNumber, String creditNumber,
                           java.sql.Date creditExpDate) throws PaymentException
   {
      ...
   }
}

The AUTHORIZED_MERCHANT role identifies Titan users who are authorized to make payments on the system. The bean class is annotated with @RolesAllowed to specify that all methods in ProcessPaymentBean , by default, can be executed only by AUTHORIZED_MERCHANT users. The byCredit( ) method inherits this default setting. Titan Cruises will accept cash payments from anybody, so the byCash( ) method is annotated with @PermitAll to allow payments from any user. For the byCheck( ) method, we have an additional requirement that only merchants that have CHECK_FRAUD_ENABLED are allowed to process check payments. For this method, an additional @RolesAllowed annotation is used to override the default one applied on the bean class. When a client invokes on an EJB method for which it doesn't have the appropriate permissions, the EJB container throws a javax.ejb.EJBAccessException .

Let's apply this security metadata with XML instead:

<ejb-jar version="3.0">
   <assembly-descriptor>
      <security-role>
         <description>This role represents an authorized merchant</description>
         <role-name>AUTHORIZED_MERCHANT</role-name>
      </security-role>
      <security-role>
         <description>
          This role represents a merchant that has check fraud enabled
         </descripton>
         <role-name>CHECK_FRAUD_ENABLED</role-name>
      </security-role>
      <method-permission>
         <role-name>AUTHORIZED_MERCHANT</role-name>
         <method>
            <ejb-name>ProcessPaymentBean</ejb-name>
            <method-name>byCredit</method-name>
         </method>
      </method-permission>
      <method-permission>
          <role-name>CHECK_FRAUD_ENABLED</role-name>
          <method>
             <ejb-name>ProcessPaymentBean</ejb-name>
             <method-name>byCheck</method-name>
          </method>
      </method-permission>
      <method-permission>
         <unchecked/>
         <method>
             <ejb-name>ProcessPaymentBean</ejb-name>
             <method-name>byCheck</method-name>
         </method>
      </method-permission>
   </assembly-descriptor>
</ejb-jar>

Method permission declarations are defined within the <assembly-descriptor> element. Each role that is used to map method permissions must be identified first within <security-role> elements. These elements have an optional <description> element that describes the role's use. The <role-name> element declares the role that will be used in the deployment. It is unclear to me why the <security-role> element is required by the specification because the referenced roles could be determined by looking at all the <method-permission> elements. It seems like complete syntactic sugar to me.

The method permissions themselves are declared with multiple <method-permission> elements. Each <method-permission> element defines one or more <role-name> elements that declare the roles allowed to access a particular <method>. The <unchecked> element is equivalent to the @PermitAll annotation. The <method> element declares the method you want to secure. The <method-name> element is allowed to take a * wildcard. If a <method-permission> declaration can be applied to one or more bean methods, then the union is taken.

Identifying Specific Methods in XML

The <method> element is used by the <method-permission> element to identify a specific group of methods in a particular bean. The <method> element always contains an <ejb-name> element that specifies the bean's name and a <method-name> element that specifies the method. It may also include a <description> element, <method-params> elements specifying which method parameters will be used to resolve overloaded methods, and a <method-intf> element that specifies whether the method belongs to the bean's remote or local interface. This last element resolves the possibility that the same method name might be used in more than one interface.

Wildcard declarations

The method name in a <method> element can be a simple wildcard (*). A wildcard applies to all methods of the bean's home and remote interfaces. For example:

<method>
    <ejb-name>ProcessPaymentBean</ejb-name>
    <method-name>*</method-name>
</method>

Although it's tempting to combine the wildcard with other characters, don't do this. The value get*, for example, is illegal. The asterisk character can only be used by itself.

Named method declarations

Named declarations apply to all methods defined in the bean's remote and local interfaces that have the specified name. For example:

<method>
    <ejb-name>ProcessPaymentBean</ejb-name>
    <method-name>byCheck</method-name>
</method>

This declaration applies to all methods with the given name in any business interface. It does not distinguish between overloaded methods. For example, if the local interface for the ProcessPayment EJB is modified so that it has three overloaded byCheck( ) methods, the previous <method> declaration would apply to all methods, as illustrated here:

@Local
public interface ProcessPaymentLocal {
   boolean byCheck(Customer cust, CheckDO check, double amount);

   boolean byCheck(double[] amounts);

   boolean byCheck( );
}

Specific method declarations

Specific method declarations use the <method-params> element to pinpoint a method by listing its parameters, which allows you to differentiate between overloaded methods. The <method-params> element contains zero or more <method-param> elements that correspond, in order, to each parameter type (including multidimensional arrays) declared in the method. To specify a method with no arguments, use a <method-params> element with no <method-param> elements nested within it.

For example, let's look again at our ProcessPayment EJB, to which we have added some overloaded byCheck( ) methods. Here are three <method> elements, each of which unambiguously specifies one of the methods by listing its parameters:

<method>
    <ejb-name>ProcessPaymentBean</ejb-name>
    <method-name>byCheck</method-name>
    <method-params>
           <method-param>com.titan.domain.Customer</method-param>
           <method-param>com.titan.processpayment.CheckDO</method-param>
           <method-param>double</method-param>
    </method-params>
</method>
<method>
    <ejb-name>ProcessPaymentBean</ejb-name>
    <method-name>byCheck</method-name>
    <method-params></method-params>
</method>
<method>
    <ejb-name>ProcessPaymentBean</ejb-name>
    <method-name>byCheck</method-name>
    <method-params>double[]</method-params>
</method>

Remote/home/local differentiation

There's one problem left. The same method name can be used in the remote interface and the local interface. To resolve this ambiguity, add the <method-intf> element to a method declaration as a modifier. Five values are allowed for a <method-intf> element: Remote, Home, LocalHome, Local, and ServiceEndpoint.

All of these styles of method declarations can be used in any combination and within any element that uses the <method> element. The <method-permission> elements are combined to form a union of role-to-method permissions. For example, in the following code, the first <method-permission> element declares that only administrators have access to the remote methods of the ProcessPayment EJB:

<method-permission>
    <role-name>administrator</role-name>
    <method>
        <ejb-name>ProcessPaymentBean</ejb-name>
        <method-name>*</method-name>
        <method-intf>Remote</method_intf>
    </method>
</method-permission>

Excluded Methods

EJB security metadata has a rarely used feature that allows you to forbid access to one or more methods. Excluded methods are identified with the @javax.annotation.security.DenyAll annotation or with the <exclude-list> element. Denying access to a particular method with an annotation isn't a very useful proposition. Why would you write a bean class method, add it to the business interface, and then annotate it so that it couldn't be used? Using this feature in XML, though, does have some uses. What if the ProcessPayment EJB was part of a third-party vendor library? The <exclude-list> element could be used to turn off various features of the API per deployment. Let's look at an example:

<ejb-jar>
    <assembly-descriptor>
        <exclude-list>
            <method>
                <ejb-name>ProcessPaymentBean</ejb-name>
                <method-name>byCash</method-name>
            </method>
        </exclude-list>
    </assembly-descriptor>
</ejb-jar>

This example disallows all access to the ProcessPaymentBean.byCash( ) method. The <exclude-list> element can take one or more method signatures.