22 Configuring Java SE Applications to Use OPSS

The information in this chapter applies only to Java SE applications, and the audience are developers of Java SE applications. For similar information about Java EE applications, see Chapter 21, "Configuring Java EE Applications to Use OPSS."

This chapter includes in the following topics:

22.1 Using OPSS in Java SE Applications

In order to use OPSS in a Java SE application, the developer should aware that:

  • The file jps-manifest.jar must be in the class path.

  • The application must call JpsStartup.start() at initialization.

  • The following system properties must be set:

    • oracle.security.jps.config, the file path to the file jps-config-jse.xml.

    • common.components.home, the path to the oracle common directory.

    • java.security.policy, the file path to the file java.policy.

    For other (optional) system properties you can set, see Section F.1, "OPSS System Properties."

  • The application should use the OPSS class AppSecurityContext, as illustrated in the following sample snippet:

    String appID = AppSecurityContext.getApplicationID();
     
           try {
                setApplicationID(appName);
                        .....
            } finally {
                setApplicationID(appID );
            }
        }
    
        private static void setApplicationID(final String applicationID) {
                    AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    AppSecurityContext.setApplicationID(applicationID);
                    return null;
                }
            });
        } 
    

    The call AppSecurityContext.setApplicationID requires a codesource permission like the following:

    <grant>
        <grantee>
            <codesource>
              <url>myJavaSEapp/-</url>
            </codesource>
        </grantee>
        <permissions>
            <permission>
                <class>oracle.security.jps.JpsPermission</class>
                <name>AppSecurityContext.setApplicationID.myAppStripeID</name>
            </permission>
        </permissions>
    </grant>
    

For information about how to configure the OPSS security store, see Chapter 8, "Configuring the OPSS Security Store."

22.3 Authentication for Java SE Applications

This section explains the identity store support for Java SE applications, and it includes the following sections:

22.3.1 The Identity Store

Authentication is the mechanism by which callers prove that they are acting on behalf of specific users or system. Using data, such as name-password combinations, authentication answers the question Who are you? The term identity store refers to the storage where identity data is kept, and authentication providers are ways to access an identity store.

An application obtains information from an OPSS security store (identity, policy, or credential store) and manages its contents using the OPSS APIs, as illustrated in the following graphic:

flow of data from app to sec store

22.3.2 Configuring an LDAP Identity Store in Java SE Applications

A Java SE application can use an LDAP-based identity store configured in the file jps-config-jse.xml with the elements <serviceProvider>, <serviceInstance>, and <jpsContext>, as illustrated in the following snippet:

<serviceProviders>
  <serviceProvider type="IDENTITY_STORE" name="idstore.ldap.provider" class="oracle.security.jps.internal.idstore.ldap.LdapIdentityStoreProvider">
    <description>Prototype LDAP-based ID store</description>
  </serviceProvider>
</serviceProviders>

<serviceInstances>
 <serviceInstance name="idstore.ldap" provider="idstore.ldap.provider">
   <property name="idstore.type" value="OID"/>
   <property name="max.search.filter.length" value="500"/>
   <extendedProperty>
     <name>user.search.bases</name>
     <values>
       <value>cn=users,dc=us,dc=oracle,dc=com</value>
     </values>
   </extendedProperty>
   <extendedProperty>
     <name>group.search.bases</name>
     <values>
       <value>cn=groups,dc=us,dc=oracle,dc=com</value>
      </values>
   </extendedProperty>
 </serviceInstance>
</serviceInstances>

<jpsContexts default="ldap_idstore">
  <jpsContext name="ldap_idstore">
    <serviceInstanceRef ref="idstore.ldap"/>
  </jpsContext>

  <jpsContext name="bootstrap_credstore_context">
     <serviceInstanceRef ref="bootstrap.cred"/>  
   </jpsContext>
</jpsContexts>

Note the following points:

  • The name of the <serviceInstance> (idstore.ldap in the example above) can have any value, but it must match the instance referenced in element <serviceInstanceRef>.

  • The name of the <serviceProvider> (idstore.ldap.provider in the example above) can have any value, but it must match the provider in element <serviceInstance>.

  • To add properties to a provider instance with a prescribed script, see Appendix E, "Configuring OPSS Service Provider Instances with a Script."

22.3.3 Supported Login Modules for Java SE Applications

A login module is a component that authenticates users and populates a subject with principals. This process occurs in two distinct phases: during the first phase, the login module attempts to authenticate a user requesting, as necessary, a name and a password or some other credential data; only if this phase succeeds, the second phase is invoked. During the second phase, the login module assigns relevant principals to a subject, which is eventually used to perform some privileged action.

22.3.3.1 The Identity Store Login Module

A Java SE application can use a stack of login modules to authenticate its users; each module performs its own computations independently from the others in the stack. These and other services are specified in the file jps-config-jse.xml.

OPSS APIs includes the interface oracle.security.jps.service.login.LoginService which allows a Java SE application to invoke not just all login modules in a stack, but a subset of them in a prescribed order.

The name of the jps context (defined in the configuration file jps-config-jse.xml) passed to the method LoginContext in the LoginService interface determines the stack of login modules that an application uses. The standard JAAS API LoginContext can also be used to invoke the login modules defined in the default context

The sequence in which a jps context lists the login modules in a stack is significant, since the authentication algorithm takes this order into account in addition to other data, such as the flag that identifies the module security level (required, sufficient, requisite, or optional).

Out-of-the-box, the identity store service is file-based, its contents being provisioned in the file system-jazn-data.xml; the service can be reconfigured to use an LDAP-based identity store.

OPSS supports the Identity Store login module in Java SE applications, which can be used for authentication or identity assertion, as explained in the following sections:

Identity Store Login Module

The class associated with this login module is the following:

oracle.security.jps.internal.jaas.module.idstore.IdStoreLoginModule

An instance of this module is configured in the file jps-config-jse.xml as illustrated in the following fragment:

<serviceInstance name="idstore.loginmodule" provider="jaas.login.provider">
  <description>Identity Store Login Module</description>
  <property name="loginModuleClassName" value="oracle.security.jps.internal.jaas.module.idstore.IdStoreLoginModule"/>
  <property name="jaas.login.controlFlag" value="REQUIRED"/>
</serviceInstance>

Properties specific to this login module include the following:

remove.anonymous.role (defaults to true)
add.application.role (defaults to true)

22.3.3.2 Using the Identity Store Login Module for Authentication

This section illustrates the use of the Identity Store login module for basic username and password authentication.

Invoke IdStoreLoginModule

The following code fragment illustrates how to set a callback handler and a context:

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

Subject sub = new Subject();
CallbackHandler cbh = new YourCallbackHandler();
LoginContext context = new LoginContext(appName, subject,  cbh);
context.login();

The callback handler must be able to handle NameCallback and PasswordCallback.

Configure jps-config-jse.xml

The following jps-config-jse.xml fragment illustrates the configuration of the context appName:

<jpsContext name="appName">
   <serviceInstanceRef ref="jaaslm.idstore1"/>
</jpsContext>

<serviceProvider type="JAAS_LM" name="jaaslm.idstore" 
      class="oracle.security.jps.internal.jaas.module.idstore.IdStoreLoginModule">
   <description>Identity Store-based LoginModule
   </description>
</serviceProvider>

<serviceInstance name="jaaslm.idstore1" provider="jaaslm.idstore">
   <property name="jaas.login.controlFlag" value="REQUIRED"/>
   <property name="debug" value="true"/>
   <property name="addAllRoles" value="true"/>
</serviceInstance>

Write the Callback Handler

The following code snippet illustrates a callback handler able to handle name and password callback:

import javax.security.auth.callback.*;
import java.io.IOException; 
public class SampleCallbackHandler implements CallbackHandler {
//For name/password callbacks
private String name = null;private char[] password = null; 
public SampleCallbackHandler(String name, char[] pwd) { 
   if (name == null || name.length() == 0 )         
      throw new IllegalArgumentException("Invalid name ");
   else 
      this.name = name; 
   if (pwd == null || pwd.length == 0)
      throw new IllegalArgumentException("Invalid password ");
   else
      this.password = pwd;
}
public String getName() {
   return name;
   } public char[] getPassword()  {
   return password;
 }    
public void handle(Callback[] callbacks) 
   throws IOException, UnsupportedCallbackException {
   if (callbacks != null && callbacks.length > 0) {
      for (Callback c : callbacks) {
      if (c instanceof NameCallback) {
            ((NameCallback) c).setName(name);
            } 
   else 
      if (c instanceof PasswordCallback) {
         ((PasswordCallback) c).setPassword(password);
              }           
   else {
      throw new UnsupportedCallbackException(c);
 }
          }
      }
  }
}

22.3.3.3 Using the Identity Store Login Module for Assertion

To use the Identity Store login module for assertion, a developer must:

  • Provide the appropriate permission for the caller to execute the protected method setIdentity. This requires granting the permission oracle.security.jps.JpsPermission with the name IdentityAssertion.

  • Implement a callback handler that uses the class oracle.security.jps.callback.IdentityCallback as shown in the code sample below.

The above two requirements are illustrated in the following configuration and code samples.

Provisioning the JpsPermission

The following configuration sample illustrates a grant allowing the code MyApp the required JpsPermission to execute protected methods in the assertion login module:

<grant>
  <grantee>
    <codesource>
      <url>file:${soa.oracle.home}/application/myApp.ear</url>
                      <--! soa.oracle.home is a system property set when 
      the server JVM is started -->
    </codesource>
  </grantee>
  <permissions>
    <permission>
      <class>oracle.security.jps.JpsPermission</class>
      <name>IdentityAssertion</name>
    </permission>
  </permissions>
</grant>

The following configuration sample illustrates a grant allowing the principal jdoe the required JpsPermission to execute the assertion login module:

<grant>
  <grantee>
    <principals>
      <principal>
        <class>weblogic.security.principal.WLSUserImpl</class>
        <name>jdoe</name>
      </principal>
    </principals>
  </grantee>
  <permissions>
    <permission>
      <class>oracle.security.jps.JpsPermission</class>
      <name>IdentityAssertion</name>
    </permission>
  </permissions>
</grant>

Implementing the CallbackHandler

The following code fragment illustrates an implementation of the callback handler:

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
 
import oracle.security.jps.callback.IdentityCallback;
 
public class CustomCallbackHandler implements CallbackHandler {
    private String name = null;
    private char[] password;
 
    public CustomCallbackHandler(String name) {
        this.name = name;
    }
    
    public CustomCallbackHandler(String name, char[] password) {
      this.name  = name;
      this.password = password;
    }
 
    public void handle(Callback[] callbacks) throws IOException,
                                                    UnsupportedCallbackException {
        for (Callback callback : callbacks) {
            if (callback instanceof NameCallback) {
              NameCallback nc = (NameCallback) callback;
              nc.setName(name);
            }
            else if (callback instanceof PasswordCallback) {
              PasswordCallback pc = (PasswordCallback) callback;
              pc.setPassword(password);
            }
            else if (callback instanceof IdentityCallback) {
                IdentityCallback idcb = (IdentityCallback)callback;
                idcb.setIdentity(name);
                idcb.setIdentityAsserted(true);
                idcb.setAuthenticationType("CUSTOM");
            } else {
                //throw exception
                throw new UnsupportedCallbackException(callback);
            }
        }
    }
}

The following code fragment illustrates the implementation of a login module:

import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginContext;
 
import oracle.security.jps.service.JpsServiceLocator;
import oracle.security.jps.service.login.LoginService;
 
public class LoginModuleExample {
    private static final String CONTEXT_NAME = "JSE_UserAuthnAssertion";
 
    public LoginModuleExample() {
        super();
    }
 
    public Subject assertUser(final String username) throws Exception {
        CallbackHandler cbh =
            AccessController.doPrivileged(new PrivilegedExceptionAction<CallbackHandler>() {
                public CallbackHandler run() throws Exception {
                    return new CustomCallbackHandler(username);
                }
            });
        Subject sub = new Subject();
        LoginService ls =
            JpsServiceLocator.getServiceLocator().lookup(LoginService.class);
        LoginContext context = ls.getLoginContext(sub, cbh);
 
        context.login();
        Subject s = context.getSubject();
 
        return s;
    }
 
    public Subject authenticate(final String username, final char[] password) throws Exception {
        CallbackHandler cbh = new CustomCallbackHandler(username, password);
        Subject sub = new Subject();
        LoginService ls =
            JpsServiceLocator.getServiceLocator().lookup(LoginService.class);
        LoginContext context = ls.getLoginContext(sub, cbh);
 
        context.login();
        Subject s = context.getSubject();
 
        return s;
    }
 
    public static void main(String[] args) {
        LoginModuleExample loginModuleExample = new LoginModuleExample();
        try {
            System.out.println("authenticated user subject = " + 
                               loginModuleExample.authenticate("testUser", 
 "welcome1".toCharArray()));
            System.out.println("asserted user subject = " +
                               loginModuleExample.assertUser("testUser"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

22.3.4 Using the OPSS API LoginService in Java SE Applications

To invoke a login module programmatically in Java SE applications, use the method getLoginContext of the interface oracle.security.jps.service.login.LoginService.

Similar to the method LoginContext in the standard JAAS API, getLoginContext returns an instance of a LoginContext object that can be used to authenticate a user, but, more generally, it also allows the use of any number of login modules in any order. Authentication is then performed on just those login modules and in the order they were passed.

The following code fragment illustrates user authentication against a subset of login modules in a prescribed order using getLoginContext:

import oracle.security.jps.service.ServiceLocator;
import oracle.security.jps.service.JpsServiceLocator;
import oracle.security.jps.service.login.LoginService;

//Obtain the login service
ServiceLocator locator = JpsServiceLocator.getServiceLocator();
LoginService loginService = locator.lookup(LoginService.class);

//Create the handler for given name and password
CallbackHandler cbh = new MyCallbackHandler("name", "password".toCharArray());

//Invoke login modules selectively in a given order
selectiveModules = new String[]{"lmName1", "lmName2", "lmName3"};
LoginContext ctx = loginService.getLoginContext(new Subject(), cbh, selectiveModules);
ctx.login();
Subject s = ctx.getSubject();

selectiveModules is an array of (login module) names, and the authentication uses precisely those login modules named in the array in the order listed in the array. Each name in the array must be the name of a service instance listed in the default context of the file jps-config-jse.xml.

The following fragment illustrates the configuration of a stack of two login modules:

<serviceProvider type="LOGIN" name="jaas.login.provider" class="oracle.security.jps.internal.login.jaas.JaasLoginServiceProvider">
   <description>Common definition for any login module instances</description>
</serviceProvider>

<serviceInstance name="auth.loginmodule" provider="jaas.login.provider">
   <description>User Authentication Login Module</description>
   <property name="loginModuleClassName" value="oracle.security.jps.internal.jaas.module.authentication.JpsUserAuthenticationLoginModule"/>
   <property name="jaas.login.controlFlag" value="REQUIRED"/>
</serviceInstance>
 
<serviceInstance name="custom.loginmodule" provider="jaas.login.provider">
   <description>My Custom Login Module</description>
   <property name="loginModuleClassName" value="my.custom.MyLoginModuleClass"/>
   <property name="jaas.login.controlFlag" value="REQUIRED"/>
</serviceInstance>
 
<jpsContexts default="aJpsContext">
   <jpsContext name="aJpsContext">
     <serviceInstanceRef ref="auth.loginmodule"/>
     <serviceInstanceRef ref="custom.loginmodule"/>
   </jpsContext>
</jpsContexts>

22.4 Configuration Examples

This section illustrates the configuration of the following artifacts:

  • XML policy and credential stores

  • XML and LDAP identity stores

  • Login Module Principals

XML Policy and Credential Stores Configuration

The following snippets illustrate the configuration of XML-based policy and credential stores. The contents of an XML-based policy store is specified in the file system-jazn-data.xml; the contents of an XML-based credential store is specified in the file cwallet.sso.

<serviceProviders> <serviceProvider class="oracle.security.jps.internal.policystore.xml.XmlPolicyStoreProvider" name="policystore.xml.provider" type="POLICY_STORE">
  <description>XML-based PolicyStore Provider</description>
 </serviceProvider>

 <serviceProvider class="oracle.security.jps.internal.credstore.ssp.SspCredentialStoreProvider" name="credstoressp" type="CREDENTIAL_STORE">
  <description>SecretStore-based CSF Provider</description>
 </serviceProvider>
</serviceProviders>

<serviceInstances>
 <serviceInstance location="./" provider="credstoressp" name="credstore">
  <description>File-based Credential Store Service Instance</description>
 </serviceInstance>

<serviceInstance location="./system-jazn-data.xml" provider="policystore.xml.provider" name="policystore.xml">
   <description>File-based Policy Store Service Instance</description>
 </serviceInstance>
</serviceInstances>

XML Identity Store Configuration

The following snippets illustrate the configuration of an XML-based identity store. The contents of an XML-based identity store is specified in the file system-jazn-data.xml.

<serviceProvider
  class="oracle.security.jps.internal.idstore.xml.XmlIdentityStoreProvider"
  name="idstore.xml.provider" 
  type="IDENTITY_STORE">
 <description>XML-based Identity Store Service Provider</description>
</serviceProvider>

<serviceInstance 
  location="./system-jazn-data.xml" provider="idstore.xml.provider"
  name="idstore.xml">
 <description>File Based Identity Store Service Instance</description>
 <property value="jazn.com" name="subscriber.name"/>
</serviceInstance>

LDAP Identity Store Configuration

The snippets below illustrate the configuration of an LDAP-based identity store, which includes the required configuration of the bootstrap credentials to access the LDAP server. The service instance property idstore.type can have the following values, according to the LDAP used:

Table 22-1 Idstore Types

Supported LDAP Idstore.type value

Oracle Internet Directory 10g and 11g

OID

Oracle Virtual Directory 10g and 11g

OVD

Sun Java System Directory Server 6.3

IPLANET

Active Directory 2003, 2008

ACTIVE_DIRECTORY

Novell eDirectory 8.8

EDIRECTORY

Oracle Directory Server Enterprise Edition 11gR1 (11.1.1.3+)

IPLANET

IBM Tivoli DS 6.2

OPEN_LDAP

OpenLDAP 2.2.

OPEN_LDAP


  <serviceProvider
   class="oracle.security.jps.internal.idstore.ldap.LdapIdentityStoreProvider"
   name="idstore.ldap.provider" type="IDENTITY_STORE">
         <description>LDAP-based Identity Store Service Provider</description>
</serviceProvider>

<serviceProvider
   class="oracle.security.jps.internal.credstore.ssp.SspCredentialStoreProvider"
   name="credstoressp" type="CREDENTIAL_STORE">
 <description>SecretStore-based CSF Provider</description>
</serviceProvider>
 
<serviceInstance name="idstore.oid" provider="idstore.ldap.provider">
         <property name="subscriber.name" value="dc=us,dc=oracle,dc=com"/>
         <property name="idstore.type" value="OID"/>
         <property value=ldap://myOID.com:3555 name="ldap.url"/>
        <extendedProperty>
                 <name>user.search.bases</name>
                  <values>
                           <value>cn=users,dc=us,dc=oracle,dc=com</value>
                  </values>
         </extendedProperty>
         <extendedProperty>
                 <name>group.search.bases</name>
                  <values>
                           <value>cn=groups,dc=us,dc=oracle,dc=com</value>
                  </values>
         </extendedProperty>
         <property name="username.attr" value="uid"/>
         <propperty name="group.attr" value="cn"/>
</serviceInstance>

<serviceInstance location="./bootstrap" provider="credstoressp"
 name="bootstrap.cred">
 <property value="./bootstrap" name="location"/>
</serviceInstance>

Login Module Principals

The following properties are set in the out-of-the-box jps-config-jse.xml:

 <property name="oracle.security.jps.enterprise.user.class"
          value="weblogic.security.principal.WLSUserImpl"/>
 
    <property name="oracle.security.jps.enterprise.role.class"
             value="weblogic.security.principal.WLSGroupImpl"/>

The above propeties must be used in any login module; this implies that the principals that represent users and groups in the identity store are the following:

weblogic.security.principal.WLSUserImpl
weblogic.security.principal.WLSGroupImpl