25 Configuring Java SE Applications to Use OPSS

The information in this chapter applies only to Java SE applications, and the intended audience are developers of Java SE applications.

For similar information about Java EE applications, see Chapter 24, "Configuring Java EE Applications to Use OPSS."

This chapter includes in the following topics:

25.1 Using OPSS in Java SE Applications

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

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

  • The application must call JpsStartup.start() at initialization. For details, see The Class JpsStartup.

  • 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.

    • opss.audit.logDirectory, the path to writable directory where audit records for JSE are written

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

  • The JVM option -Djava.security.policy specifies the location of the Oracle WebLogic Server policy file weblogic.policy, typically located in the directory wl_home/server/lib.

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

    Note:

    The sample snippet is not required if you want to develop and use only CSF/KSS functionality.
    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 9, "Configuring the OPSS Security Store."

25.1.1 The Class JpsStartup

This section describes the following enhancements to the class JpsStartup:

  • A set of OPSS states for the method JpsStartup.start()

  • Several run-time options for the method JpsStartup.start()

  • A new constructor for the class JpsStartup

  • The method JpsStartup.getState()

For complete information about this class, see Oracle Fusion Middleware Java API Reference for Oracle Platform Security Services. For examples of use, see OPSS Starting Examples.

This section contains the following information:

25.1.1.1 JpsStartup.start States

The transition states of JpsStartup.start() are defined by the following constants:

  • UNINITIALIZED - The default state before JpsStartup.start() is invoked.

  • INITIALIZING - The state to which it transitions after JpsStartup.start() has been invoked.

  • FAILURE - The state to which it transitions if any failure has occurred during the INITIALIZING state.

  • ACTIVE - The state to which it transitions if all services have been started without failures, that is, if no failures have occurred in the INITIALIZING state.

  • INACTIVE - The state to which it transitions after JpsStatup.stop() is invoked.

25.1.1.2 Run-Time Options to JpsStartup

The run-time options with which you can invoke the class constructor jpsStartup() are described in the following table:

Table 25-1 JpsStartup Run-Time Options

Option Values Default Description

ACTIVE_CONTEXT

The name of an active context or the string "default".

"default"

If passed, use the specified context; otherwise, use the "default" context.

ENABLE_JAVA_POLICY_PROVIDER

TRUE or FALSE

TRUE

Set to TRUE to use the Java2 policy provider; otherwise, set to FALSE.

ENABLE_AUDIT_SERVICE_EXT

TRUE or FALSE

TRUE

Set to TRUE to start the audit service with audit monitoring and auditloader; otherwise, set to FALSE.

ENABLE_POLICY_STORE_SERVICE_EXT

TRUE or FALSE

TRUE

Set to TRUE to start the Policy Distribution Point (PDP) with policy change scanner thread; otherwise, set to FALSE.

ENABLE_DEPLOYMENT_HANDLING

TRUE or FALSE

TRUE

Set to TRUE to create deployment handlers; otherwise, set to FALSE.

RUNTIME_MODE

DEFAULT or

SCRIPT

DEFAULT

Set to SCRIPT to have the options ENABLE_JAVA_POLICY_PROVIDER , ENABLE_AUDIT_SERVICE_EXT, ENABLE_POLICY_STORE_SERVICE_EXT, and ENABLE_DEPLOYMENT_HANDLING set to FALSE; otherwise set to DEFAULT.

JPS_CONFIG

The path to the file jps.config.xml

The path specified by the property oracle.security.jps.config

If passed, used the specified path; otherwise, use the default path.


25.1.1.3 A New Class Constructor

The following constructor has been added to the class:

JpsStatup(java.lang.String platformType,
          java.util.Map<java.lang.String, 
          ContextConfiguration>  ctxtCfgs,
          java.util.Map<java.lang.String,?> options)

The argument ContextConfiguration holds the details about the context within <jpsContext> in the configuration file jps-config-jse.xml. To obtain ContextConfiguration before calling JpsStartup, see examples 3 and 4 in OPSS Starting Examples.

To start multiple contexts (in addition to the "default" context), pass multiple context information; see example 4 in OPSS Starting Examples. To start some context (other than the "default" context), then pass the context name; if nothing is passed, then the default context is started.

The argument options encloses all the startup properties used to start OPSS; for the list of available options, see Table 25-1.

This constructor is typically invoked when OPSS is to be started with additional ContextConfiguration details; see example 3 in OPSS Starting Examples.

25.1.1.4 The Method JpsStartup.getState

This method returns the state OPSS is at the point of invocation, that is, it returns one of INITIALIZING, UNINITIALIZED, FAILURE, ACTIVE or INACTIVE.

25.1.1.5 OPSS Starting Examples

The following code examples illustrate the use of the class JpsStartup in typical starting scenarios.

Example 1

The following example illustrates how to start OPSS without any explicit parameters.

JpsStartup jpsStartUp = new JpsStartup();
jpsStartUp.start();
jpsStartUp.getState();   
jpsStartUp.stop();

Example 2

The following example illustrates how to start OPSS with the following configuration:

  • default1 is the default context passed as part of the contextConfig map.

  • To disable audit runtime services, such as the audit loader and the audit monitoring, then pass the parameter "ENABLE_AUDIT_SERVICE_EXT" set to "FALSE"; note that by default this parameter is enabled.

  • To disable runtime services, such as policy changes scanner thread, pass the parameter "ENABLE_POLICY_STORE_SERVICE_EXT" set to "FALSE"; note that by default this parameter is enabled.

ConfigurationServiceProvider prov = ConfigurationServiceProvider.newInstance();
ConfigurationService configService = prov.getConfigurationService();
ContextConfiguration configuration = configService.getContextConfiguration("default1");
Map<String, ContextConfiguration> contextConfig = new HashMap<String, ContextConfiguration>();
contextConfig.put(JpsConstants.DEFAULT_CONTEXT_KEY, configuration);

Map<String, Object> option = new HashMap<String, Object>();
option.put(JpsConstants.ENABLE_AUDIT_SERVICE_EXT, "FALSE");
option.put(JpsConstants.ENABLE_POLICY_STORE_SERVICE_EXT, "FALSE");

JpsStartup  jpsStartUp = new JpsStartup("JSE", contextConfig, option);
jpsStartUp.start();
jpsStartUp.stop();

Example 3

The following example illustrates how to start OPSS with additional ContextConfigurations details.

Map<String, Object> startupOption = new HashMap<String, Object>();
        
ConfigurationServiceProvider prov = ConfigurationServiceProvider.newInstance();
ConfigurationService configService = prov.getConfigurationService();
ContextConfiguration configuration = configService.getContextConfiguration("default1");

Map<String, ContextConfiguration> contextConfig = new HashMap<String, ContextConfiguration>();
contextConfig.put(JpsConstants.DEFAULT_CONTEXT_KEY, configuration);
 
JpsStartup jpsStartUp = new JpsStartup("JSE", contextConfig, startupOption);
jpsStartUp.start();
jpsStartUp.stop();

Example 4

The following example illustrates how to start OPSS with multiple contexts. It is assumed that the jps.config.xml file contains the following contexts:

<jpsContexts default="default">
 <jpsContext name="default">
  <serviceInstanceRef ref="credstore"/> ...
 </jpsContext>

 <jpsContext name="default1">
  <serviceInstanceRef ref="idstore.loginmodule"/> ...
 </jpsContext>

 <jpsContext name="default2">
  <serviceInstanceRef ref="keystore"/> ...
 </jpsContext>

 <jpsContext name="default3">
  <serviceInstanceRef ref="policystore "/> ...
 </jpsContext>

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

ConfigurationServiceProvider prov = ConfigurationServiceProvider.newInstance();
ConfigurationService configService = prov.getConfigurationService();
ContextConfiguration configuration1 = configService.getContextConfiguration("default1");
ContextConfiguration configuration2 = configService.getContextConfiguration("default2");
ContextConfiguration configuration3 = configService.getContextConfiguration("default3");
 
Map<String, ContextConfiguration> contextConfig = new HashMap<String, ContextConfiguration>();
 
contextConfig.put(JpsConstants.DEFAULT_CONTEXT_KEY, configuration);
contextConfig.put(("default1", configuration1);
contextConfig.put(("default2", configuration2);
contextConfig.put(("default3", configuration3);
 
Map<String, Object> startupOption = new HashMap<String, Object>();
startupOption.put(JpsConstants.ACTIVE_CONTEXT, "default");
 
JpsStartup jpsStartUp = new JpsStartup("JSE", contextConfig, startupOption);
jpsStartUp.start();
jpsStartUp.stop();

The parameter "ACTIVE_CONTEXT" specifies the context to use as default. If the JpsConstants.DEFAULT_CONTEXT_KEY is not passed as part of map, then by default the "default" context is started provided there is no "ACTIVE_CONTEXT" key is passed as part of startupOption map.

Example 5

The following example illustrates how to start OPSS in "SCRIPT" mode. This mode is typically used during migration or upgrading, when runtime services are not required to be started.

Map<String, Object> startupOption = new HashMap<String, Object>();
startupOption.put(JpsConstants.RUNTIME_MODE, "SCRIPT");        
 
JpsStartup jpsStartUp = new JpsStartup("JSE", startupOption);
jpsStartUp.start();
jpsStartUp.stop();

25.3 Authenticating Java SE Applications

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

25.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

25.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."

25.3.3 Login Modules

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.

In any of the supported login modules, you can set the following properties:

enable.anonymous (default: false)
remove.anonymous.role (default: true)
add.application.roles (default: true)
add.authenticated.role (default: true)

For a sample configuration, see "<serviceInstance>"

The supported login modules are the following:

Note 1:

The User Authentication login module and the User Assertion login modules can be used in both Java EE and SE applications.

Note 2:

In Java SE applications, the User Authentication login module and the User Assertion login modules are supported by the default identity store service.

25.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)
25.3.3.1.1 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);
 }
          }
      }
  }
}
25.3.3.1.2 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();
        }
    }
}

25.3.3.2 The User Authentication Login Module

The User Authentication login module can be used by both Java EE and Java SE applications to authenticate a user given a user name and a password. The configuration of this login module is available out-of-the-box. This login module authenticates against the domain identity store.

Here is a code fragment using this module for programmatic authentication:

import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
public class MyCallbackHandler  extends CallbackHandler {
        private String name = null;
        private char[] password = null;
 
        public MyCallbackHandler(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 ncb  = (NameCallback) callback;
                    ncb.setName(name);
                }
                else if (callback instanceof PasswordCallback) {
                    PasswordCallback pcb  = (PasswordCallback) callback;
                    pcb.setPassword(password);
                }
                else {
                    throw UnsupportedCallbackException(callback);
                }
            }
        }
}
 
JpsLoginModuleFactory factory = JpsLoginModuleFactory.getLoginModuleFactory();
CallbackHandler cbh = new  MyCallbackHandler("name", "password".toCharArray());
LoginContext ctx = factory.getLoginContext(JpsLoginModuleType.USER_AUTHENTICATION, null, cbh);
ctx.login();
Subject s = ctx.getSubject();

25.3.3.3 The User Assertion Login Module

The User Assertion login module can be used by both Java EE and Java SE applications to assert a user identity. This login module asserts against the domain identity store, and it requires implementing the callback oracle.security.jps.callback.IdentityCallback. Moreover, execution of this login module requires an IdentityAssertion permission (with execute as action) for oracle.security.jps.JpsPermission.

To use the User Assertion login module for programmatic assertion, a developer must:

  • Provide the appropriate permission for the caller to execute login module protected methods. This requires granting the permission oracle.security.jps.JpsPermission with name IdentityAssertion (and action execute).

  • Implement a callback handler that uses the class oracle.security.jps.callback.IdentityCallback.Implement the programmatic assertion code.

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

25.3.3.3.1 Provisioning the jpsPermission

The following configuration sample illustrates a grant allowing the principal jdoe the required JpsPermission to execute the User 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>
      <action>execute</action>
    </permission>
  </permissions>
</grant>

For a codesource example, see grant sample in Section 24.6.6, "Using Supported Permission Classes."

25.3.3.3.2 Implementing the CallbackHandler

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

import oracle.security.jps.callback.IdentityCallback;
 
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;
 
    public class AssertCallbackHandler implements CallbackHandler {
        private String name = null;
 
        public AssertCallbackHandler(String name) {
            this.name = name;
        }
 
 
        public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
            for (Callback callback : callbacks) {
                if (callback instanceof IdentityCallback) {
                    IdentityCallback nc = (IdentityCallback) callback;
                    nc.setIdentity(name);
                }
                else {
                    throw new UnsupportedCallbackException(callback);
                }
            }
        }
    }
25.3.3.3.3 Implementing the Programmatic Assertion

The following code fragment illustrates how to assert a user:

import oracle.security.jps.callback.IdentityCallback;
import oracle.security.jps.internal.api.jaas.module.JpsLoginModuleFactory;
import oracle.security.jps.internal.api.jaas.module.JpsLoginModuleType;
 
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
 
        Subject subject = AccessController.doPrivileged(new PrivilegedExceptionAction<Subject>() {
            public Subject run() throws Exception {
                JpsLoginModuleFactory f = JpsLoginModuleFactory.getLoginModuleFactory();
                CallbackHandler cbh = new AssertCallbackHandler(name);
                LoginContext c = f.getLoginContext(JpsLoginModuleType.USER_ASSERTION, null, cbh);
                c.login();
                return c.getSubject();
            }
        });

25.3.3.4 Executing As an Asserted User

Applications often need to execute actions as if the operation were run by a user, that is, to invoke run-as operations. OPSS solves this problem by allowing Java EE and Java SE applications to use the subjects of an asserted user to execute application logic as if it were executed by that user; this functionality is encapsulated in the OPSS class SubjectSecurity and supported on the Oracle WebLogic platform.

This section includes the following topics:

25.3.3.4.1 Use Cases

Here are several examples where the class SubjectSecurity can be used to solve a programming challenge:

  • UC1 - A scheduler allows submitting and executing jobs; these jobs are typically submitted at one time and executed at some future time, therefore the need to assert the user's identity and execute the scheduled job in the user's security context.

  • UC2 - A scheduled job is executed by an Entity Bean that, further, may call other Entity Beans to complete the job, and all the code to complete the job must execute in the user's security context; therefore this context needs to be propagated through the call path, across Entity Beans.

  • UC3 - The permissions to execute a scheduled job are based on the user's application roles; in this case, the user's security context must be aware of the application roles granted to the user regardless of the code path across Entity Beans.

  • UC4 - When an unauthenticated user submits a job, the job metadata needs to persist the anonymous user; eventually, the job must as executed by the anonymous user.

  • UC5 - An MBean running in an application is invoked from a UI via an MBean server; the MBean operation invokes a series of diagnostic tests that need to be run as a particular user.

  • UC6 - A user identity has been asserted by the user assertion API, and has produced a user security context that can be used to run operations as that user.

25.3.3.4.2 Programming Guidelines and Recommendations

The SubjectSecurity API allows a user to execute code either when a pre-computed Subject is available (as in use case UC6), or when the user needs to be first asserted. If an application security context has not been set, then, before using the SubjectSecurity API, the application must set its id as follows:

AppSecurityContext.setApplicationID(applicationID)

For example, the application id must be set with setApplicationID in use cases UC1, UC2, and UC3, and in the case of a system Mbean as in use case UC5. When an application security context has been set, the call to setApplicationID is optional.

The following are general recommendations when using the class SubjectSecurity.

  • Use SubjectSecurity.getActionExecutor(subject) if you have already obtained a subject and want to execute some operation with it. Depending on how the subject was obtained, we have the following cases:

    • If the container authentication was performed and the subject was obtained using SubjectUtil.getCurrentSubject(), then that subject works with the container security and OPSS security, and application roles are considered.

    • If the subject was obtained via programmatic authentication using JpsLoginModuleFactory, then that subject works with the container security and OPSS security.

  • Use SubjectSecurity.getActionExecutor(userName) if just the user name is available and want to execute some operation as that user. This method performs privilege operations including identity assertion, which requires the calling code to have the necessary permissions to invoke those operations. Therefore, applications using this method require a codebase permission like the following:

    <grant>
        <grantee>
            <codesource>
               <url>file:${domain.home}/servers/${weblogic.Name}/myApp.ear/-</url>
            </codesource>
        </grantee>
        <permissions>
            <permission>
                <class>oracle.security.jps.JpsPermission</class>
                <name>IdentityAssertion</name>
                <actions>execute</actions>
            </permission>
        </permissions>
    </grant>
    

    where <url> specifies the location of the code where the method is called.

  • ActionExecutor.execute(PrivilegedAction) invokes Subject.doAs() with the user's JAAS subject and the application's PrivilegedAction.

  • Use SubjectSecurity.executeAs(Subject subject, PrivilegedAction<T> action) to execute the action immediately.

  • Use SubjectSecurity.executeAs(Subject subject, PrivilegedExceptionAction<T> action) to execute the action immediately. This method obtains the action executor using getActionExecutor(subject), and executes the action.

For complete details about the class oracle.security.jps.runtime.SubjectSecurity and the interface oracle.security.jps.runtime.ActionExecutor see Oracle Fusion Middleware Java API Reference for Oracle Platform Security Services.

25.3.3.4.3 A Code Example

The following sample code illustrates the use of the SubjectSecurity API to run as an asserted user named jdoe:

// get a SubjectSecurity instance
        final SubjectSecurity subjectSecurity = SubjectSecurity.getInstance();
        String username = "jdoe";
 
// create ActionExecutor with subjectSecurity getActionExecutor(String username)
        ActionExecutor executor =
        AccessController.doPrivileged(new PrivilegedExceptionAction<ActionExecutor>() {
            public ActionExecutor run() throws AssertionException {
                    return subjectSecurity.getActionExecutor(username);
            }
        }, null);
 
// When OPSS subjects are available,
// create ActionExecutor with SubjectSecurity getActionExecutor(Subject subject)
        Subject opssSubject = SubjectUtil.getCurrentSubject();
        ActionExecutor ececutor = subjectSecurity.getActionExecutor(opssSubject);
 
 //run privilegedAction
        PrivilegedAction action = new MyPrivilegedAction();
        executor.execute(action);
 
 //run PrivilegedExceptionAction
        PrivilegedExceptionAction exceptionAction = new MyPrivilegedExceptionAction();
        try {
            executor.execute(exceptionAction);
        } catch (PrivilegedActionException e) {
            // handle PrivilegedActionException
 
        }

25.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>

25.4 Auditing in Java SE Applications

This section provides the background you need to get started leveraging the Common Audit Framework (CAF) to audit events with your Java SE applications. It explains basic requirements and describes some common audit usage scenarios for Java SE clients. It contains these topics:

25.4.1 About Auditing in the Java SE Environment

This section explains useful audit concepts.

See Also:

For background information about using OPSS in Java SE environments, see Section 25.1.

Audit Configuration Properties

As explained in Section 25.1, the audit configuration resides in the jps-config-jse.xml file.

Here is an example fragment from that file showing the audit properties:

<serviceInstance provider="audit.provider" name="audit">
 <property value="Medium" name="audit.filterPreset"/>
 <property value="0" name="audit.maxDirSize"/>
 <property value="104857600" name="audit.maxFileSize"/>
 <property value="" name="audit.specialUsers"/>
 <property value="" name="audit.customEvents"/>
 <property value="Db" name="audit.loader.repositoryType"/>
 <property value="file" name="auditstore.type"/>
</serviceInstance>

Audit Service and Processes

The audit run-time service enables the application to audit various events dictated by conditions specified in audit metadata (which resided in audit store), and records each event in the bus-stop files.

Other notable processes include the audit loaders which move data from bus-stop files to the database.

Bus-stop Files

These files are explicitly designed to hold the newly generated audit records. Although they are sometimes referred to mistakenly as audit logs, they should not be confused with system log files.

The following sections provide more details about aspects of audit operation including requirements and recommendations for the JavaSE environment.

25.4.2 Configuring the Audit Bus-stop Directory

Note:

Starting with 11gRelease 1 (11.1.1.9), the naming format of bus-stop files has changed to:
$hostname_$processId_audit_$majorVersion_$minorVersion.log

For example:

example.myhost.com_12345_audit_1_0.log

Java SE clients must specify a writable directory to which audit bus-stop files will be written. To configure this directory:

  • Set it as an audit service property ("audit.logDirectory" in the file jps-config-jse.xml) using the setAuditRepository() WLST command. For example:

    setAuditRepository(logDirectory="/Audit")
    

    or

  • Pass system property "opss.audit.logDirectory" OR "oracle.instance" to the JVM

WARNING:

If the runtime process is unable to determine a writable directory, the audit service may stop working.

This directory can be shared between JavaSE client processes if:

  • You set the directory with the audit.logDirectory service property in the file jps-config-jse.xml and all the Java SE clients/processes in the environment use the same jps-config-jse.xml

    or

  • the same value for the bus-stop directory is passed as a system property to all Java SE clients.

25.4.3 Configuring Audit Loaders

The audit data loader is a thread/process that pushes the data from the bus-stop file to a database.

Note:

Having the audit.loader.repositoryType property set to 'Db' is the default requirement for the audit loader for both JavaEE and JavaSE environments.

There are two kinds of loaders for JavaSE applications:

  • The first is the JavaSE audit service loader, a thread started by the runtime OPSS audit service.

    Out-of-the-box, this audit loader is disabled. To enable it, pass "audit.loader.enable=true" to the JVM.

  • The second loader is a standalone JavaSE application. It is the recommended loader for this environment, especially if you are using a shared directory as described in Section 25.4.2.

    For details about starting the stand-alone loader, see Section 14.2.4.2 and the example in the final step of Section 25.4.4.1.

25.4.4 Implementing Common Audit Scenarios in JavaSE

This section describes some common scenarios that implement auditing in Java SE.

We consider environments with and without a co-located Oracle WebLogic Server.

25.4.4.1 Auditing by JavaSE Clients, with co-located WebLogic Server

After setting up the domain, take these steps as administrator:

  1. Set up the audit loader configuration by either executing the setAuditRepository() WLST command or by using Fusion Middleware Control.

    Note:

    setAuditRepository is an online command.

    This step also updates appropriate properties in the file jps-config.xml and jps-config-jse.xml, thus enabling both Java SE and Java EE scenarios. The WLST script is similar to the following example:

    # logDirectory is the location of the audit log directory   
    # jdbcSring is the connect string
    # user and pass refer to the IAU append schema user and password  
    # (These get stored in bootstrap credential store)
     
    setAuditRepository(switchToDB = "true",dataSourceName = "jdbc/AuditDB",interval = "20", timezone="utc", logDirectory="/foo/bar", jdbcString="jdbc:oracle:thin:@host:1521:sid", dbUser="test_iau_append", dbPassword="password");
    

    Note:

    A standalone audit loader is not needed if the writable audit directory for JavaSE processes is set to be the same directory as the log directory for Oracle WebLogic Server (say DOMAIN_HOME/servers/AdminServer/logs). The container's audit loader process takes care of this task.
  2. If choosing a separate writable audit directory for JavaSE processes, start a standalone audit loader to push these records to the database. Pass one of the system properties, setting it to the value of the bus-stop directory. In the example below, oracle.instance is used:

    $JAVA_HOME/bin/java 
    -classpath $COMMON_COMPONENTS_HOME/modules/oracle.jps_11.1.1/jps-manifest.jar:
    $COMMON_COMPONENTS_HOME/modules/oracle.jdbc_11.1.1/ojdbc6dms.jar:
    $COMMON_COMPONENTS_HOME/modules/oracle.iau_11.1.1./fmw_audit.jar 
    -Doracle.instance=$ORACLE_INST 
    -Doracle.security.jps.config=path_to_jps-config-jse.xml
    oracle.security.audit.ajl.loader.StandaloneAuditLoader
    

    See Also:

    The final step in Section 25.4.4.2.

25.4.4.2 Auditing by JavaSE Clients, without co-located WebLogic Server

The assumption here is that a middleware home is available but no Oracle WebLogic Server is running. So Fusion Middleware Control or online WLST commands such as setAuditRepository() cannot be used.

The steps are as follows:

  1. Out-of-the-box, audit runtime is enabled in JavaSE if a writable directory is provided to the JVM.

    Since audit.logDirectory service property is not available by default in the file jps-config-jse.xml, we recommend that you set either opss.audit.logDirectory OR oracle.instance system property to specify the bus-stop directory.

  2. As administrator, you then add a bootstrap credential to the bootstrap credential store as defined in the file jps-config-jse.xml using the addBootStrapCredential( ) WLST command:

    addBootStrapCredential(jpsConfigFile='../../../user_projects/domains/base_domain/config/fmwconfig/jps-config-jse.xml', map='AuditDbPrincipalMap', key='AuditDbPrincipalMap', username='TEST_IAU_APPEND', password='password')
    
  3. If JavaSE processes use a shared audit directory, start the standalone audit loader to push these records to the database.

  4. Finally, ensure that the relevant audit loader is operational. There are two scenarios:

    • The audit directory is not shared (that is, every JavaSE process writes to its own audit directory).

      In this case, simply enable the runtime service's audit loader by specifying audit.loader.enable=true.

    • The audit directory is shared (that is, multiple JavaSE processes write to the same audit directory).

      In this case, start the standalone audit loader as shown in Section 25.4.4.1.

      Note:

      The standalone audit loader can also be used when the directory is not shared.

25.5 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 25-2 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