< Day Day Up > |
EJB technology defines a set of processes, starting from the application development and following through the administration of the deployed code. The EJB specification describes a set of contracts to enable the development and deployment of software components and also outlines processes that span from software development to deployment. EJB roles take on specific responsibilities at the various stages in the development/deployment/administration process. These roles include responsibility for defining and managing transactional characteristics of each of the components, as well as security. The defined EJB roles with security considerations discussed in this chapter are
Each of these EJB roles is described with respect to its responsibilities in the definition and management of security. EJB security is intended to be managed by the EJB container and driven by declarative security policy rather than security policy being hard-coded within the application code. Much of this declarative policy information is encoded within the EJB deployment descriptor. Removing security from the application code greatly reduces the burden on application developers, allowing them to concentrate on modeling business processes. Specifically, most application developers may not be familiar with the intricacies of security and the coding practices that will ensure secure deployed applications. Additionally, application developers are usually unaware of the details of the security environment into which their code is to be deployed. Application code containing security policy decisions for authentication, authorization, secrecy , and integrity usually does not quickly adapt to changes in enterprise security requirements. Externalizing security policy from the EJB code provides greater opportunity for deployment flexibility, code reuse, and portability between container implementations . EJB delegates security issues to those EJB roles having greater familiarity with the security features of the EJB container and deployment environment. In practice, the effective security policy is defined by the Deployer and the System Administrator, and the EJB container is responsible for enforcement of the policy. Every enterprise bean goes through the process shown in Figure 5.2.
Figure 5.2. Process of EJB Development to Deployment
5.2.1 Enterprise Bean ProviderThe Enterprise Bean Provider is the application developer who is an expert in writing code that embodies enterprise rules. As we saw in Section 3.2.1 on page 58, enterprise beans fall into three broad categories:
For entity beans, persistence managed by code in the enterprise bean is called bean-managed persistence (BMP). Persistence managed by the EJB container is called container managed persistence (CMP). 5.2.1.1 Communicating with an Enterprise BeanAll communication between a remote client and an enterprise bean is mediated by RMI-IIOP, as shown in Figure 5.1 on page 159. If the client and the enterprise bean are colocated in the same container, the request is mediated by the container itself. This architecture allows for
Figure 5.3 shows the two types of interfaces that each enterprise bean defines as part of the contract between the container and clients .
Figure 5.3. EJB Interfaces
If the client and the enterprise bean reside in different containers, the enterprise bean must implement the methods exposed in the home and remote interfaces. When the client and the enterprise bean reside in the same container, it is still acceptable, but not necessary, for the enterprise bean to implement the home and remote interfaces. Prior to EJB V2.0, there were no local home or local interfaces. According to the RMI-IIOP specification, all RMI-IIOP method invocations must follow call- by-value (CBV) semantics. When a method is called by value , the method sees a copy of any primitives passed to it. Therefore, any changes the method makes to those values have no effect on the caller's variables . This requirement also applies to object references passed as parameters. The callee cannot change the caller's referenced fields and objects. For clients and enterprise beans colocated in the same container and when the parameter and return values contain large data structures, CBV marshaling/demarshaling is a significant performance overhead. To overcome this performance bottleneck, EJB V2.0compliant containers now allow enterprise beans to define local home and local interfaces that the container will use to perform call-by-reference (CBR) rather than CBV to bypass marshaling/demarshaling of parameters and return values. When a method is called by reference , the callee sees the caller's original fields and objects passed as parameters, not copies. References to the callee's objects are treated the same way. Thus, any changes the callee makes to the caller's fields and objects affect the caller's original fields and objects. Regardless of whether an enterprise bean is called via a remote, home, local, or local home interface, the container still performs all required security operations, including authorization, as described in Section 5.4 on page 184. 5.2.1.2 ScenarioListings 5.1, 5.2, and 5.3 show the code fragments for a simple entity bean.
CMP is assumed, to simplify the example. Listing 5.1 shows a simplified example of a home interface. Listing 5.1. TravelerCreditCardHome.javaimport javax.ejb.EJBHome; import javax.ejb.CreateException; import javax.ejb.FinderException; import javax.ejb.RemoveException; import java.rmi.RemoteException; public interface TravelerCreditCardHome extends EJBHome { public TravelerCreditCard create (TravelerId travelerId, String cardNumber) throws CreateException, RemoteException; public TravelerCreditCard findByPrimaryKey (TravelerId travelerId) throws FinderException, RemoteException; public void remove(TravelerId travelerId) throws RemoveException, RemoteException; } This interface exhibits three methods.
In all cases, if an error occurs during the method invocation on the EJBHome object, specific Exceptions are thrown and must be caught by the client. Also note that the primary key class, TravelerId , must be serializable. Listing 5.2 gives a simplified example of a remote interface. Listing 5.2. TravelerCreditCardInterface.javaimport javax.ejb.EJBObject; import javax.ejb.EJBException; import java.rmi.RemoteException; public interface TravelerCreditCardInterface extends EJBObject { public void debit(double amount) throws EJBException; public void credit(double amount) throws EJBException; public double balance() throw EJBException; } This interface exhibits three methods.
In all cases, if an error occurs during the method invocation on the enterprise bean's business methods, an EJBException is thrown and must be caught by the client. Listing 5.3 shows a simplified example of an entity bean. Listing 5.3. TravelerCreditCard.javaimport javax.ejb.EntityBean; public class TravelerCreditCard extends javax.ejb.EntityBean { public double balance; // value managed by the container public void debit(double amount) throws EJBException; { // Implementation code goes here... } public void credit(double amount) throws EJBException; { // Implementation code goes here... } public double balance() throws EJBException; { // Implementation code goes here... } // Other code goes here... } This entity bean exhibits the three implemented business methods as described by the remote interface (see Listing 5.2). As described on page 76, security in J2EE is largely declarative, defined by the Deployer and the System Administrator, and enforced by the container rather than being encoded in the application. Leaving security out of the application enhances the application's portability. Conversely, portablity of an enterprise bean between container implementations or to newer versions of the container from the same vendor is diminished when the enterprise bean contains code that embodies security policy. The EJB model has no API to influence the security context and the identity under which an enterprise bean method is executed. [1] Aside from adding optional comments into the EJB deployment descriptor, the Enterprise Bean Provider has no other obligations with respect to security.
To call an enterprise bean, a JNDI call locates the target enterprise bean and its stub, as shown in Listing 5.4. The presence of the stub allows RMI calls. It is the responsibility of the Deployer and the System Administrator to determine whether the principal executing the code is authorized to get the stub to invoke the EJB methods. The EJB container is responsible for authenticating the principal making the RMI request and authorizing the principal to invoke the requested method. Listing 5.4. Code Fragment Performing the JNDI Lookupimport javax.ejb.EJBException; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.rmi.PortableRemoteObject; import java.rmi.RemoteException; // Other code goes here... InitialContext initialContext = null; try { initialContext = new InitialContext(); } catch (NamingException ne) { throw new EJBException ("Could not create an InitialContext", ne); } // Other code goes here... try { Object iclu = initialContext.lookup ("java:comp/env/ejb/TravelerCreditCard", TravelerCreditCardHome.class); TravelerCreditCardHome tcc = (TravelerCreditCardHome) javax.rmi.PortableRemoteObject.narrow(iclu); } catch (NamingException ne) { throw new EJBException ("Attempting to get TravelerCreditCardHome", ne); } catch (RemoteException re) { throw new EJBException ("Attempting to get TravelerCreditCardHome", re); } The code fragment in Listing 5.4 shows the use of JNDI and RMI to locate and obtain the stub code for TravelerCreditCardHome . Note that each Exception is handled for unexpected errors. This makes the code more robust in the presence of network and other failures. 5.2.1.3 Security APIs for Enterprise BeansBecause the Enterprise Bean Provider has no security obligations, gaps do exist in the EJB security model. One of these gaps is the occasional need for instance-level or application-specific authorization. As is described later in this chapter, method-level authorization is provided. However, circumstances arise in which the application needs to apply additional security constraints or needs to obtain information about the security context under which the enterprise bean is executing. Two APIs for security are found in the javax.ejb.EJBContext interface: EJB methods isCallerInRole() and getCallerPrincipal() . We introduced the first security- related method, isCallerInRole() , in Chapter 3. This method returns a boolean indicating whether the caller is defined to be a member of an EJB security role. EJB security roles are discussed in Section 5.2.2.1 on page 177. Of the two security-related methods in the EJBContext , isCallerInRole() is the most likely to work across container implementations and yield consistent results. Listing 5.5 shows how to determine whether the caller is a member of the Supervisor security role. The debit() method may be called by a booking agent or a Supervisor. However, the enterprise bean wants to limit the agent's ability to place charges against the credit card. A call to isCallerInRole() will enable the method to determine whether a charge limit should apply. Listing 5.5. Code Fragment Showing Use of isCallerInRole()import javax.ejb.EntityBean; import javax.ejb.EntityContext; public class TravelerCreditCard extends EntityBean { EntityContext ejbContext; static double agentLimit = 10000.0; public void debit(double amount) throws AgentChargeLimitExceededException { // The customer credit card can only be charged up // to a limit by the booking agent. Higher charges // must be done by the Supervisor. if (amount > agentLimit && ! ejbContext.isCallerInRole("Supervisor")) throw new AgentChargeLimitExceededException(); // Other code goes here... } // Other code goes here... } Application-specific authorization uses the isCallerInRole() method in the EJBContext . This examples shows how application-specific information can be combined with security informationthe Supervisor security roleto constrain authorization to execute a method. The Supervisor role name must be specified in the EJB deployment descriptor. The isCallerInRole() method takes a single String argumentthe name of an EJB security role. Nominally, this is a problem. Each enterprise bean may use a different security role name to describe the same EJB security role. Or, two different enterprise beans may use the same security role name to describe two different security roles. The EJB container needs to know how to map the application-specified security role names to the runtime security roles. A String value used in an enterprise bean code to describe a role in the isCallerInRole() call is called a role-name . Part of the EJB deployment descriptor provides a mapping from role-name values to identifiers used by the EJB container. This mapping allows the Deployer and the EJB container to map nonidentical role names as may be found in different enterprise beans to a single security role name at deployment and runtime. The same mechanisms can be used to disambiguate the same role-name in different enterprise beans that use the same String value but are intended to have different runtime security roles. This mapping is described in Section 5.2.2.3 on page 179. A fragment of an EJB deployment descriptor is shown in Listing 5.6, which illustrates an example of a security-role-ref entry defining a security role-name and a description of the purpose of the security role. This description is particularly important when the Enterprise Bean Provider is not also the Application Assembler, because it lets the Application Assembler know the purpose of the security role. This enables the Application Assembler to correctly define the security role mappings between the enterprise beans that are being brought together to form an application. Listing 5.6. EJB Deployment Descriptor Fragment for Security role-name Definitions<enterprise-beans> ... <entity> <ejb-name>TravelerCreditCard</ejb-name> <ejb-class> com.travel.booking.TravelerCreditCard </ejb-class> ... <security-role-ref> <description> This security role should be assigned to employees who work in supervisory capacity. In particular, this role allows them to authorize large charges on customer credit cards. </description> <role-name>Supervisor</role-name> </security-role-ref> ... </entity> ... </enterprise-beans> In Listing 5.6, the enterprise bean is an entity bean called TravelerCreditCard whose class is specified. The security-role-ref element defines a security role's name and a description of the role's purpose as it is understood by the Enterprise Bean Provider. The role-name is a String value used as a parameter to isCallerInRole() . The description of the role's purpose is important communication between the Enterprise Bean Provider and the Application Assembler and, ultimately, the Deployer. Depending on how isCallerInRole() is used in the enterprise bean, poor communication of the intended use of the security role can have unintended consequences, including incorrect authorizations. The second security-related method, getCallerPrincipal() , was also introduced in Chapter 3. This method returns a java.security.Principal object representing the caller as authenticated and defined by the EJB container's security subsystem. Listing 5.7 shows an example of obtaining the caller Principal . This method is not guaranteed to provide consistent results across container implementations from different EJB Container Providers, or vendors . In addition, the result of calling getCallerPrincipal() can be deployment specific, even from the same EJB Container Provider, as it depends on the user registry against which the users of the container are authenticated. The EJB specification states that the Principal is the client's. However, the Deployer, System Administrator, and EJB container are all involved in defining credential mappings that affect the result of calling getCallerPrincipal() . For example, a client that authenticates using an X.509 certificate in its Web browser can be mapped to a Kerberos principal. The enterprise bean's call to getCallerPrincipal() will return the Kerberos Principal , not the X.509 Principal . In a large enterprise or for interenterprise method calls, the identity mappings may be more complex. For this reason, we discourage Enterprise Bean Providers from using getCallerPrincipal() for making authorization decisions. Finally, even if a run-as identity is specified in the EJB deployment descriptor, getCallerPrincipal() returns the client's Principal , not the Principal for the run-as identity. Listing 5.7. Code Fragment Showing Use of getCallerPrincipal()import javax.ejb.EntityBean; import javax.ejb.EntityContext; import javax.rmi.PortableRemoteObject; import javax.naming.Context; import javax.naming.InitialContext; import java.security.Principal; public class TravelerCreditCard extends EntityBean { EntityContext ejbContext; public void debit(double amount) throws AgentChargeLimitExceededException { Context initialContext = new InitialContext(); // Get home interface of the employee database. Object lookupResult = initialContext.lookup ("java:/comp/env/ejb/EmployeeRecord"); EmployeeRecordHome employeeRecordHome = (EmployeeRecordHome) PortableRemoteObject.narrow (lookupResult, EmployeeRecordHome.class); // Get the caller Principal and name. Principal callerPrincipal = ejbContext.getCallerPrincipal(); String callerName = callerPrincipal.getName(); // Not likely to be portable or work consistently // across all deployments. EmployeeRecord employeeRecord = employeeRecordHome. findByPrimaryKey(callerName); String businessRole = employeeRecord.getJobTitle(); // The customer credit card can only be charged up // to a limit by the booking agent. Higher charges // must be done by the supervisor. if (amount > agentLimit && ! businessRole.equals("Supervisor")) throw new AgentChargeLimitExceededException(); // Other code goes here... } // Other code goes here... } The code fragment in Listing 5.7 shows how to get the caller's Principal . The use of getCallerPrincipal() is not likely to be portable and useful for authorization purposes in a container- and deployment-independent manner. Finally, if you should choose to use getCallerPrincipal() or isCallerInRole() , the client security-context information is not available within all EJB methods. In particular, calling getCallerPrincipal() or isCallerInRole() is not supported if the following method calls are performed from within:
Attempts to use isCallerInRole() or getCallerPrincipal() from EJB methods that do not support these calls should result in the throwing of a java.lang.IllegalStateException . 5.2.1.4 EJB Runtime RestrictionsJ2SE is a rich programming environment, including extensive support for networking, file I/O, and multithreading. However, in the context of high-performance transaction processing, many of the J2SE features conflict with the transaction-processing objectives. Therefore, a number of programming restrictions must be followed [2] in order to maintain portability of enterprise beans between containers and to preserve security. Some of these restrictions can be enforced by the EJB Container Provider; others are solely the responsibility of the Enterprise Bean Provider.
The EJB container can enforce some of the preceding programming model restrictions through the use of a J2SE security policy configuration. Such a policy configuration should unconditionally deny java.security.AllPermission , java.awt.AWTPermission , java.io.FilePermission , java.net.NetPermission , java.lang.reflect.ReflectPermission , java.security.SecurityPermission , and java.io.SerializablePermission and grant all code java.util.PropertyPermission "read", "*" , java.lang.RuntimePermission "queuePrintJob" , and java.net.SocketPermission "connect", "*" . These authorizations allow enterprise beans to be portable. However, some containers may allow the Deployer to grant some of the denied permissions. 5.2.2 Application AssemblerLike the Enterprise Bean Provider, the Application Assembler is an expert in modeling business models. Whereas the Enterprise Bean Provider is focused on implementing the business objects and rules, the Application Assembler is combining these business objects and rules into complete applications. The Application Assembler must have a broader perspective, being knowledgable in transaction processing and security issues as they relate to the applications being assembled. This requires having at least some knowledge of the intended target environments into which the application will ultimately be installed. The Application Assembler's responsibility is to assemble software components into an entire application that models an aspect of an enterprise. Once the Application Assembler has completed the assembly process, a deployment descriptor contains the information needed by a Deployer to install the application in one or more containers. Part of the process is creating a security view of the application. Creation of this view makes it easier to deploy the application, as fewer details of the application need to be known by the Deployer. For security, the Application Assembler
The Application Assembler takes one or more enterprise beans from Enterprise Bean Providers and constructs a logical view of authorization requirements for the application. This logical view is provided as guidance to the Deployer and, subsequently, the System Administrator. 5.2.2.1 EJB Security RolesThe deployment descriptor contains a set of security roles whose scope is an EJB JAR file. The definition of a security role is provided in a security-role deployment descriptor element as part of the assembly-descriptor element and includes a description subelement and a role-name subelement. An example of an assembly-descriptor element with two security-role subelements is shown in Listing 5.9. Listing 5.9. Example of an XML assembly-descriptor Element in a Deployment Descriptor<assembly-descriptor> ... <security-role> <description> This role is for the travel agency office manager or supervisor who has a master-key authority to perform any of the transactions allowed by any of the travel agents. In addition, this role is allowed to perform a number of financial transactions otherwise not allowed the travel agents. </description> <role-name>Manager</role-name> </security-role> <security-role> <description> This role is for each of the travel agents who book reservations and perform most of the financial transactions associated with the reservations. </description> <role-name>Agent</role-name> </security-role> ... <assembly-descriptor> This deployment descriptor fragment shows the definition of two security roles named Manager and Agent. The security roles scope to the applications in the EJB JAR file to which the deployment descriptor belongs. These security role names will be used in defining method authorizations. 5.2.2.2 EJB Method AuthorizationsNow that the security roles are defined, it is possible to specify the authorization requirements for each of the methods of the enterprise beans in the JAR file. In particular, the EJBHome , EJBObject , EJBLocalHome , and EJBLocalObject methods of the session and entity beans need to have their authorization requirements defined in the deployment descriptor. To invoke one of those EJB methods, the calling principal must be a member of one of the security roles defined in the deployment descriptor that is authorized for the requested method. In the deployment descriptor, each method-permission element contains one or more security roles and EJB methods. This element defines the list of methods that can be accessed by a user granted one or more of the associated security roles. Security roles and methods may appear in more than one method-permission element in the deployment descriptor. The deployment descriptor fragments of Listing 5.10, 5.11, and 5.12 demonstrate how to specify EJB methods in the deployment descriptor. Basically, there are three ways to specify EJB methods.
Listing 5.12. How to Include a Specific Method of an Enterprise Bean<method> <ejb-name>TravelerCreditCard</ejb-name> <method-name>debit</method-name> <method-params> <method-param>double</method-param> </method-params> </method> The deployment descriptor fragment of Listing 5.13 shows that a principal in the role of an Agent is authorized to debit a customer's credit card. Also, a Manager is authorized to perform any operation on methods in the TravelerCreditCard bean. Listing 5.13. Deployment Descriptor Fragment Showing how to Assign Security Roles to Methods<method-permission> <role-name>Agent</role-name> <method> <ejb-name>TravelerCreditCard</ejb-name> <method-name>debit</method-name> </method> </method-permission> <method-permission> <role-name>Manager</role-name> <method> <ejb-name>TravelerCreditCard</ejb-name> <method-name>*</method-name> </method> </method-permission> If the role-name were to be replaced by an unchecked element, the method(s) are authorized for every security role. Similarly, if role-name were replaced by an exclude-list element, no one would be authorized to call the method, even if there were other declarations containing roles for the same method. 5.2.2.3 Linking EJB Security Roles to Role ReferencesThe last step is to link the ejb-jar-scoped security roles, as defined by the Application Assembler, to the role references defined by the Enterprise Bean Provider. This requires mapping the security-role elements to the security-role-ref elements. This is done with the role-link element, as is shown in Listing 5.14. Listing 5.14. Tying a Security Role to a Security Role Reference<enterprise-beans> ... <entity> <ejb-name>TravelerCreditCard</ejb-name> <ejb-class> com.travel.booking.TravelerCreditCard </ejb-class> ... <security-role-ref> <description> This security role should be assigned to employees who work in supervisory capacity. In particular, this role allows them to authorize large charges on customer credit cards. </description> <role-name>Supervisor</role-name> <role-link>Manager</role-link> </security-role-ref> ... </entity> ... </enterprise-beans> This deployment descriptor fragment is the same as in Listing 5.6 on page 169, with the addition of the role-link element that ties the TravelerCreditCard security role Supervisor to the ejb-jar file's Manager security role. When called by a principal having the Manager role, the method isCallerInRole("Supervisor") returns true . It is the responsibility of the container to map the Manager security role to the role name Supervisor as appropriate during the execution of TravelerCreditCard. 5.2.2.4 EJB Principal DelegationSometimes, the Application Assembler knows that after a caller has been authenticated and authorized to execute a method, the method should be run using a specific principal, regardless of who the caller was. This is done by using the run-as element. The run-as identity applies to all methods in an enterprise bean and is used when making subsequent calls from the enterprise bean. The identity of the caller remains unaffected and is still used for authorization to access the enterprise bean's methods. The run-as identity is a logical security role name, as the Application Assembler does not know the deployment environment. The run-as identity refers to one of the security roles defined by the Application Assembler in the deployment descriptor. The Deployer, using the deployment tools, may be required to assign a principal to this security role at deployment time. Listing 5.15 shows a deployment descriptor fragment illustrating how to use the run-as element. Listing 5.15. Specifying the Identity for Method Execution Using the run-as Element<enterprise-beans> ... <entity> <ejb-name>TravelerCreditCard</ejb-name> ... <security-identity> <run-as> <role-name>Credit-Card-Agent</role-name> </run-as> </security-identity> ... </entity> ... </enterprise-beans> The role name specified in the role-name element indicates the security role that will be mapped at deployment time to the principal that will be used for execution of the methods in the enterprise bean. 5.2.3 DeployerTo run an enterprise bean, its ejb-jar file must be deployed into an operational environment. That environment includes the EJB container and the security platform in which the enterprise bean will be operating. To deploy the enterprise bean, the Deployer will be using tools supplied by the EJB Container Provider. These tools process the ejb-jar file's deployment descriptor, as prepared by the Application Assembler, and map the users and groups within the operational environment to the security roles described in the deployment descriptor. The steps in the deployment process are as follows .
The EJB Container Provider supplies tools for security management at deployment time, as well as for the System Administrator for ongoing security administration of the enterprise beans and container runtime environment. The tools may include support for mapping of principals, such as users and groups, between security realms when more than one security realm is involved in the deployment of the applications. The output of the deployment is container specific. 5.2.4 System AdministratorThe Deployer takes one or more assembled ejb-jar files from the Application Assemblers and deploys, or installs , them in one or more EJB containers. This process includes mapping identities from the client to EJB security roles and may include configuration management across multiple security domains. The System Administrator is responsible for overall management of security in the operational environment. In particular, the System Administrator is responsible for configuration of security domains and ongoing user- and group -identity management: defining users and groups, mapping them to the EJB security roles, and providing principal mapping across security domains within the enterprise or between enterprises , as appropriate. When an audit-trail capability is provided by the EJB container, the System Administrator is responsible for management of the facility. 5.2.5 EJB Container ProviderOnce an enterprise bean is deployed, it is the responsibility of the container to enforce the security policy defined by the Application Assembler and/or the Deployer. The J2EE specification does not prescribe how containers provide security. However, the container is responsible for principal authentication, access authorization for EJB method calls, resource manager access, and secure communication, including privacy and integrity, with remote clients. One of the primary responsibilities of the EJB container is the establishment of a security context so that it can be correctly propagated in a chain of EJB calls. In particular, this is necessary to enable the getCallerPrincipal() method in the EJBContext interface to get the correct result when there is a chain of calls between enterprise beans or calls to resource managers. If principal delegation is used to specify an identity under which an enterprise bean's method should execute, the container must support this identity and be able to propagate it for subsequent calls to enterprise beans and resource managers. Part of the EJB container's establishment of the security context includes support for the EJBContext isCallerInRole() and getCallerPrincipal() methods. For isCallerInRole() , the container must correctly map the role names, as defined by the Enterprise Bean Provider, to the role names defined by the Application Assembler and the Deployer. All the mappings are transparent to the Enterprise Bean Provider and the Application Assembler, thus making their jobs simpler and enabling greater portablity of applications between containers and deployment environments. |
< Day Day Up > |