Applying EJB Security with J2EE RI

Now you will add security constraints to the Agency case study. If you want to look at the finished results of following the steps described in the rest of this section, you can look at the agency.ear file in the examples/j2ee-ri directory for Day 15 on the accompanying Web site.

If you want to add security to the Agency case study as you have developed it so far, copy the agency.ear file from the Day06/agency/j2ee-ri directory to a working directory and open this copy using deploytool to follow the steps in the rest of this section.

The Agency application has two JAR files--one for the Session beans, and one for the Entity beans. Select the agency Jar file in the left pane and the Roles tab in the right pane. Use the Add button on the Roles page to define the three roles shown in Table 15.2. Figure 15.5 shows the deploytool screen after adding these roles.

Figure 15.5. Adding security roles.


Table 15.2. Agency Case Study Roles
RoleDescription
AdministratorAdministers the skills and locations tables
ApplicantRegisters details to apply for jobs
CustomerAdvertises job details

Roles are defined in the <assembly-descriptor> component inside the <ejb-jar> tag in the DD. A <security-role> tag with <role-name> and <description> elements defines each role.

The following deployment descriptor fragment shows the entry created for the three roles you have just defined:

<ejb-jar>
...
  <assembly-descriptor>
   <security-role>
     <role-name>Administrator</role-name>
   </security-role>
   <security-role>
     <role-name>Applicant</role-name>
   </security-role>
   <security-role>
     <role-name>Customer</role-name>
   </security-role>
...
  </assembly-descriptor>
</ejb-jar>

Defining the Security Identity

After the roles for a JAR file have been defined, you can restrict access to the methods of an EJB. In deploytool, select the AgencyBean EJB and then the Security tab. You will see the screen shown in Figure 15.6.

Figure 15.6. Security for EJB methods.


The Security Identity section at the top of this screen shows that authorization is controlled by the caller's identity. There are two options for security identity:

  • Use the caller's ID.

  • Use a defined role.

In Figure 15.6, Use Caller ID is selected to apply authorization based on the client's identity.

The <security-identity> tag in the deployment descriptor defines how access to an EJB is authorized. The tag is part of the <session> bean definition, and the <use-caller-identity> option is shown in the following extract:

<enterprise-beans>
  ...
  <session>
    <display-name>AgencyBean</display-name>
    <ejb-name>AgencyBean</ejb-name>
    <home>agency.AgencyHome</home>
    <remote>agency.Agency</remote>
    <ejb-class>agency.AgencyBean</ejb-class>
    <session-type>Stateless</session-type>
    ...
    <security-identity>
      <use-caller-identity/>
    </security-identity>
    ...
  </session>
  ...
</enterprise-beans>

The use of roles for the security identity is discussed in the “Using Roles as the Security Identity” section later in this chapter.

The lower part of the deploytool Security page is the Role References section, and this is discussed in the section “Applying Programmatic EJB Security.”

Defining Method Permissions

The Method Permissions section in the middle of the deploytool window shown in Figure 15.6 lists the methods for the interfaces defined for your bean. The radio button selects which interface is displayed. Each bean method is displayed as a row in the table; the columns of this table are the roles defined for the JAR file. By using the cells in this table, you can select which methods can be called by each role.

From Figure 15.6, you can see that the default access (the Availability column) to all methods is All Users which is why, so far, you have been able to access all your application functionality.

In the underlying deployment descriptor, the method permissions are added to the <assembly-descriptor> tag. The <method-permissions> tag associates one or more permissions with one or more methods. A permission is the name of a role specified with the <role-name> tag or the empty tag <unchecked/> to show that access is unchecked (it is callable by all clients). The methods associated with a particular <method-permissions> tag are defined by the <method> tag; this has three variants:

  1. Authorize all methods in an EJB using a tag of the following form:

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

    where the * means all methods.

  2. Authorize a named method in an EJB using a tag of the following form:

    <method>
      <ejb-name>EJBname</ejb-name>
      <method-name>MethodName</method-name>
    </method>
    
  3. Authorize a specific overloaded method in an EJB using a tag of the following form:

    <method>
      <ejb-name>EJBname</ejb-name>
      <method-name>MethodName</method-name>
      <method-params>
        <method-param>ParameterClass1</method-param>
        ...
        <method-param>ParmeterClassN</method-param>
      </method-params>
    </method>
    

TIP

Using the third form of the <method> tag is considered poor design, because there should be no need to differentiate between overloaded functions for security purposes. Overloaded forms of the same method should perform the same function and therefore require the same security permissions. If the security requirements are different, good design would imply using different method names.


The <method> tag also allows a <method-intf> (method interface) tag for defining the interface name if it is duplicated in the home and remote interfaces. The default deployment descriptor setting for applying method permission to the methods of an EJB is to tag them as <unchecked/> in a <method-permissions> element as follows:

<ejb-jar>
...
  <assembly-descriptor>
...
    <method-permission>
      <unchecked/>
      <method>
        <ejb-name>agency</ejb-name>
        <method-name>*</method-name>
      </method>
    <method-permission>
...
  </assembly-descriptor>
</ejb-jar>

Returning to the deploytool screen shown in Figure 15.6, you can see that the default access for all methods (All Users) maps onto the <unchecked/> tag in the deployment descriptor. You can examine the deployment descriptor using the Tools, Descriptor Viewer, Descriptor Viewer menu option.

Applying security is now just a matter of deciding which roles can call which methods for every EJB in your application. In the agency bean, you can set the access permissions as shown in Table 15.3.

Table 15.3. Agency EJB Authorization
MethodAvailability and Roles
removeLocationAdministrator
updateLocationAdministrator
deleteCustomerAdministrator, Customer
getPrimaryKeyAll Users
getEJBHomeAll Users
getCustomersAll Users
getLocationDescriptionAll Users
getApllicantsAll Users
removeSkillAdministrator
getSkillsAll Users
getAgencyNameAll Users
getHandleAll Users
SelectAdministrator
addLocationAdministrator
RemoveAll Users
updateSkillAdministrator
isIdenticalAll Users
addSkillAdministrator
getSkillDescriptionAll Users
createCustomerAdministrator, Customer
getLocationsAll Users
deleteApplicantAdministrator, Applicant
createApplicantAdministrator, Applicant

TIP

As a general guideline only apply security authorization to business methods; leave all the lifecycle methods accessible to all users. The only proviso to this is if you apply authorization to an Entity bean you will want to restrict access to the ejbCreate() and ejbRemove() methods as these are effectively the business methods for creating and deleting entities.


When you have added the permissions, the security page in deploytool will look similar to that shown in Figure 15.7.

Figure 15.7. Security DD for agency EJB.


If you haven't already done so, save your agency.ear file at this point.

In this example, authorization is only required for the methods in the remote interface. Some of the methods (such as getSkills()) can be accessed by all users, so the home interface methods (such as create()) must also be available to all users. Consequently, the default authorizations are appropriate for the home interface.

In the deployment descriptor you will have added entries for each role defining the methods accessible to that role. The following extract shows how the createApplicant() method is authorized for the Administrator and Applicant roles:

<ejb-jar>
...
  <assembly-descriptor>
...
    <method-permission>
     <role-name>Administrator</role-name>
     <method>
       <ejb-name>agency</ejb-name>
      <method-name>createApplicant</method-name>
     </method>
...
    </method-permission>
    <method-permission>
     <role-name>Applicant</role-name>
     <method>
       <ejb-name>agency</ejb-name>
      <method-name>createApplicant</method-name>
     </method>
...
    </method-permission>
...
  </assembly-descriptor>
</ejb-jar>

The remaining three EJBs in the Agency application must also have method permissions defined. These are much easier to specify.

The Advertise EJB and AdvertiseJob EJB must have all the home and remote interface methods accessible to the Administration and Customer roles only.

At the present time you will not restrict access to the Register bean; adding applicant security authorization is the subject of today's exercise.

Make sure you save these changes before completing this example.

After all the method permissions have been defined, the application is now ready for the deployer to map the principals in the target security domain onto the roles you have just defined.

Mapping J2EE RI Principals to Roles

A developer or assembler in the EJB development lifecycle undertakes the process of defining the roles and method permissions. The process of mapping principals to roles is very much a deployer function.

The mapping of roles to the target security domain is outside the scope of the J2EE specification and will vary from one J2EE server to another. The J2EE RI uses deploytool to map roles in the EJB JAR files onto the users and groups defined, or you can useasadmin or the Admin Console.

To map your EJB roles using deploytool, select the agency enterprise application (not the Agency JAR) in the left pane and the Security Role Mapping in the right pane. This is shown in Figure 15.8.

Figure 15.8. Mapping Roles to J2EE RI Users and Groups.


In Figure 15.8 you can see that deploytool has retrieved the role names from all the JAR files in the application. By selecting a role and clicking the Add User/Group to Role button, you can map each role onto one or more users or groups in the authentication domain.

The Agency case study requires the role mappings shown in Table 15.4.

Table 15.4. Case study Role Mappings
RoleUserGroup
Administratoragency 
Applicant applicant
Customer customer

Figure 15.9 shows the popup window with the list of J2EE RI defined users and groups ready to be added to the selected roles.

Figure 15.9. Mapping users and groups to selected roles.


TIP

If you do not get the popup list shown in Figure 15.9, your deploytool connection to the J2EE RI server has probably been timed out. Close down the popup window and select the localhost:4848 server in the left deploytool pane. This will reconnect deploytool to the server. Reselect the agency application and the Security Role Mapping screen and try again.

You might also come across a problem with deploytool not displaying the J2EE RI user or group names on the Security Role Mapping screen. Closing down and restarting deploytool and reconnecting to the J2EE RI server will usually fix this display problem.


Mapping the roles onto J2EE RI users and groups, as shown in Table 15.4, adds the following entry to the Sun-specific deployment descriptor sun-application.xml:

<sun-application>
...
  <security-role-mapping>
    <role-name> Administrator</role-name>
    <principal-name>agency</principal-name>
  </security-role-mapping>
  <security-role-mapping>
    <role-name>Customer</role-name>
   <group-name>customer</group-name>
  </security-role-mapping>
  <security-role-mapping>
    <role-name>Applicant</role-name>
    <group-name>applicant</group-name>
  </security-role-mapping>
</sun-application>

After defining the role mappings for the Agency case study, you can deploy your application.

Deploying and Testing the Secured EJBs

Deploy your application as normal with deploytool using the Tools, Deploy menu option. After deploying the application select the localhost:4848 server in left pane of deploytool, select the agency deployed object in the right pane and click on Client Jar… and save the requested application client to the Day15/examples/j2ee-ri directory.

Alternatively, save your deployool changes and from the Day15/examples directory run the command

asant deploy-j2ee-ri

You can test the changes to the application using the command

asant run-j2ee-ri

When you run the command-line client, you will be prompted to supply a username and password.

If you log in as agency, you will have unrestricted access to the application. If you log in as an applicant, such as romeo or juliet, you will only be able to use the applicant registration functionality. Similarly, customers such as winston and george will only be able to access job advertisement functionality.

To verify this, log in as romeo and check that access to the customer and administrative functionality is denied. Figure 15.10 shows the J2EE RI login screen with the username and password entered as romeo. Figure 15.11 shows the exception thrown if romeo attempts to use the unauthorized customer functionality.

Figure 15.10. J2EE RI login prompt.


Figure 15.11. J2EE RI exception thrown when attempting unauthorized EJB method call.


In a real-world application you would catch the security failure exception and display a user-friendly error message. A better design would be to update the client functionality to not present the user with the ability to access unauthorized functionality. To do this you need to understand how to use programmatic security to develop code where the user roles and principal name can be used to configure EJB and client functionality.

Before updating the application client to include programmatic security to make it aware of the underlying security constraints, you will complete your study of EJB authorization by looking at running an EJB as a specified user.

Using Roles as the Security Identity

An alternative to propagating the caller's security identity is to define a bean as using a specific role. This is achieved on an EJB's Security page in deploytool by selecting the Run As Role option and selecting the appropriate role from the list of roles displayed. The developer or assembler determines whether to use the caller ID or a specific role for a bean.

The <run-as> tag is used to define the role for beans that run with a specified role as follows:

<enterprise-beans>
  ...
  <session>
    <display-name>AgencyBean</display-name>
    <ejb-name>AgencyBean</ejb-name>
    <home>agency.AgencyHome</home>
    <remote>agency.Agency</remote>
    <ejb-class>agency.AgencyBean</ejb-class>
    <session-type>Stateless</session-type>
    ...
    <security-identity>
      <run-as>
        <role-name>Administrator</role-name>
      </run-as>
    </security-identity>
    ...
  </session>
  ...
</enterprise-beans>

Defining the bean to run using a specific role meets the developer's requirements, but the deployer must also map the role onto a principal in the target specific security domain for it to be effective. Defining a bean to run as a specific role is useful when the bean requires special permissions that the client cannot be guaranteed to provide. However, running an EJB in a specified role should be used carefully because it effectively negates the authorization process. Any client with access to the bean automatically gets the appropriate security permissions; therefore, it is imperative that only authorized clients use the bean.

NOTE

When using the <run-as> security identity, it is usual to define all bean methods as having <unchecked/> access allowing access to all roles. If checked access is applied to the methods, the <run-as> role must be defined as the <role-name> in the <method-permission> tag; otherwise, the method can never be called.


Applying Programmatic EJB Security

If simple declarative security constraints cannot express all of the security policy rules, the developer must resort to adding security into the EJB code.

The javax.ejb.EJBContext interface defines two methods for supporting programmatic security:

  • java.security.Principal getCallerPrincipal() returns an object defining the principal calling the method. The Principal class defines a getName() method that returns the name of the principal. The getCallerPrincipal() method never returns null.

  • boolean isCallerInRole(String roleName) returns true if the caller of the method is in the role passed as a parameter.

These two methods allow the developer to use the client's security identity to enable or disable EJB functionality.

Of the two methods, isCallerInRole() is considered portable because the developer doesn't work with actual role names but defines role references (much like EJB references discussed on Day 5, “Session EJBs”). The developer defines the role references used in the code, and the deployer (or assembler) maps this reference onto a real role.

The getCallerPrincipal() method is considered non-portable because the principal name used is dependent on the authentication mechanism used by the target J2EE server. In practice, as long as principal names are not defined as string literals in the Java code, the getCallerPrincipal() method can be used in a portable manner.

Using the Agency case study as an example, you will now augment your system to apply the following client specific constraints and changes in functionality:

  • Any client can register as a customer, but the customer name must be the same as the client's principal name.

  • Registered applicants or customers can only remove their own details from the system.

  • Registered applicants or customers can only access their own data in the system.

  • Administrators have unrestricted access to the system.

(You will further restrict applicant functionality as part of today's exercise.) Listing 15.1 shows the ejbCreate() method of the Advertise Session bean that has been modified to prevent clients from accessing data that does not match their principal name. Administrators (role reference admin) are permitted to access data for any client.

Listing 15.1. The ejbCreate() Method from agency.AdvertiseBean.java
public void ejbCreate (String login) throws CreateException {
  try {
    if (ctx.isCallerInRole("admin") ||
      ctx.getCallerPrincipal().getName().equals(login)) {
        customer = customerHome.findByPrimaryKey(login);
    }
    else
      throw new CreateException("Customer name does not match principal name");
  }
  catch (FinderException ex) {
    error ("Cannot find applicant: "+login,ex);
  }
}

The revised ejbCreate() method checks if the caller is not in the admin role and rejects the operation if the customer login name does not match the caller's principal name.

The parameter to the isCallerInRole() method is a role reference, and this must be mapped onto a real role by the application deployer. Figure 15.12 shows the Advertise bean security page in deploytool with the role reference defined.

Figure 15.12. Defining a role reference.


In Figure 15.12, the coded role of admin is mapped onto the real role of Administrator.The role references for a bean are defined in the bean's entry in the deployment descriptor as follows:

<enterprise-beans>
  ...
  <session>
    <display-name>AdvertiseBean</display-name>
    <ejb-name>AdvertiseBean</ejb-name>
    ...
    <security-role-ref>
      <role-name>admin</role-name>
      <role-link>Administrator</role-link>
    </security-role-ref>
    ...
  </session>
  ...
</enterprise-beans>

In addition to the change to the Advertise EJB, the Agency Session bean should also be updated to ensure non-administrator clients can only create or delete customers with a login name equal to the client's principal name (see Listing 15.2).

Listing 15.2. The Create and Delete Customer Methods from agency.AgencyBean.java
public void createCustomer(String login, String name, String email)
    throws DuplicateException, CreateException{
    try {
        if (ctx.isCallerInRole("admin") ||
            ctx.getCallerPrincipal().getName().equals(login)) {
            CustomerLocal customer = customerHome.create(login,name,email);
        }
        else
            throw new IllegalArgumentException(
"Cannot create a customer with a different name to the principal name");
    }
    catch (CreateException e) {
        error("Error adding Customer "+login,e);
    }
}

public void deleteCustomer (String login) throws NotFoundException {
    try {
        if (ctx.isCallerInRole("admin") ||
            ctx.getCallerPrincipal().getName().equals(login)) {
            customerHome.remove(login);
        }
        else
            throw new IllegalArgumentException(
"Cannot delete a customer with a different name to the principal name");
    }
    catch (RemoveException e) {
        error("Error removing customer "+login,e);
    }
}

The code uses the java.lang.IllegalArgumentException to show the error when the customer login name does not match the principal name.

As with the agency.AdvertiseBean, you must also add the admin to administrator role ref for the Agency session EJB.

The ejbCreate() method in agency.AdvertiseJobBean should also be updated in a similar manner to the ejbCreate() method in agency.AdvertiseBean, and the admin to administrator role ref created for the AdvertiseJob session EJB.

The revised application can now be deployed and tested.

Deploying and Testing EJB Programmed Security

Use deploytool or the supplied asant build files (remembering to retrieve a new client JAR file). To test the changes, run the client application and log in as winston, select the Customer tab, enter george as the customer name and click the login button. You should now get an exception thrown by the ejbCreate() because the caller principal name does not match the login name supplied as a parameter.

At this point you should revisit the functionality of the client application to improve the user interface. Obvious changes would be

  • Suppress display of the customer details page for applicants.

  • Use the principal name to automate access to details for a registered customer.

  • Suppress display of the applicant details page for customers.

  • Use the principal name to automate access to details for a registered applicant.

  • Suppress display of the administration pages for non-administrators.

Rather than make these changes in the client application, you will, instead, look at adding security constraints to the Web interface where all these improvements can also be made.

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

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