EJB Security Mechanisms

EJB technology promotes the following development model: Bean Providers develop EJBs; Application Assemblers assemble EJBs into working applications; Deployers deploy these applications to specific J2EE App Servers; and Administrators look after day-to-day operations such as adding new users, assigning them appropriate privileges, monitoring system performance, and so on.

For this model to work, a player in one role must make security decisions in such a manner that these decisions don't become overly restrictive for subsequent operations by a player in a different role. For example, a bean provider must not assume the existence of a particular account management system, for the choice of a particular account management system is a deployment time decision. Similarly, a deployer must not assume a fixed set of users with privileges at the time of deployment, as the administrator is likely to add more users, remove existing users and change privileges.

EJB technology incorporates a security architecture that supports this paradigm. The elements of this architecture include:

  • Transport Security with SSL. EJB lookup and method invocation involve the exchange of code and data objects over network. This exchange can be secured by using SSL for transport. In fact, support of SSL for EJB method invocations is a requirement for J2EE1.3 compliance. As we see shortly, using SSL is quite straightforward with WebLogic Server.

  • Role-Based Security. Bean providers and application assemblers assign security privileges to roles and not to individual users. A bean provider or an application assembler can assume the existence of certain roles but need not know how to map users or user groups to these roles. The deployer does this mapping in a container-specific manner.

  • Declarative Security. An application assembler declaratively specifies, in the deployment descriptor, who (i.e., which roles) can access specific EJBs and specific methods within the EJB. This promotes separation of business logic and security policy and makes it possible to change policy without modifying the application code.

  • Programmatic Security. A bean provider can programmatically query whether the current user belongs to a particular role or not and allow certain operations based on this determination. This role needs to be listed in the deployment descriptor of the EJB. The name chosen by the bean provider for a role with certain privileges may not match the role name chosen by the application assembler and hence it should be possible to link the role name assigned by the bean provider to the one selected by the application assembler. Additionally, the bean provider can get the identity of the current user and use this identity information for logging and/or authorization decisions.

  • Protection Domain. In general, a client program running on behalf of a user needs to authenticate the user to the server by providing some secret credential so that the server accepts the identity of the user. However, if the client is trusted by the server to furnish authenticated identities, then both are said to belong to a protection domain. This is the case when an EJB invokes another EJB deployed within the same WebLogic Server. As we see later, it is possible to have a protection domain that spans multiple servers.

  • Propagated vs. Delegated Identity. An EJB method, invoked by an authenticated client, either local or remote, can run with the identity of the caller or an identity separate from caller's identity, as specified in the deployment descriptor. The former is known as identity propagation and the later as identity delegation. Identity delegation allows a potentially large number of Web users to be mapped to one or a small number of database users.

Let us explore these points further.

Transport Security with SSL

By default, WebLogic Server listens for normal connections on port 7001 and SSL connections on 7002. We have already seen that port 7001 accepts not only HTTP connections but also t3 and IIOP connections. The same is true for port 7002. It can accept HTTPS, t3s or IIOP over SSL.

With WebLogic Server, you don't need to worry about a server certificate for just trying out client-server interaction over SSL. A demo server certificate is used by default. For a production environment, if you are serious about certificate-based server authentication, you should replace this demo certificate with your own certificate.

Client-side configuration to use SSL depends on the system properties and the URL used to get JNDI ENC. The following command runs the client program of the last section so that it communicates over t3s protocol:

C:ch10ex1>%JAVA% -Dbea.home=%BEA_HOME% 
							-Dweblogic.security.SSL.trustedCAKeyStore=%WLS_HOME%libcacerts 
							-Dweblogic.security.SSL.ignoreHostnameVerification=true 
							-cp %CLASSPATH%;echo_client.jar;build 
							client.Client t3s://localhost:7002
						

Pay attention to the system properties specified for the JVM and the target server URL. The specific system properties are different from the ones that we used for Sun's J2SE v1.4 SDK, but their purpose is similar. Essentially, a bunch of environment variables need to be set so that the SSL handling code knows how to validate the server certificate and what to do with the name associated with the certificate. The above setting simply ignores the name specified in the certificate, but it is possible to write a custom handler to match the name with the hostname of the URL or do some other custom verification.

What about IIOP over SSL? This can be accomplished in a similar manner, but requires a few additional environment variables to be set as shown below:

C:ch10ex1>%JAVA% -Dbea.home=%BEA_HOME% 
							-Dweblogic.security.SSL.trustedCAKeyStore=%WLS_HOME%libcacerts 
							-Dweblogic.security.SSL.ignoreHostnameVerification=true 
							-Dorg.omg.CORBA.ORBClass=weblogic.corba.orb.ssl.ORB 
							-Dweblogic.SSL.ListenPorts=localhost:7001:7002 
							-cp %CLASSPATH%;echo_client.jar;build 
							client.Client iiop://localhost:7002
						

As we can see, switching from non-SSL to SSL communication is fairly straightforward and is mostly a matter of changing the environment. We presented the specifics only for WebLogic but other J2EE App Servers are likely to have similar mechanisms.

It is also possible to specify a client-side certificate and accomplish certificate-based authentication while establishing a connection with the WebLogic Server. The details can be found in the WebLogic Server documentation.

EJB Security Context and Programmatic Security

EJB specification has the following note regarding the use of programmatic API for enforcing security:

In general, security management should be enforced by the container in a manner that is transparent to the enterprise beans' business methods. The security API described in this section [programmatic access to caller's security context] should be used only in less frequent situations in which the enterprise bean business methods need to access the security context information.

The basic idea behind this note is to discourage mixing security rules enforcing code with the business code and to promote the use of security decisions through declarative means at the time of application assembly and deployment. However in practice, not all authorization decisions can be made declaratively. There are times when authorizations or security privileges of a user are best determined at runtime, perhaps by consulting a separate authorization module.

The EJB container makes available the caller's security context to the EJB in the form of a javax.ejb.EJBContext object. This Interface has the following methods to retrieve the security context:

  • java.security.Principal getCallerPrincipal(): This method allows the EJB methods to access the identity information associated with the caller. The class of the object returned implements interface Principal. Note that JAAS (Java Authentication and Authorization Service) also uses the same type to represent user and role identity.

  • boolean isCallerInRole(String roleName): This method allows the EJB to determine whether the caller is associated with the role specified in the argument or not.

Go back to the source code in Listing 10-1. Class EchoBean has method setSessionContext() that takes an argument of type javax.ejb.SessionContext, a sub-interface of EJBContext, and assigns it to member field ctx. This method is invoked by the container to pass the proper context to the bean. We have a print statement in the body of this method so that we know when this method is invoked.

Let us modify the echo() method of EchoBean.java of the example to access the bean's security context and print the results:

public String echo(String arg) {
  System.out.println("--- BEGIN EchoBean.echo("" +arg+ "") ---");
  PrintCallerInfo();
  System.out.println("--- END EchoBean.echo("" + arg + "") ---");

  return arg;
}
private void printCallerInfo(){
  java.security.Principal caller = ctx.getCallerPrincipal();
							boolean inRole = ctx.isCallerInRole("echouser");
  System.out.println("Caller Name: " + caller.getName());
  System.out.println("Caller in role "echouser"? " + inRole);
}

Running the client program against this modified Echo EJB produces twice the following output on the display window associated with the WebLogic Server:

EchoBean.setSessionContext called
EchoBean.ejbCreate called
--- BEGIN EchoBean.echo("Hello, World!!") ---
Caller Name: <anonymous>
							Caller in role "echouser"? false
--- END EchoBean.echo("Hello, World!!") ---
--- BEGIN EchoBean.echo("Hello, World!!") ---
Caller Name: <anonymous>
							Caller in role "echouser"? false
--- END EchoBean.echo("Hello, World!!") ---

You can see that the method setSessionContext() was called before the method ejbCreate() and only once, even though the client was run twice. Also look at the string "<anonymous>" returned by the getCallerPrincipal() method. This outcome is not surprising, for the client program did not provide any identification information.

Client Authentication

How does a Java client authenticate itself to an EJB Server? There are two different ways:

  • JNDI Authentication: The client program specifies the username and password during JNDI Environment Naming Context setup. BEA WebLogic Server also allows X.509 certificate and private key-based JNDI authentication.

  • JAAS Authentication: The client program uses JAAS to authenticate the user and runs the JNDI setup and EJB invocation code in the context associated with the logged-in user.

In both cases, the user must be a valid user of the WebLogic domain. A user can be added to, modified or removed from a WebLogic domain through the WebLogic console.

JNDI Authentication

A Java client establishes a connection with WebLogic Server by getting a JNDI InitialContext. As the following code fragment from the main() of the client program shows, this is done by specifying the fully qualified class name of JNDI provider and the URL of the WebLogic Server as the value of the properties Context.INITIAL_CONTEXT_FACTORY and Context.PROVIDER_URL:

Properties h = new Properties();
h.put(Context.INITIAL_CONTEXT_FACTORY,
							"weblogic.jndi.WLInitialContextFactory");
							h.put(Context.PROVIDER_URL, url);
Context ctx = new InitialContext(h);

To pass username and the password to the server, the client program specifies username and password as value of properties Context.SECURITY_PRINCIPAL and Context.SECURITY_CREDENTIALS. Assuming that username and password are available in string variables uname and passwd, the following code fragment would attempt JNDI based authentication of the client:

Properties h = new Properties();
h.put(Context.INITIAL_CONTEXT_FACTORY,
        "weblogic.jndi.WLInitialContextFactory");
h.put(Context.PROVIDER_URL, url);
h.put(Context.SECURITY_PRINCIPAL, uname);
							h.put(Context.SECURITY_CREDENTIALS, passwd);
Context ctx = new InitialContext(h);

A modified client that takes the username and password as a command line argument can be found under srcjsbookch10ex2 subdirectory of the JSTK installation. This directory also has the modified EchoBean.java that prints the name of the caller. Everything else, except for the bean's JNDI name, which is changed from ex1-echo-EchoHome to ex2-echo-EchoHome, remains the same.

A sample execution of the modified client is shown below, assuming that the modified bean is deployed to the WebLogic domain and the environment variables are properly set:

C:ch10ex2>%JAVA% -cp %CLASSPATH%;echo_client.jar;build client.Client 
							t3://localhost:7001 akriti akritipass
Exception in thread "main" javax.naming.AuthenticationException.  
Root exception is java.lang.SecurityException: User: akriti, failed 
to be authenticated.

Start server side stack trace:
java.lang.SecurityException: User: akriti, failed to be authenticated.
... additional lines skipped ...

This outcome is not surprising. After all, akriti is not a valid user within the current domain. To add user akriti to the WebLogic domain, point your browser to the WebLogic Server console URL http://localhost:7001/console and add a user with name akriti and password akritipass by selecting on SecurityRealmmyrealmsUsers and clicking on "Configure a new user ...". Once this user is created, rerun the client program by issuing the previous command. This time, the client execution should succeed and you should see the following message on the WebLogic command window:

							... previous messages skipped ...
--- BEGIN EchoBean.echo("Hello, World!!") ---
Caller Name: user1
Caller in role "echouser"? false
--- END EchoBean.echo("Hello, World!!") ---

How do we assign role echouser to user akriti? First thing to do is to declare the existence of echouser role in deployment descriptor file ejb-jar.xml as shown below:

<ejb-jar>
  <enterprise-beans>
... details skipped ...
  </enterprise-beans>

  <assembly-descriptor>
							<security-role>
							<role-name>echouser</role-name>
							</security-role>
							</assembly-descriptor>
  <ejb-client-jar>echo_client.jar</ejb-client-jar>
</ejb-jar>

Next we associate this role with user akriti in the container-dependent manner. For WebLogic, this is done by modifying WebLogic Server specific deployment descriptor file weblogic-ejb-jar.xml:

<weblogic-ejb-jar>
  <weblogic-enterprise-bean>
... details skipped ...
  </weblogic-enterprise-bean>

<security-role-assignment>
							<role-name>echouser</role-name>
							<principal-name>akriti</principal-name>
							</security-role-assignment>
</weblogic-ejb-jar>

We cover the details of EJB deployment descriptors later in this chapter. For the time being, just read it as self-explanatory structured text.

Recompile the EJB application and deploy it to the WebLogic Server. Run the client program as before. What does Echo bean report regarding the role of the caller?

This method of associating a username with a role suffers from a major limitation. For every new user added to the system, the deployment descriptor needs to be modified and the application needs to be rebuilt and redeployed. These steps can be bypassed if we create a group (say, echogroup) in the WebLogic domain and specify this group name as the value of principal-name element in weblogic-ejb-jar.xml file. Now every member of this group will be associated with echouser role. As the group membership can be determined at the time of user creation or even later without touching the application, this mechanism avoids the above-mentioned limitation.

JAAS Authentication

JAAS authentication has been covered in Chapter 5, Access Control. Here is a brief recap of the steps: The login function is assisted by login modules, configured within a configuration entry in a login configuration file. The Java program wanting to run a particular piece of code in security context of an authenticated user instantiates a javax.security.auth.login.LoginContext object, passing the configuration entry identifier and a javax.security.auth.callback.CallbackHandler object with callback functions to get the username and password. Invocation of login() method on the LoginContext object results in invocation of callback handler methods and login() method(s) of the configured login modules. On successful authentication, a LoginContext object, containing an initialized javax.security.auth.Subject object representing the logged-in user, is created. The piece of code to be run within the context of the authenticated user is placed in or called from the run() method of a class derived from PrivilegedAction or PrivilegedExceptionAction of javax.security package. The run() method of this class is executed in the context of a particular user by calling static method Subject.doAs() and passing the Subject instance and a concrete instance of the class with run() method as arguments.

The examples we discussed in Chapter 5 used a login module to validate the user and password based on either a keystore or a user account system. A user authenticated with such a login module in a client program will not be known to the WebLogic Server. WebLogic addresses this problem by requiring the login module to interact with the server for the authentication and validate the user based on a user database maintained by the server. One such login module, UsernamePasswordLoginModule, comes bundled with WebLogic software and can be used for password-based authentication. During the authentication process, the client JVM communicates with the server and the server keeps track of the client JVM and the fact that a particular user at this JVM has been authenticated.

Example ch10-ex2 includes client program Client2.java and other supporting classes and files for illustrating JAAS authentication using UsernamePasswordLoginModule. Let us look at the details of this program, starting with the login configuration file:

// File: srcjsbookch10ex2login.conf
Sample {
  weblogic.security.auth.login.UsernamePasswordLoginModule required
    debug=false;
};

The login configuration entry "Sample" specifies exactly one login module: UsernamePasswordLoginModule. This entry is referenced in the main client program, Client2.java, shown in Listing 10-6.

Listing 10-6. Client program for JAAS authentication
// File: srcjsbookch10ex2clientClient2.java
package client;

import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;

public class Client2 {
  public static void main(String[] args) throws Exception {
    String url = null;
    String uname = null;
    String passwd = null;

    if (args.length > 2){
    url = args[0]; uname = args[1]; passwd = args[2];
    } else {
      System.out.println("Usage:: java client.Client <url>
        <uname> <passwd>");
   return;
 }

    LoginContext loginContext = new LoginContext("Sample",
      new SampleCallbackHandler(uname, passwd, url));
    loginContext.login();
    Subject subject = loginContext.getSubject();
    SampleAction sampleAction = new SampleAction(url);
    Subject.doAs(subject, sampleAction);
  }
}

This program passes an instance of SampleCallbackHandler, a class derived from CallbackHandler, to the constructor of LoginContext. You may recall from our earlier discussion in the Access Control chapter that the handle() method of this class is invoked by the login module to get the username and password information. This method can either prompt the user for username and password or get these values through some other mechanism. Here you can see that an instance of SampleCallbackHandler is initialized with the username and the password obtained from the command line, by taking these values as constructor arguments. In fact, it also takes the server URL as a constructor argument. The login module gets the username and password through standard callback classes NameCallback and PasswordCallback and the server URL through WebLogic provided class weblogic.security.auth.callback.URLCallback. Thus the login module has all the information required to interact with WebLogic Server and authenticate the specified user.

It is possible to write additional login modules that interact with the WebLogic Server for user authentication. The details can be found in the WebLogic Server documentation.

A bare-bone version of SampleCallbackHandler code is shown below in Listing 10-7. Look at the SampleCallbackHandler.java file in client directory for complete listing of the code.

Listing 10-7. Callback Handler class for JAAS Authentication
// File: srcjsbookch10ex2clientSampleCallbackHandler.java
package client;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.NameCallback;
import weblogic.security.auth.callback.URLCallback;

class SampleCallbackHandler implements CallbackHandler {
  private String uname = null;
  private String passwd = null;
  private String url = null;

  public SampleCallbackHandler(String uname, String passwd, String url){
    this.uname = uname;
    this.passwd = passwd;
    this.url = url;
  }

  public void handle(Callback[] callbacks) throws
      java.io.IOException, UnsupportedCallbackException {
    for(int i = 0; i < callbacks.length; i++){
      if(callbacks[i] instanceof NameCallback){
        NameCallback nc = (NameCallback)callbacks[i];
        nc.setName(uname);
      } else if(callbacks[i] instanceof URLCallback){
        URLCallback uc = (URLCallback)callbacks[i];
        uc.setURL(url);
      } else if(callbacks[i] instanceof PasswordCallback){
        PasswordCallback pc = (PasswordCallback)callbacks[i];
        pc.setPassword(password.toCharArray());
      }
    } // for
  } // handle()
}

The code that runs within the context of logged-in user is in class SampleAction, shown in Listing 10-8. This is similar to the main client code that gets the JNDI InitialContext, locates the EchoHome stub, creates Echo stub from the EchoHome stub, and invokes the echo() method on this stub.

Listing 10-8. The client code for JAAS authentication example
// File: srcjsbookch10ex2clientSampleAction.java
package client;
import javax.rmi.PortableRemoteObject;
import java.util.Properties;
import echo.Echo;
import echo.EchoHome;

public class SampleAction implements java.security.PrivilegedAction {
  private String url;
  public SampleAction(String url){
    this.url = url;
  }

  public Object run(){
    Object obj = null;
    try {
      doit();
    } catch(Exception e) {
      e.printStackTrace();
    }
    return obj;
  }

  public void doit() throws javax.naming.NamingException,
       javax.ejb.CreateException, java.rmi.RemoteException,
       javax.ejb.RemoveException {
    java.util.Properties h = new java.util.Properties();
    h.put(Context.INITIAL_CONTEXT_FACTORY,
       "weblogic.jndi.WLInitialContextFactory");
    h.put(Context.PROVIDER_URL, url);

    javax.naming.Context ctx = new javax.naming.InitialContext(h);

     Object home = ctx.lookup("ex2-echo-EchoHome");
     EchoHome ehome = (EchoHome)narrow(home, EchoHome.class);
     Echo estub = (Echo)narrow(ehome.create(), Echo.class);

     String msg = "Hello, World!!";
     System.out.println("Calling Echo.echo("" + msg + "") ...");
     String resp = estub.echo(msg);
     System.out.println("... Echo.echo("" + msg + "") = " + resp);

    System.out.println("... Echo Client Executed successfully.");
  }
}

Compilation of this client is no different from Example ch10-ex1. Execution step requires that you set the system property java.security.auth.login.config to the pathname of the login configuration file. The following command illustrates this:

c:ch10ex2>%JAVA% -Djava.security.auth.login.config=login.conf 
							-cp %CLASSPATH%;echo_client.jar;build client.Client2 
							t3://localhost:7001 akriti akritipass
						

You are encouraged to compile and run this example. You may also try variations, such as using different usernames, roles, user groups, protocol, and so on. to get a better handle on how things work.

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

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