10 Working with Security

This chapter describes how to apply security to a Coherence*Extend client. Coherence*Extend allows a wider range of consumers access to Coherence caches. These consumers include desktop applications, remote servers, and machines located across WAN connections.

This chapter has the following sections:

10.1 Introduction

Coherence*Extend consists of an extend client running outside the cluster and a proxy service running inside the cluster and hosted by one or more cache servers. The client APIs route all requests to the proxy. The proxy responds to client requests by delegating to Coherence clustered services, such as a partitioned or replicated cache service or an invocation service.

Since the extend client exists outside of the cluster, the issue of securing access to the cluster takes on greater importance. This chapter illustrates three techniques that you can use to secure access between the client and the cluster. The techniques include using identity token-based passwords, an entitled cache service, and an invocation service.

A detailed discussion of these security techniques and extend clients is beyond the scope of this tutorial. For more information on these topics, see the Oracle Coherence Client Guide.

10.2 Enabling Token-Based Security

You can implement token-based security to enable access between an extend client and an extend proxy in the cluster. To enable access between the extend client and an extend proxy the following files are typically required:

  • client application files, which describes the functionality that wants to access the cluster

  • cache configuration files, where the extend client and the extend proxy each have their own cache configuration

  • operational override file, which overrides the operational and run-time settings in the default operational deployment descriptor

  • server start-up files, where there will be a start-up file for the extend proxy and for the cache server in the cluster

  • POF configuration deployment descriptor, which specifies custom data types when using POF to serialize objects.

To add token-based security, you must additionally provide identity transformer and asserter implementations. The transformer generates the token on the client side and the asserter validates it on the cluster side.

The following steps describe how to create and run an application for an extend client that uses token-based security to access the cluster.

  1. Use a Security Helper File

  2. Create an Identity Transformer

  3. Create an Identity Asserter

  4. Create the Password File

  5. Enable the Identity Transformer and Asserter

  6. Create a Cache Configuration File for the Extend Client

  7. Create a Cache Configuration File for the Extend Proxy

  8. Create a Start-Up File for a Cache Server with a Proxy Service

  9. Create a Start-Up File for a Cache Server

  10. Configure Project Classpaths and Runtime Properties

  11. Run the Password Example

10.2.1 Use a Security Helper File

The examples in this chapter reference a security helper file which defines role-based security policies and access control to the cache. For the purposes of these examples, a file with simplified mappings is provided for you.

Cache access will be determined by a user's role. The security helper file defines several roles: role_reader, role_writer, and role_admin. It defines the mappings of various users to the roles, such as BuckarooBanzai to ROLE_ADMIN. It defines the mappings of roles to integer IDs, such as ROLE_ADMIN to 9. The helper file also defines the cache name and the Invocation Service name used in the examples.

The key features of this file are the login and checkAccess methods. The login method takes a user name and constructs a simplified Distinguished Name (DN). It then associates a role with the name. PofPrincipal provides the Principal implementation.

The checkAccess method demonstrates where the authorization code is placed. It determines whether the user can access the cache based on a provided user role.

To create a new project and the security helper file:

  1. Create a new project in JDeveloper named Security. Ensure that the package path is com.oracle.handson.

    See "Creating a New Project in an Existing Application" if you need detailed information on creating a project.

  2. Create a new Java file named SecurityExampleHelper.java.

    See "Creating a Java Class" if you need detailed information on creating a Java class.

  3. Copy the code illustrated in Example 10-1 into the file.

    Example 10-1 A Security Helper File

    package com.oracle.handson;
     
    import com.tangosol.io.pof.PofPrincipal;
     
    import com.tangosol.net.security.SecurityHelper;
     
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
     
    import java.security.Principal;
     
    import javax.security.auth.Subject;
     
     
    /**
    * This class provides extremely simplified role based policies and access control.
    *
    */
    public class SecurityExampleHelper
        {
        // ----- static methods -------------------------------------------------
     
        /**
        * Login the user.
        *
        * @param sName  the user name
        *
        * @return the authenticated user
        */
        public static Subject login(String sName)
            {
            // For simplicity, just create a Subject. Normally, this would be
            // done using JAAS.
            String sUserDN       = "CN=" + sName + ",OU=Yoyodyne";
            Set setPrincipalUser = new HashSet();
     
            setPrincipalUser.add(new PofPrincipal(sUserDN));
            // Map the user to a role
            setPrincipalUser.add(new PofPrincipal((String) s_mapUserToRole.get(sName)));
     
            return new Subject(true, setPrincipalUser, new HashSet(), new HashSet());
            }
     
        /**
        * Assert that a Subject is associated with the calling thread with a
        * Principal representing the required role.
        *
        * @param sRoleRequired  the role required for the operation
        *
        * @throws SecurityException if a Subject is not associated with the
        *         calling thread or does not have the specified role Principal
        */
        public static void checkAccess(String sRoleRequired)
            {
            Subject subject = SecurityHelper.getCurrentSubject();
     
            if (subject == null)
                {
                throw new SecurityException("Access denied, authentication required");
                }
     
            Map     mapRoleToId   = s_mapRoleToId;
            Integer nRoleRequired = (Integer) mapRoleToId.get(sRoleRequired);
     
            for (Iterator iter = subject.getPrincipals().iterator(); iter.hasNext();)
                {
                Principal principal = (Principal) iter.next();
                String    sName     = principal.getName();
     
                if (sName.startsWith("role_"))
                    {
                    Integer nRolePrincipal = (Integer) mapRoleToId.get(sName);
     
                    if (nRolePrincipal == null)
                        {
                        // invalid role
                        break;
                        }
     
                    if (nRolePrincipal.intValue() >= nRoleRequired.intValue())
                        {
                        return;
                        }
                    }
                }
     
            throw new SecurityException("Access denied, insufficient privileges");
            }
     
     
        // ----- constants  -----------------------------------------------------
     
        public static final String ROLE_READER = "role_reader";
     
        public static final String ROLE_WRITER = "role_writer";
     
        public static final String ROLE_ADMIN  = "role_admin";
     
        /**
        * The cache name for security examples
        */
        public static final String SECURITY_CACHE_NAME = "security";
     
        /**
        * The name of the InvocationService used by security examples.
        */
        public static String INVOCATION_SERVICE_NAME = "ExtendTcpInvocationService";
     
     
        // ----- static data  ---------------------------------------------------
     
        /**
        * The map keyed by user name with the value being the user's role.
        * Represents which user is in which role.
        */
        private static Map s_mapUserToRole = new HashMap();
     
        /**
        * The map keyed by role name with the value the role id.
        * Represents the numeric role identifier.
        */
        private static Map s_mapRoleToId = new HashMap();
     
     
        // ----- static initializer ---------------------------------------------
     
        static
            {
            // User to role mapping
            s_mapUserToRole.put("BuckarooBanzai", ROLE_ADMIN);
            s_mapUserToRole.put("JohnWhorfin", ROLE_WRITER);
            s_mapUserToRole.put("JohnBigboote", ROLE_READER);
     
            // Role to Id mapping
            s_mapRoleToId.put(ROLE_ADMIN, Integer.valueOf(9));
            s_mapRoleToId.put(ROLE_WRITER, Integer.valueOf(2));
            s_mapRoleToId.put(ROLE_READER, Integer.valueOf(1));
            }
        }
    

10.2.2 Create an Identity Transformer

An identity transformer (com.tangosol.net.security.IdentityTransformer) is a client-side component that converts a Subject or Principal into an identity token. The token must be a type that Coherence knows how to serialize. Coherence automatically serializes the token at run time and sends it as part of the connection request to the proxy.

To create an identity transformer implementation:

  1. Create a new Java class in the Security project named PasswordIdentityTransformer.

    See "Creating a Java Class" if you need detailed information.

  2. Import the IdentityTransformer interface. Ensure that PasswordIdentityTransformer implements IdentityTransformer.

  3. Implement the transformIdentity method so that it performs the following tasks:

    • tests whether the Subject exists and is complete

    • gets the principal names from the Subject and saves them in a String array

    • constructs the token as a combination of the password plus the Principal names as Pof-able types

Example 10-2 illustrates a possible implementation of PasswordIdentityTransformer.

Example 10-2 Sample Identity Transformer Implementation

package com.oracle.handson;
 
 
import com.tangosol.net.security.IdentityTransformer;
 
import java.security.Principal;
import java.util.Iterator;
import java.util.Set;
 
import javax.security.auth.Subject;
 
/**
* PasswordIdentityTransformer creates a security token that contains the
* required password and then adds a list of Principal names.
*
*/
public class PasswordIdentityTransformer
        implements IdentityTransformer
 
    {
    // ----- IdentityTransformer interface ----------------------------------
 
    /**
    * Transform a Subject to a token that asserts an identity.
    *
    * @param subject the Subject representing a user.
    *
    * @return the token that asserts identity.
    *
    * @throws SecurityException if the identity transformation fails.
    */
    public Object transformIdentity(Subject subject)
            throws SecurityException
        {
        if (subject == null)
            {
            throw new SecurityException("Incomplete Subject");
            }
 
        Set setPrincipals = subject.getPrincipals();
 
        if (setPrincipals.isEmpty())
            {
            throw new SecurityException("Incomplete Subject");
            }
 
        String[] asPrincipalName = new String[setPrincipals.size() + 1];
        int      i                = 0;
 
        asPrincipalName[i++] = System.getProperty("coherence.password",
                "secret-password");
 
        for (Iterator iter = setPrincipals.iterator(); iter.hasNext();)
            {
            asPrincipalName[i++] = ((Principal) iter.next()).getName();
            }
 
        // The token consists of the password plus the principal names as an
        // array of pof-able types, in this case strings.
        return asPrincipalName;
        }
    }

10.2.3 Create an Identity Asserter

An identity asserter (com.tangosol.net.security.IdentityAsserter) is a cluster-side component that resides on the cache server that hosts an extend proxy service. The asserter validates that the token created by the identity transformer on the extend client contains the required credentials to access the cluster.

To create an identity asserter implementation:

  1. Create a new Java class in the Security project named PasswordIdentityAsserter.

    See "Creating a Java Class" if you need detailed information.

  2. Import the IdentityAsserter interface. Ensure that PasswordIdentityAsserter implements IdentityAsserter.

  3. Implement the assertIdentity method such that it:

    • validates that the token contains the correct password, and that the password is the first name in the token

Example 10-3 illustrates a a possible implementation of PasswordIdentityAsserter.

Example 10-3 Sample Identity Asserter Implementation

package com.oracle.handson;
 
 
import com.tangosol.io.pof.PofPrincipal;
 
import com.tangosol.net.security.IdentityAsserter;
 
import java.util.HashSet;
import java.util.Set;
 
import javax.security.auth.Subject;
 
 
/**
* PasswordIdentityAsserter asserts that the security token contains the
* required password and then constructs a Subject based on a list of
* Principal names.
*
*/
public class PasswordIdentityAsserter
        implements IdentityAsserter
    {
    // ----- IdentityAsserter interface -------------------------------------
 
    /**
    * Asserts an identity based on a token-based identity assertion.
    *
    * @param oToken the token that asserts identity.
    *
    * @return a Subject representing the identity.
    *
    * @throws SecurityException if the identity assertion fails.
    */
    public Subject assertIdentity(Object oToken)
            throws SecurityException
        {
        if (oToken instanceof Object[])
            {
            String   sPassword        = System.getProperty(
                    "coherence.password", "secret-password");
            Set      setPrincipalUser = new HashSet();
            Object[] asName           = (Object[]) oToken;
 
            // first name must be password
            if (((String) asName[0]).equals(sPassword))
                {
                // prints the user name to server shell to ensure we are
                // communicating with it and to ensure user is validated
                System.out.println("Password validated for user: " + asName[1]);
                for (int i = 1, len = asName.length; i < len; i++)
                    {
                    setPrincipalUser.add(new PofPrincipal((String)asName[i]));
                    }
 
                return new Subject(true, setPrincipalUser, new HashSet(),
                        new HashSet());
                }
            }
        throw new SecurityException("Access denied");
        }
    }

10.2.4 Create the Password File

Create a Java file that will require a password to get a reference to a cache. Use the SecurityExampleHelper.login("BuckarooBanzai") to call the login method in SecurityExampleHelper to generate a token. At run time, the user name is associated with its Subject defined in the SecurityExampleHelper file. A token is generated from this Subject by PasswordIdentityTransformer and validated by PasswordIdentityAsserter as part of the connection request. If the validation succeeds, then a connection to the proxy and a reference to the cache is granted. Use Subject.doas to make the Subject available in the security context.

  1. Create a new Java class with a main method in the Security project named PasswordExample.

    See "Creating a Java Class" if you need detailed information.

  2. Implement the main to get a reference to the cache.

  3. Use SecurityExampleHelper.login to get a Subject for user BuckarooBanzai

  4. Implement the doAs method to make the subject part of the Java security context. The subject will be available to any subsequent code. In this case doAs is implemented to validate whether the user can access the cache based on its defined role.

Example 10-4 illustrates a a possible implementation of PasswordExample.

Example 10-4 Sample Implementation to Run the Password Example

package com.oracle.handson;
 
import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;
 
import java.io.IOException;
 
import java.security.PrivilegedExceptionAction;
 
import javax.security.auth.Subject;
 
 
/**
* This class shows how a Coherence Proxy can require a password to get a
* reference to a cache.
* <p>
* The PasswordIdentityTransformer will generate a security token that
* contains the password. The PasswordIdentityAsserter will validate the
* security token to enforce the password. The token generation and
* validation occurs automatically when a connection to the proxy is made.
*
*/
public class PasswordExample
    {
    // ----- static methods -------------------------------------------------
 
    /**
    * Get a reference to the cache. Password will be required.
    */
    public static void main (String[] args){
         getCache();  
           }
      public static void getCache()
        {
        System.out.println("------password example begins------");
 
        Subject subject = SecurityExampleHelper.login("BuckarooBanzai");
 
        try
            {
            NamedCache cache = (NamedCache) Subject.doAs(
                    subject, new PrivilegedExceptionAction()
                {
                public Object run()
                        throws Exception
                    {
                    NamedCache cache;
 
                    cache = CacheFactory.getCache(
                            SecurityExampleHelper.SECURITY_CACHE_NAME);
                    System.out.println("------password example succeeded------");
                    return cache;
                    }
                });
            }
        catch (Exception e)
            {
            // get exception if the password is invalid
            System.out.println("Unable to connect to proxy");
            e.printStackTrace();
            }
        System.out.println("------password example completed------");
        }
     
    }

10.2.5 Enable the Identity Transformer and Asserter

Configure an operational override file (tangosol-coherence-override.xml) to identify the classes that define the identity transformer (the class that transforms a Subject to a token on the extend client) and the identity asserter (the class that validates the token on the cluster).

  1. Create an XML file in JDeveloper. Name the file tangosol-coherence-override.xml and store it in the C:\home\oracle\labs\ directory.

  2. Use the identity-transformer and identity-asserter elements within the security-config stanza to identify the full path to the PasswordIdentityTransformer and PasswordIdentityAsserter implementation classes, respectively. Set subject-scope to true to associate the identity from the current security context with the cache and remote invocation service references that are returned to the client.

Example 10-5 illustrates a possible implementation of tangosol-coherence-override.xml.

Example 10-5 Specifying an Identity Transformer and an Asserter

<?xml version='1.0'?>
 
<!DOCTYPE coherence SYSTEM "coherence.dtd">
 
<coherence>
  <security-config>
    <identity-transformer>
      <class-name>com.oracle.handson.PasswordIdentityTransformer</class-name>
    </identity-transformer>
    <identity-asserter>
      <class-name>com.oracle.handson.PasswordIdentityAsserter</class-name>
    </identity-asserter>
    <subject-scope>true</subject-scope>
  </security-config>
</coherence>

10.2.6 Create a Cache Configuration File for the Extend Client

The cache configuration file for the extend client routes cache operations to an extend proxy in the cluster. At run time, cache operations are not executed locally, but are sent to the extend proxy service.

To create a cache configuration file for an extend client:

  1. Create an XML file in JDeveloper. Name the file client-cache-config.xml and save it in the C:\home\oracle\labs\ directory.

  2. Write the extend client cache configuration. The following list highlights some key elements:

    • use the cache-name element to define security as the name of the cache. Note that there must be a cache defined in the cluster-side cache configuration that is also named security.

    • use the remote-cache-scheme stanza to define the details about the remote cache

    • use the address and port elements in the tcp-initiator stanza to identify the extend proxy service that is listening on address localhost at port 9099

    • use defaults and serializer with a value of pof to call the serializer for the custom POF configuration file (which you will create later in this chapter)

Example 10-6 illustrates a possible implementation for client-cache-config.xml.

Example 10-6 Sample Extend Client Cache Configuration File

<?xml version="1.0"?>
<!DOCTYPE cache-config SYSTEM "cache-config.dtd">
 
<cache-config>
  <defaults>
    <serializer>pof</serializer>
  </defaults>
 
  <caching-scheme-mapping>
    <cache-mapping>
      <cache-name>security</cache-name>
      <scheme-name>examples-remote</scheme-name>
    </cache-mapping>
  </caching-scheme-mapping>
 
  <caching-schemes>
    <remote-cache-scheme>
      <scheme-name>examples-remote</scheme-name>
      <service-name>ExtendTcpCacheService</service-name>
      <initiator-config>
        <tcp-initiator>
          <remote-addresses>
            <socket-address>
              <address system-property="tangosol.coherence.proxy.address">localhost</address>
              <port system-property="tangosol.coherence.proxy.port">9099</port>
            </socket-address>
          </remote-addresses>
        </tcp-initiator>
      </initiator-config>
    </remote-cache-scheme>
 
    <remote-invocation-scheme>
      <scheme-name>remote-invocation-scheme</scheme-name>
      <service-name>ExtendTcpInvocationService</service-name>
      <initiator-config>
        <tcp-initiator>
          <remote-addresses>
            <socket-address>
              <address system-property="tangosol.coherence.proxy.address">localhost</address>
              <port system-property="tangosol.coherence.proxy.port">9099</port>
            </socket-address>
          </remote-addresses>
          <connect-timeout>2s</connect-timeout>
        </tcp-initiator>
        <outgoing-message-handler>
          <request-timeout>5s</request-timeout>
        </outgoing-message-handler>
      </initiator-config>
    </remote-invocation-scheme>
  </caching-schemes>
</cache-config>

10.2.7 Create a Cache Configuration File for the Extend Proxy

Create a cache configuration file for the extend proxy service.

  1. Create an XML file in JDeveloper. Name the file examples-cache-config.xml and save it in the C:\home\oracle\labs\ directory.

  2. Configure the extend proxy cache configuration file. The following list highlights some key elements:

    • use the cache-name element to define security as the name of the cache. Note that there must be a cache defined in the extend client cache configuration that is also named security.

    • use the address and port elements in the acceptor-config stanza to identify the extend proxy service that is listening on address localhost at port 9099

    • use the autostart element with the tangosol.coherence.extend.enabled system property to prevent the cache server from running a proxy service.

    • use defaults and serializer with a value of pof to call the serializer for the custom POF configuration file (which you will create later in this chapter)

Example 10-7 illustrates a possible implementation for the examples-cache-config.xml

Example 10-7 Sample Cache Configuration File for the Proxy Server

<?xml version="1.0"?>
 
<!DOCTYPE cache-config SYSTEM "cache-config.dtd">
 
<cache-config>
  <defaults>
    <serializer>pof</serializer>
  </defaults>
 
  <caching-scheme-mapping>
    <cache-mapping>
      <cache-name>security</cache-name>
      <scheme-name>ExamplesPartitionedPofScheme</scheme-name>
    </cache-mapping>
  </caching-scheme-mapping>
 
  <caching-schemes>
    <distributed-scheme>
      <scheme-name>ExamplesPartitionedPofScheme</scheme-name>
      <service-name>PartitionedPofCache</service-name>
      <backing-map-scheme>
        <local-scheme>
          <!-- each node will be limited to 32MB -->
          <high-units>32M</high-units>
          <unit-calculator>binary</unit-calculator>
        </local-scheme>
      </backing-map-scheme>
      <autostart>true</autostart>
    </distributed-scheme>
 
    <!--
    Proxy Service scheme that allows remote clients to connect to the
    cluster over TCP/IP.
    -->
    <proxy-scheme>
      <scheme-name>secure-proxy</scheme-name>
      <service-name>ProxyService</service-name>
 
      <thread-count system-property="tangosol.coherence.extend.threads">2</thread-count>
 
      <acceptor-config>
        <tcp-acceptor>
          <local-address>
            <address system-property="tangosol.coherence.extend.address">localhost</address>
            <port system-property="tangosol.coherence.extend.port">9099</port>
            <reusable>true</reusable>
          </local-address>
        </tcp-acceptor>
      </acceptor-config>
 
       <autostart system-property="tangosol.coherence.extend.enabled">false</autostart>
    </proxy-scheme>
  </caching-schemes>
</cache-config>

10.2.8 Create a Start-Up File for a Cache Server

Create a start-up file for a cache server cluster node. The start-up file must include the system properties to designate a proxy service and the cluster-side cache configuration file. You must also include the application class files and the XML configuration files on the classpath.

To create a start-up file for a cache server:

  1. Create a start-up file in JDeveloper. Right-click the Security project and select New. In the New Gallery, select General in the Categories field and File in the Items field. In the New File dialog, enter security-cache-server.cmd as the file name and save it in the Coherence \bin directory (in this case, C:\oracle\product\coherence\bin).

  2. Enter the commands to start the cache server. The key commands include:

    • the tangosol.coherence.cacheconfig system property to indicate the path to the cluster-side cache configuration file (in this case, examples-cache-config.xml)

    • the classpath defined to include the path to coherence.jar (C:\oracle\product\lib\coherence.jar), the Security project classes (C:\home\oracle\labs\Security\classes), and the XML configuration files (C:\home\oracle\labs)

    Note:

    Ensure that the XML configuration files (under C:\home\oracle\labs) appear before coherence.jar on the classpath. The classloader must encounter the custom POF configuration file (which you will create later in this chapter) before it references the one in coherence.jar.

Example 10-8 illustrates a possible implementation for security-cache-server.cmd.

Example 10-8 Start-Up File for a Cache Server

@echo off
@
@rem This will start a cache server
@
setlocal
 
:config
@rem specify the Coherence installation directory
set coherence_home=c:\oracle\product\coherence
 
@rem specify the JVM heap size
set memory=512m
 
 
:start
if not exist "%coherence_home%\lib\coherence.jar" goto instructions
 
if "%java_home%"=="" (set java_exec=java) else (set java_exec=%java_home%\bin\java)
 
 
:launch
 
 
set COH_OPTS=%COH_OPTS% -Dtangosol.coherence.cacheconfig=\home\oracle\labs\examples-cache-config.xml
 
"%java_exec%" -server -showversion %COH_OPTS% -cp C:\home\oracle\labs;"%coherence_home%\lib\coherence.jar";C:\home\oracle\labs\Security\classes -Xms128m -Xmx128m com.tangosol.net.DefaultCacheServer  %2 %3 %4 %5 %6 %7
 
goto exit
 
:instructions
 
echo Usage:
echo   ^<coherence_home^>\bin\cache-server.cmd
goto exit
 
:exit
endlocal
@echo on

10.2.9 Create a Start-Up File for a Cache Server with a Proxy Service

Create a file to start an extend proxy service on a cache server in the cluster. The extend client will connect to this service. The start-up file must include the system properties to designate a proxy service and the cluster-side cache configuration file. You must also include the application class files and the XML configuration files on the classpath.

For these examples, the cache server with proxy service start-up file will have the same configuration as the cache server start-up file, but it will include the system property to enable the extend proxy.

To create a start-up file for a cache server with a proxy service:

  1. Create a start-up file in JDeveloper. Right-click the Security project and select New. In the New Gallery, select General in the Categories field and File in the Items field. In the New File dialog, enter security-run-proxy.cmd as the file name and save it in the Coherence \bin directory. (in this case, C:\oracle\product\coherence\bin).

  2. Enter the commands to start the cache server. The key commands include:

    • the tangosol.coherence.extend.enabled system property to designate a proxy service

    • the tangosol.coherence.cacheconfig system property to indicate the path to the cluster-side cache configuration file (in this case, examples-cache-config.xml)

    • the classpath defined to include the path to coherence.jar (C:\oracle\product\lib\coherence.jar), the Security classes (C:\home\oracle\labs\Security\classes), and the XML configuration files (C:\home\oracle\labs)

Note:

Ensure that the XML configuration files (under C:\home\oracle\labs) appear before coherence.jar on the classpath. The classloader must encounter the custom POF configuration file (which you will create later in this chapter) before it references the one in coherence.jar.

Example 10-9 illustrates a possible implementation of the cluster-side cache server with proxy service start-up file, security-run-proxy.cmd.

Example 10-9 Start-Up File for a Cache Server with Proxy Service

@echo off
@
@REM this will start a proxy cache server
@
setlocal
 
:config
 
@rem specify the Coherence installation directory
set coherence_home=c:\oracle\product\coherence
 
@rem specify the JVM heap size
set memory=512m
 
:start
if not exist "%coherence_home%\lib\coherence.jar" goto instructions
 
if "%java_home%"=="" (set java_exec=java) else (set java_exec=%java_home%\bin\java)
 
 
:launch
 
set COH_OPTS=%COH_OPTS% -Dtangosol.coherence.extend.enabled=true -Dtangosol.coherence.cacheconfig=\home\oracle\labs\examples-cache-config.xml
 
"%JAVA_HOME%"\bin\java -server -showversion %COH_OPTS% -cp C:\home\oracle\labs;"%coherence_home%\lib\coherence.jar";C:\home\oracle\labs\Security\classes -Xms128m -Xmx128m com.tangosol.net.DefaultCacheServer %2 %3 %4 %5 %6 %7
 
goto exit
 
:instructions
 
echo Usage:
echo   ^<coherence_home^>\bin\security-run-proxy.cmd
goto exit
 
:exit
endlocal

10.2.10 Configure Project Classpaths and Runtime Properties

Configure the classpath and run-time properties for the project.

  1. In JDeveloper, compile the Java files in the project. Right-click the Security project and select Make Security.jpr.

  2. Set the Java run-time options for the project.

    Right-click the Security project and select Project Properties. Select Run\Debug\Profile and edit the Default run configuration. In the Java Options field, enter the system properties to disable local storage and to identify the client-side cache configuration file client-cache-config.xml.

    -Dtangosol.coherence.distributed.localstorage=false -Dtangosol.coherence.cacheconfig=\home\oracle\labs\client-cache-config.xml
    

    Click OK to dismiss the Edit Run configuration window.

  3. Set the libraries and classpaths for the project. In the Project Properties window, click Libraries and Classpaths. Add the coherence.jar, the Security project Java classes (C:\home\oracle\labs\Security\classes), and the directory containing the XML configuration files (C:\home\oracle\labs) to the project classpath.

    Note:

    Ensure that the XML configuration files (under C:\home\oracle\labs) appear before coherence.jar on the classpath. The classloader must encounter the custom POF configuration file (which you will create later in this chapter) before it references the one in coherence.jar.

    The Libraries and Classpath window should look similar to Figure 10-1.

    See "Changing Project Properties, Setting Runtime Configuration" if you need detailed information on setting libraries and classpaths.

    Figure 10-1 Libraries and Classpath for the Security Project

    Libraries and classpaths for the Security project.
  4. Click OK to dismiss the Libraries and Classpath dialog box, then OK to dismiss the Project Properties dialog box.

  5. Save and compile the project. Right-click the project and select Make Security.jpr.

10.2.11 Run the Password Example

Run the password example to generate and validate the token, and pass it to the proxy service.

  1. Compile the files in the Security project if you have not already done so.

  2. Stop any running cache servers. Open a command prompt, and run the proxy server security-run.proxy.cmd.

  3. Open another command prompt and run the cache server security-cache-server.cmd.

  4. Run the password example: right-click PasswordExample.java and select Run.

You should see output similar to Example 10-10 in the JDeveloper Log window. The output indicates the start of the password example, the opening of the socket to the proxy server, and the completion of the example.

Example 10-10 Password Example Output in the JDeveloper Log Window

...
------password example begins------
2010-06-09 11:50:49.220/0.344 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational configuration from "jar:file:/C:/oracle/product/coherence_3.5/lib/coherence.jar!/tangosol-coherence.xml"
2010-06-09 11:50:49.220/0.344 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational overrides from "jar:file:/C:/oracle/product/coherence_3.5/lib/coherence.jar!/tangosol-coherence-override-dev.xml"
2010-06-09 11:50:49.220/0.344 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational overrides from "file:/C:/home/oracle/labs/tangosol-coherence-override.xml"
2010-06-09 11:50:49.235/0.359 Oracle Coherence 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" is not specified
 
Oracle Coherence Version 3.6.0.0 DPR3 Build 16141
 Grid Edition: Development mode
Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
 
2010-06-09 11:50:49.470/0.594 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded cache configuration from "file:/C:/home/oracle/labs/client-cache-config.xml"
2010-06-09 11:50:49.673/0.797 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Loaded POF configuration from "jar:file:/C:/oracle/product/coherence_3.5/lib/coherence.jar!/pof-config.xml"
2010-06-09 11:50:49.688/0.812 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Loaded included POF configuration from "jar:file:/C:/oracle/product/coherence_3.5/lib/coherence.jar!/coherence-pof-config.xml"
2010-06-09 11:50:49.782/0.906 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Started: TcpInitiator{Name=ExtendTcpCacheService:TcpInitiator, State=(SERVICE_STARTED), ThreadCount=0, Codec=Codec(Format=POF), Serializer=com.tangosol.io.pof.ConfigurablePofContext, PingInterval=0, PingTimeout=0, RequestTimeout=0, ConnectTimeout=0, SocketProvider=SystemSocketProvider, RemoteAddresses=[hostname/130.35.99.213:9099], KeepAliveEnabled=true, TcpDelayEnabled=false, ReceiveBufferSize=0, SendBufferSize=0, LingerTimeout=-1}
2010-06-09 11:50:49.798/0.922 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Opening Socket connection to 130.35.99.213:9099
2010-06-09 11:50:49.798/0.922 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Connected to 130.35.99.213:9099
------password example succeeded------
------password example completed------
Process exited with exit code 0.

You should see a response in the proxy server shell similar to Example 10-11. It lists the CN and OU values from the distinguished name and whether the password was validated.

Example 10-11 Response from the Cache Server Running the Proxy Service Shell

...
2010-06-09 11:50:39.829/2.781 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=2):
Services
  (
  ClusterService{Name=Cluster, State=(SERVICE_STARTED, STATE_JOINED), Id=0, Version=3.6, OldestMemberId=1}
  InvocationService{Name=Management, State=(SERVICE_STARTED), Id=1, Version=3.1, OldestMemberId=6}
  PartitionedCache{Name=PartitionedPofCache, State=(SERVICE_STARTED), LocalStorage=enabled, PartitionCount=257, BackupCount=1, AssignedPartitions=128, BackupPartitions=129}
  ProxyService{Name=ProxyService, State=(SERVICE_STARTED), Id=7, Version=3.2, OldestMemberId=6}
  )

Started DefaultCacheServer...

Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne

10.3 Including Role-Based Access Control to the Cluster

This section describes how to create an example that employs role-based policies to access the cluster. The code will log in to get a Subject with a user-ID assigned to a particular role. It will get a cache reference running in the context of the Subject, and then attempt various cache operations. Depending on the role granted to the user, the cache operation is allowed or denied. Note that the role mapping and role-based authorization in the example is simplified and not intended for real security use.

For example, a user with a "writer" role is allowed to put and get. A user with a "reader" role can get, but not put. A user with a "writer" role cannot destroy a cache. However, a user with an "admin" role can destroy a cache.

Note that when the cache reference is created in the context of a Subject, that identity is permanently associated with that reference. Any use of that cache reference is on behalf of that identity.

The example will use the PasswordIdentityTransformer and PasswordIdentityAsserter classes that you created in the previous section. The PasswordIdentityTransformer will generate a security token that contains the password, the user ID, and the roles. The PasswordIdentityAsserter (running in the proxy) will validate the security token to enforce the password, and construct a Subject with the proper user ID and roles. The production and assertion of the security token happens automatically.

Follow these steps to create the example:

  1. Define which User Roles are Entitled to Access Cache Methods

  2. Apply the Entitlements to the Cache Service

  3. Create the Access Control Example Program

  4. Edit the Cluster-Side Cache Configuration File

  5. Run the Access Control Example

10.3.1 Define which User Roles are Entitled to Access Cache Methods

Create a Java file that allows access to cache methods on the basis of a user's role. To do this, you can apply access permissions to a wrapped NamedCache using the Subject passed from the client by using Coherence*Extend. The implementation will allow only clients with a specified role to access the wrapped NamedCache.

The class that you create in this section should extend the com.tangosol.net.cache.WrapperNamedCache class. This class is a convenience function that enables you to secure the methods on the NamedCache interface.

To determine which user role can access a cache method, include a call to SecurityExampleHelper.checkAccess in each cache method's implementation. As the argument to checkAccess, provide the user role that is permitted to access the method. Close the implementation with a call to super.

For example, the following code indicates that users with the admin role can destroy the cache.

    public void destroy()
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_ADMIN);
        super.destroy();
        }

In this example, users with the reader role can call the aggregate method.

    public Object aggregate(Filter filter, EntryAggregator agent)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        return super.aggregate(filter, agent);
        }

To create a file that determines which user role can call cache methods:

  1. Create a new Java class named EntitledNamedCache in the Security project.

    See "Creating a Java Class" if you need detailed information.

  2. Ensure that the class imports and extends WrapperNamedCache.

  3. Import the Filter, MapListener, ValueExtractor, Collection, Comparator, Map, and Set classes. The methods in WrapperNamedCache (and by extension, EntitledNamedCache) use arguments with these types.

  4. Implement the methods in EntitledNamedCache such that only a user with a specific role can call the method.

Example 10-12 illustrates a possible implementation of EntitledNamedCache.

Example 10-12 Entitled Named Cache

package com.oracle.handson;
 
 
import com.tangosol.net.NamedCache;
 
import com.tangosol.net.cache.WrapperNamedCache;
 
 
import com.tangosol.util.Filter;
import com.tangosol.util.MapListener;
import com.tangosol.util.ValueExtractor;
 
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
 
 
/**
* Example WrapperNamedCache that demonstrates how entitlements can be applied
* to a wrapped NamedCache using the Subject passed from the client through
* Coherence*Extend. This implementation only allows clients with a specified
* role to access the wrapped NamedCache.
*
*/
public class EntitledNamedCache
        extends WrapperNamedCache
    {
    /**
    * Create a new EntitledNamedCache.
    *
    * @param cache  the wrapped NamedCache
    */
    public EntitledNamedCache(NamedCache cache)
        {
        super(cache, cache.getCacheName());
        }
 
 
    // ----- NamedCache interface -------------------------------------------
 
    /**
    * {@inheritDoc}
    */
    public void release()
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        super.release();
        }
 
    /**
    * {@inheritDoc}
    */
    public void destroy()
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_ADMIN);
        super.destroy();
        }
 
    /**
    * {@inheritDoc}
    */
    public Object put(Object oKey, Object oValue, long cMillis)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        return super.put(oKey, oValue, cMillis);
        }
 
    /**
    * {@inheritDoc}
    */
    public void addMapListener(MapListener listener)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        super.addMapListener(listener);
        }
 
    /**
    * {@inheritDoc}
    */
    public void removeMapListener(MapListener listener)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        super.removeMapListener(listener);
        }
 
    /**
    * {@inheritDoc}
    */
    public void addMapListener(MapListener listener, Object oKey, boolean fLite)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        super.addMapListener(listener, oKey, fLite);
        }
 
    /**
    * {@inheritDoc}
    */
    public void removeMapListener(MapListener listener, Object oKey)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        super.removeMapListener(listener, oKey);
        }
 
    /**
    * {@inheritDoc}
    */
    public void addMapListener(MapListener listener, Filter filter, boolean fLite)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        super.addMapListener(listener, filter, fLite);
        }
 
    /**
    * {@inheritDoc}
    */
    public void removeMapListener(MapListener listener, Filter filter)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        super.removeMapListener(listener, filter);
        }
 
    /**
    * {@inheritDoc}
    */
    public int size()
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        return super.size();
        }
 
    /**
    * {@inheritDoc}
    */
    public void clear()
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        super.clear();
        }
 
    /**
    * {@inheritDoc}
    */
    public boolean isEmpty()
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        return super.isEmpty();
        }
 
    /**
    * {@inheritDoc}
    */
    public boolean containsKey(Object oKey)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        return super.containsKey(oKey);
        }
 
    /**
    * {@inheritDoc}
    */
    public boolean containsValue(Object oValue)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        return super.containsValue(oValue);
        }
 
    /**
    * {@inheritDoc}
    */
    public Collection values()
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        return super.values();
        }
 
    /**
    * {@inheritDoc}
    */
    public void putAll(Map map)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        super.putAll(map);
        }
 
    /**
    * {@inheritDoc}
    */
    public Set entrySet()
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        return super.entrySet();
        }
 
    /**
    * {@inheritDoc}
    */
    public Set keySet()
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        return super.keySet();
        }
 
    /**
    * {@inheritDoc}
    */
    public Object get(Object oKey)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        return super.get(oKey);
        }
 
    /**
    * {@inheritDoc}
    */
    public Object remove(Object oKey)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        return super.remove(oKey);
        }
 
    /**
    * {@inheritDoc}
    */
    public Object put(Object oKey, Object oValue)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        return super.put(oKey, oValue);
        }
 
    /**
    * {@inheritDoc}
    */
    public Map getAll(Collection colKeys)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        return super.getAll(colKeys);
        }
 
    /**
    * {@inheritDoc}
    */
    public boolean lock(Object oKey, long cWait)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        return super.lock(oKey, cWait);
        }
 
    /**
    * {@inheritDoc}
    */
    public boolean lock(Object oKey)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        return super.lock(oKey);
        }
 
    /**
    * {@inheritDoc}
    */
    public boolean unlock(Object oKey)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        return super.unlock(oKey);
        }
 
    /**
    * {@inheritDoc}
    */
    public Set keySet(Filter filter)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        return super.keySet(filter);
        }
 
    /**
    * {@inheritDoc}
    */
    public Set entrySet(Filter filter)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        return super.entrySet(filter);
        }
 
    /**
    * {@inheritDoc}
    */
    public Set entrySet(Filter filter, Comparator comparator)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        return super.entrySet(filter, comparator);
        }
 
    /**
    * {@inheritDoc}
    */
    public void addIndex(ValueExtractor extractor, boolean fOrdered, Comparator comparator)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        super.addIndex(extractor, fOrdered, comparator);
        }
 
    /**
    * {@inheritDoc}
    */
    public void removeIndex(ValueExtractor extractor)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        super.removeIndex(extractor);
        }
 
    /**
    * {@inheritDoc}
    */
    public Object invoke(Object oKey, EntryProcessor agent)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        return super.invoke(oKey, agent);
        }
 
    /**
    * {@inheritDoc}
    */
    public Map invokeAll(Collection collKeys, EntryProcessor agent)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        return super.invokeAll(collKeys, agent);
        }
 
    /**
    * {@inheritDoc}
    */
    public Map invokeAll(Filter filter, EntryProcessor agent)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        return super.invokeAll(filter, agent);
        }
 
    /**
    * {@inheritDoc}
    */
    public Object aggregate(Collection collKeys, EntryAggregator agent)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        return super.aggregate(collKeys, agent);
        }
 
    /**
    * {@inheritDoc}
    */
    public Object aggregate(Filter filter, EntryAggregator agent)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        return super.aggregate(filter, agent);
        }
 
 
    // ----- helper methods -------------------------------------------------
 
    /**
    * Return the wrapped NamedCache.
    *
    * @return  the wrapped CacheService
    */
    public NamedCache getNamedCache()
        {
        return (NamedCache) getMap();
        }
    }

10.3.2 Apply the Entitlements to the Cache Service

Create a file that demonstrates how access entitlements can be applied to a wrapped CacheService using the Subject passed from the client through Coherence*Extend. The implementation should delegate access control for cache operations to the EntitledNamedCache you created in the previous section.

The class that you create should extend com.tangosol.net.WrapperCacheService. This is a convenience function that allows you to secure the methods on the CacheService. It also provides a mechanism to delegate between the cache service on the proxy and the client request.

Implement the methods ensureCache, releaseCache, and destroyCache such that only users with specific roles can use them. This will include a call to SecurityExampleHelper.checkAccess with a specific user role as its argument. For example, the following code ensures that only users with role admin can destroy the cache.

    public void destroyCache(NamedCache map)
        {
        if (map instanceof EntitledNamedCache)
            {
            EntitledNamedCache cache =  (EntitledNamedCache) map;
            SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_ADMIN);
            map = cache.getNamedCache();
            }
        super.destroyCache(map);
        }

To create a file that applies entitlements to access the cache service:

  1. Create a new Java class named EntitledCacheService in the Security project.

  2. Ensure that the class imports and extends WrapperCacheService.

  3. Implement the ensureCache, releaseCache, and destroyCache methods such that only users with specific roles can use them.

Example 10-13 illustrates a possible implementation of EntitledCacheService.

Example 10-13 Entitled Cache Service

package com.oracle.handson;
 
 
import com.tangosol.net.CacheService;
import com.tangosol.net.NamedCache;
import com.tangosol.net.WrapperCacheService;
 
 
/**
* Example WrapperCacheService that demonstrates how entitlements can be
* applied to a wrapped CacheService using the Subject passed from the
* client through Coherence*Extend. This implementation delegates access control
* for cache operations to the EntitledNamedCache.
*
*/
public class EntitledCacheService
        extends WrapperCacheService
    {
    /**
    * Create a new EntitledCacheService.
    *
    * @param service     the wrapped CacheService
    */
    public EntitledCacheService(CacheService service)
        {
        super(service);
        }
 
 
    // ----- CacheService interface -----------------------------------------
 
    /**
    * {@inheritDoc}
    */
    public NamedCache ensureCache(String sName, ClassLoader loader)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
        return new EntitledNamedCache(super.ensureCache(sName, loader));
        }
 
    /**
    * {@inheritDoc}
    */
    public void releaseCache(NamedCache map)
        {
        if (map instanceof EntitledNamedCache)
            {
            EntitledNamedCache cache =  (EntitledNamedCache) map;
            SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
            map = cache.getNamedCache();
            }
        super.releaseCache(map);
        }
 
    /**
    * {@inheritDoc}
    */
    public void destroyCache(NamedCache map)
        {
        if (map instanceof EntitledNamedCache)
            {
            EntitledNamedCache cache =  (EntitledNamedCache) map;
            SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_ADMIN);
            map = cache.getNamedCache();
            }
        super.destroyCache(map);
        }
    }

10.3.3 Create the Access Control Example Program

Create a file to run the access control example. The role policies are defined in SecurityExampleHelper. The EntitledCacheService and EntitledNamedCache files enforce the policies.

The program should specify various users as arguments to SecurityHelperFile.login, and then attempt to perform cache read, write, and destroy operations. Based on the entitlement policies defined in EntitledCacheService and EntitledNamedCache the operations should succeed or fail.

  1. Create a new Java class with a main method in the Security project named AccessControlExample.

    See "Creating a Java Class" if you need detailed information.

  2. Implement the main to access the cache.

  3. Specify users defined in the SecurityExampleHelper file as arguments to its login method.

  4. For each user, try to execute read (get), write (put), and destroy operations on the cache and provide "Success" or "Failure" messages in response.

Example 10-14 illustrates a possible implementation of AccessControlExample.java.

Example 10-14 Sample Program to Run the Access Control Example

package com.oracle.handson;
 
 
import com.tangosol.net.CacheFactory;
import com.tangosol.net.InvocationService;
import com.tangosol.net.NamedCache;
import java.security.PrivilegedExceptionAction;
 
import java.util.Map;
 
import javax.security.auth.Subject;
 
 
/**
* This class demonstrates simplified role based access control.
* <p>
* The role policies are defined in SecurityExampleHelper. Enforcmenent
* is done by EntitledCacheService and EntitledNamedCache.
*
*/
public class AccessControlExample
    {
    // ----- static methods -------------------------------------------------
 
     public static void main (String[] args){
           accessCache();
             }
    /**
    * Demonstrate role based access to the cache.
    */
    public static void accessCache()
        {
        System.out.println("------cache access control example begins------");
 
        Subject subject = SecurityExampleHelper.login("JohnWhorfin");
 
        // Someone with writer role can write and read
        try
            {
            NamedCache cache = (NamedCache) Subject.doAs(
                    subject, new PrivilegedExceptionAction()
                {
                public Object run()
                        throws Exception
                    {
                    return CacheFactory.getCache(SecurityExampleHelper.SECURITY_CACHE_NAME);
                    }
                });
            cache.put("myKey", "myValue");
            cache.get("myKey");
            System.out.println("    Success: read and write allowed");
            }
        catch (Exception e)
            {
            // get exception if not allowed to perform the operation
            e.printStackTrace();
            }
 
        // Someone with reader role can read but not write
        subject = SecurityExampleHelper.login("JohnBigboote");
        try
            {
            NamedCache cache = (NamedCache) Subject.doAs(
                    subject, new PrivilegedExceptionAction()
                {
                public Object run()
                        throws Exception
                    {
                    return CacheFactory.getCache(SecurityExampleHelper.SECURITY_CACHE_NAME);
                    }
                });
            cache.get("myKey");
            System.out.println("    Success: read allowed");
            cache.put("anotherKey", "anotherValue");
            }
        catch (Exception e)
            {
            // get exception if not allowed to perform the operation
            System.out.println("    Success: Correctly cannot write");
            }
 
        // Someone with writer role cannot call destroy
        subject = SecurityExampleHelper.login("JohnWhorfin");
        try
            {
            NamedCache cache = (NamedCache) Subject.doAs(
                    subject, new PrivilegedExceptionAction()
                {
                public Object run()
                        throws Exception
                    {
                    return CacheFactory.getCache(SecurityExampleHelper.SECURITY_CACHE_NAME);
                    }
                });
            cache.destroy();
            }
        catch (Exception e)
            {
            // get exception if not allowed to perform the operation
            System.out.println("    Success: Correctly cannot " +
                    "destroy the cache");
            }
 
        // Someone with admin role can call destroy
        subject = SecurityExampleHelper.login("BuckarooBanzai");
        try
            {
            NamedCache cache = (NamedCache) Subject.doAs(
                    subject, new PrivilegedExceptionAction()
                {
                public Object run()
                        throws Exception
                    {
                    return CacheFactory.getCache(SecurityExampleHelper.SECURITY_CACHE_NAME);
                    }
                });
            cache.destroy();
            System.out.println("    Success: Correctly allowed to " +
                    "destroy the cache");
            }
        catch (Exception e)
            {
            // get exception if not allowed to perform the operation
            e.printStackTrace();
            }
        System.out.println("------cache access control example completed------");
        } 
    }

10.3.4 Edit the Cluster-Side Cache Configuration File

Edit the cluster-side cache configuration file examples-cache-config.xml. Specify the full path of the class name of the cache service in the cache-service-proxy stanza under proxy-config. The cache-service-proxy stanza contains the configuration information for a cache service proxy managed by a proxy service.

In this case, the cache service class name is com.oracle.handson.EntitledCacheService proxy and the param-type should be com.tangosol.net.CacheService.

Example 10-15 illustrates the XML code to add to the configuration.

Example 10-15 Cache Service Proxy Configuration for a Cluster-Side Cache Configuration

...
      <proxy-config>
        <cache-service-proxy>
          <class-name>com.oracle.handson.EntitledCacheService</class-name>
          <init-params>
            <init-param>
              <param-type>com.tangosol.net.CacheService</param-type>
              <param-value>{service}</param-value>
            </init-param>
         </init-params>
        </cache-service-proxy>
      </proxy-config>
...

10.3.5 Run the Access Control Example

Run the access control example to demonstrate how access to the cache can be granted or denied, based on a user's role.

  1. Compile the files in the Security project if you have not done so already.

  2. Stop any running cache servers. Start the cache server running the proxy service with security-run-proxy.cmd.

  3. Start the cache server in the cluster with security-cache-server.cmd.

  4. Right-click the AccessControlExample.java file and select Run.

You should see output similar to Example 10-16 in the JDeveloper Log window. The messages correspond to the various users specified in AccessControlExample trying to execute read, write, and destroy operations on the cache.

  • the Success: read and write allowed message corresponds to user with role writer attempting to read from and write to the cache

  • the Success: read allowed message corresponds to the user with role reader attempting to read from the cache

  • the Success: Correctly cannot write message corresponds to the user with role reader attempting to write to the cache

  • the Success: Correctly cannot destroy the cache message corresponds to the user with role writer attempting to destroy the cache

  • the Success: Correctly allowed to destroy the cache message corresponds to the user with role admin attempting to destroy the cache

Example 10-16 Access Control Example Output in the JDeveloper Log Window

...
------cache access control example begins------
2010-06-10 17:07:51.500/0.344 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational configuration from "jar:file:/C:/oracle/product0529/coherence_3.5/lib/coherence.jar!/tangosol-coherence.xml"
2010-06-10 17:07:51.500/0.344 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational overrides from "jar:file:/C:/oracle/product0529/coherence_3.5/lib/coherence.jar!/tangosol-coherence-override-dev.xml"
2010-06-10 17:07:51.500/0.344 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational overrides from "file:/C:/home/oracle/labs/tangosol-coherence-override.xml"
2010-06-10 17:07:51.500/0.344 Oracle Coherence 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" is not specified
 
Oracle Coherence Version 3.6.0.0 DPR3 Build 16141
 Grid Edition: Development mode
Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
 
2010-06-10 17:07:51.703/0.547 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded cache configuration from "file:/C:/home/oracle/labs/client-cache-config.xml"
2010-06-10 17:07:51.906/0.750 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Loaded POF configuration from "jar:file:/C:/oracle/product0529/coherence_3.5/lib/coherence.jar!/pof-config.xml"
2010-06-10 17:07:51.906/0.750 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Loaded included POF configuration from "jar:file:/C:/oracle/product0529/coherence_3.5/lib/coherence.jar!/coherence-pof-config.xml"
2010-06-10 17:07:52.015/0.859 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Started: TcpInitiator{Name=ExtendTcpCacheService:TcpInitiator, State=(SERVICE_STARTED), ThreadCount=0, Codec=Codec(Format=POF), Serializer=com.tangosol.io.pof.ConfigurablePofContext, PingInterval=0, PingTimeout=0, RequestTimeout=0, ConnectTimeout=0, SocketProvider=SystemSocketProvider, RemoteAddresses=[tpfaeffl-lap7/130.35.99.213:9099], KeepAliveEnabled=true, TcpDelayEnabled=false, ReceiveBufferSize=0, SendBufferSize=0, LingerTimeout=-1}
2010-06-10 17:07:52.031/0.875 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Opening Socket connection to 130.35.99.213:9099
2010-06-10 17:07:52.031/0.875 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Connected to 130.35.99.213:9099
    Success: read and write allowed
2010-06-10 17:07:52.125/0.969 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Started: TcpInitiator{Name=ExtendTcpCacheService:TcpInitiator, State=(SERVICE_STARTED), ThreadCount=0, Codec=Codec(Format=POF), Serializer=com.tangosol.io.pof.ConfigurablePofContext, PingInterval=0, PingTimeout=0, RequestTimeout=0, ConnectTimeout=0, SocketProvider=SystemSocketProvider, RemoteAddresses=[tpfaeffl-lap7/130.35.99.213:9099], KeepAliveEnabled=true, TcpDelayEnabled=false, ReceiveBufferSize=0, SendBufferSize=0, LingerTimeout=-1}
2010-06-10 17:07:52.125/0.969 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Opening Socket connection to 130.35.99.213:9099
2010-06-10 17:07:52.125/0.969 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Connected to 130.35.99.213:9099
    Success: read allowed
    Success: Correctly cannot write
    Success: Correctly cannot destroy the cache
2010-06-10 17:07:52.187/1.031 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=ExtendTcpCacheService:TcpInitiator, member=n/a): Started: TcpInitiator{Name=ExtendTcpCacheService:TcpInitiator, State=(SERVICE_STARTED), ThreadCount=0, Codec=Codec(Format=POF), Serializer=com.tangosol.io.pof.ConfigurablePofContext, PingInterval=0, PingTimeout=0, RequestTimeout=0, ConnectTimeout=0, SocketProvider=SystemSocketProvider, RemoteAddresses=[tpfaeffl-lap7/130.35.99.213:9099], KeepAliveEnabled=true, TcpDelayEnabled=false, ReceiveBufferSize=0, SendBufferSize=0, LingerTimeout=-1}
2010-06-10 17:07:52.187/1.031 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Opening Socket connection to 130.35.99.213:9099
2010-06-10 17:07:52.187/1.031 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Connected to 130.35.99.213:9099
    Success: Correctly allowed to destroy the cache
------cache access control example completed------
Process exited with exit code 0.

Example 10-17 lists the output in the shell where the cache server is running the proxy service. Notice that the security exceptions in the output, correspond to the Success: Correctly cannot write and Success: Correctly cannot destroy the cache messages in the JDeveloper Log window.

Example 10-17 Output for the Cache Server Running the Proxy Service

2010-06-10 17:07:25.468/18.156 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=Cluster, member=1): Member 2 joined Service ProxyService with senior member 1
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
Password validated for user: role_writer
Password validated for user: role_writer
Password validated for user: role_writer
Password validated for user: role_reader
Password validated for user: role_reader
Password validated for user: role_reader
2010-06-10 17:07:52.140/44.828 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=Proxy:ProxyService:TcpAcceptorWorker:0, member=1): An exception occurred while processing a PutRequest for Service=Proxy:ProxyService:TcpAcceptor: java.lang.SecurityException: Access denied, insufficient privileges
        at com.oracle.handson.SecurityExampleHelper.checkAccess(SecurityExampleHelper.java:91)
        at com.oracle.handson.EntitledNamedCache.put(EntitledNamedCache.java:66)
        at com.tangosol.coherence.component.net.extend.proxy.NamedCacheProxy.put$Router(NamedCacheProxy.CDB:1)
        at com.tangosol.coherence.component.net.extend.proxy.NamedCacheProxy.put(NamedCacheProxy.CDB:2)
        at com.tangosol.coherence.component.net.extend.messageFactory.NamedCacheFactory$PutRequest.onRun(NamedCacheFactory.CDB:6)
        at com.tangosol.coherence.component.net.extend.message.Request.run(Request.CDB:4)
        at com.tangosol.coherence.component.net.extend.proxy.NamedCacheProxy.onMessage(NamedCacheProxy.CDB:11)
        at com.tangosol.coherence.component.net.extend.Channel$MessageAction.run(Channel.CDB:13)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAs(Subject.java:337)
        at com.tangosol.coherence.component.net.extend.Channel.execute(Channel.CDB:29)
        at com.tangosol.coherence.component.net.extend.Channel.receive(Channel.CDB:26)
        at com.tangosol.coherence.component.util.daemon.queueProcessor.service.Peer$DaemonPool$WrapperTask.run(Peer.CDB:9)
        at com.tangosol.coherence.component.util.DaemonPool$WrapperTask.run(DaemonPool.CDB:32)
        at com.tangosol.coherence.component.util.DaemonPool$Daemon.onNotify(DaemonPool.CDB:63)
        at com.tangosol.coherence.component.util.Daemon.run(Daemon.CDB:42)
        at java.lang.Thread.run(Thread.java:619)
 
2010-06-10 17:07:52.140/44.828 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=Proxy:ProxyService:TcpAcceptorWorker:1, member=1): An exception occurred while processing a DestroyCacheRequest for Service=Proxy:ProxyService:TcpAcceptor: java.lang.SecurityException: Access denied, insufficient privileges
 
        at com.oracle.handson.SecurityExampleHelper.checkAccess(SecurityExampleHelper.java:91)
        at com.oracle.handson.EntitledCacheService.destroyCache(EntitledCacheService.java:64)
        at com.tangosol.coherence.component.net.extend.messageFactory.CacheServiceFactory$DestroyCacheRequest.onRun(CacheServiceFactory.CDB:6)
        at com.tangosol.coherence.component.net.extend.message.Request.run(Request.CDB:4)
        at com.tangosol.coherence.component.net.extend.proxy.serviceProxy.CacheServiceProxy.onMessage(CacheServiceProxy.CDB:9)
        at com.tangosol.coherence.component.net.extend.Channel$MessageAction.run(Channel.CDB:13)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAs(Subject.java:337)
        at com.tangosol.coherence.component.net.extend.Channel.execute(Channel.CDB:29)
        at com.tangosol.coherence.component.net.extend.Channel.receive(Channel.CDB:26)
        at com.tangosol.coherence.component.util.daemon.queueProcessor.service.Peer$DaemonPool$WrapperTask.run(Peer.CDB:9)
        at com.tangosol.coherence.component.util.DaemonPool$WrapperTask.run(DaemonPool.CDB:32)
        at com.tangosol.coherence.component.util.DaemonPool$Daemon.onNotify(DaemonPool.CDB:63)
        at com.tangosol.coherence.component.util.Daemon.run(Daemon.CDB:42)
        at java.lang.Thread.run(Thread.java:619)
 
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne

10.4 Including Role-Based Access Control to an Invocable Object

An invocation service cluster service allows extend clients to execute invocable objects on the cluster. This example demonstrates how you can use role-based policies to determine which users can run the invocable objects.

For example, a user with a "writer" role is allowed to run an invocable object. A user with a "reader" role may not.

In this example you will create a simple invocable object that can be called from a client program. Since it will be serializable, you must also list it in a POF configuration file. You will also create a invocation service program that will test whether a user can execute methods on the service based on the user's role.

As in the previous example, this example will use PasswordIdentityTransformer to generate a security token that contains the password, the user ID, and the roles. The PasswordIdentityAsserter (running in the proxy) will be used to validate the security token to enforce the password, and construct a Subject with the proper user ID and roles. The production and assertion of the security token happens automatically.

Follow these steps to create the example:

  1. Create an Invocable Object

  2. Create an Entitled Invocation Service

  3. Create the Access Invocation Service Example Program

  4. Edit the Cluster-Side Cache Configuration File

  5. Create a POF Configuration File

  6. Run the Access Invocation Service Example

10.4.1 Create an Invocable Object

Create an implementation of a simple invocable object that will be used by an entitled Invocation Service. For example, the invocable object can be written to increment and return an integer.

To create an invocable object:

  1. Create a new Java class named ExampleInvocable in the Security project.

    See "Creating a Java Class" if you need detailed information.

  2. Import the Invocable and InvocationService interfaces. Since this class will be working with serializable objects, also import PortableObject, PofReader and PofWriter.

  3. Ensure that the ExampleInvocable class implements Invocable and PortableObject.

  4. Implement ExampleInvocable to increment an integer and return the result.

  5. Implement the PofReader.readExternal and PofWriter.writeExternal methods.

Example 10-18 illustrates a possible implementation of ExampleInvocable.java.

Example 10-18 A Sample Invocable Object

package com.oracle.handson;
 
 
import com.tangosol.io.pof.PortableObject;
import com.tangosol.io.pof.PofReader;
import com.tangosol.io.pof.PofWriter;
 
import com.tangosol.net.Invocable;
import com.tangosol.net.InvocationService;
 
import java.io.IOException;
 
 
/**
 * Invocable implementation that increments and returns a given integer.
 */
public class ExampleInvocable
        implements Invocable, PortableObject
    {
    // ----- constructors ---------------------------------------------
 
    /**
     * Default constructor.
     */
    public ExampleInvocable()
        {
        }
 
 
    // ----- Invocable interface --------------------------------------
 
    /**
     * {@inheritDoc}
     */
    public void init(InvocationService service)
        {
        m_service = service;
        }
 
    /**
     * {@inheritDoc}
     */
    public void run()
        {
        if (m_service != null)
            {
            m_nValue++;
            }
        }
 
    /**
     * {@inheritDoc}
     */
    public Object getResult()
        {
        return new Integer(m_nValue);
        }
 
 
    // ----- PortableObject interface ---------------------------------
 
    /**
     * {@inheritDoc}
     */
    public void readExternal(PofReader in)
            throws IOException
        {
        m_nValue = in.readInt(0);
        }
 
    /**
     * {@inheritDoc}
     */
    public void writeExternal(PofWriter out)
            throws IOException
        {
        out.writeInt(0, m_nValue);
        }
 
 
    // ----- data members ---------------------------------------------
 
    /**
     * The integer value to increment.
     */
    private int m_nValue;
 
    /**
     * The InvocationService that is executing this Invocable.
     */
    private transient InvocationService m_service;
    }

10.4.2 Create an Entitled Invocation Service

This example shows how a remote invocation service can be wrapped to provide access control. Access entitlements can be applied to a wrapped InvocationService using the Subject passed from the client by using Coherence*Extend. This implementation only allows clients with a specified role to access the wrapped invocation service.

The class that you create should extend the com.tangosol.net.WrapperInvocationService class. This is a convenience function that allow you to secure the methods on InvocationService. It also provides a mechanism to delegate between the invocation service on the proxy and the client request.

To create an entitled invocation service:

  1. Create a new Java class named EntitledInvocationService in the Security project.

  2. Import the Invocable, InvocationObserver, InvocationService, WrapperInvocationService, Map, and Set interfaces. Ensure that EntitledInvocationService class extends WrapperInvocationService.

  3. Implement the query and execute methods. In the implementations, include a call to SecurityExampleHelper.checkAccess to determine whether a specified user role, in this case, ROLE_WRITER, can access these operations.

Example 10-19 illustrates a possible implementation of EntitledInvocationService.java.

Example 10-19 A Sample Entitled Invocation Service

package com.oracle.handson;
 
 
import com.tangosol.net.Invocable;
import com.tangosol.net.InvocationObserver;
import com.tangosol.net.InvocationService;
import com.tangosol.net.WrapperInvocationService;
 
import java.util.Map;
import java.util.Set;
 
 
/**
* Example WrapperInvocationService that demonstrates how entitlements can be
* applied to a wrapped InvocationService using the Subject passed from the
* client through Coherence*Extend. This implementation only allows clients with a
* specified role to access the wrapped InvocationService.
*
*/
public class EntitledInvocationService
        extends WrapperInvocationService
    {
    /**
    * Create a new EntitledInvocationService.
    *
    * @param service  the wrapped InvocationService
    */
    public EntitledInvocationService(InvocationService service)
        {
        super(service);
        }
 
 
    // ----- InvocationService interface ------------------------------------
 
    /**
    * {@inheritDoc}
    */
    public void execute(Invocable task, Set setMembers, InvocationObserver observer)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        super.execute(task, setMembers, observer);
        }
 
    /**
    * {@inheritDoc}
    */
    public Map query(Invocable task, Set setMembers)
        {
        SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);
        return super.query(task, setMembers);
        }
    }

10.4.3 Create the Access Invocation Service Example Program

Create a program to run the access invocation service example. The objective of the program should be to test whether various users defined in SecurityExampleHelper are able to access and run an invocable object. The enforcement of the role-based policies should be provided by EntitledInvocationService.

To create a program to run the Access Invocation Service example:

  1. Create a Java class with a main method in the Security project named AccessInvocationServiceExample.java.

    See "Creating a Java Class" if you need detailed information.

  2. Among other classes, import ExampleInvocable, CacheFactory, and InvocationService.

  3. Implement the main method to invoke the accessInvocationService method.

  4. Implement accessInvocationService so that various users defined in SecurityExampleHelper attempt to log in to the service and run the object defined in ExampleInvocable. Use the SecurityExampleHelper.login method to test whether various users can access the Invocable Service.

Example 10-20 illustrates a possible implementation of AccessInvocationServiceExample.java.

Example 10-20 Sample Program to Run the Access Invocation Service Example

package com.oracle.handson;
 
import com.oracle.handson.ExampleInvocable;
 
import com.tangosol.net.CacheFactory;
import com.tangosol.net.InvocationService;
 
import java.security.PrivilegedExceptionAction;
 
import javax.security.auth.Subject;
 
 
/**
* This class demonstrates simplified role based access control for the
* invocation service.
* <p>
* The role policies are defined in SecurityExampleHelper. Enforcmenent
* is done by EntitledInvocationService.
*
*/
public class AccessInvocationServiceExample
    {
    /**
    * Invoke the example
    *
    * @param asArg  command line arguments (ignored in this example)
    */
    public static void main(String[] asArg)
        {
        accessInvocationService();
        }
 
    /**
    * Access the invocation service
    */
    public static void accessInvocationService()
        {
        System.out.println("------InvocationService access control example " +
                "begins------");
 
        // Someone with writer role can run invocables
        Subject subject = SecurityExampleHelper.login("JohnWhorfin");
 
        try
            {
            InvocationService service = (InvocationService) Subject.doAs(
                    subject, new PrivilegedExceptionAction()
                {
                public Object run()
                    {
                    return CacheFactory.getService(
                            SecurityExampleHelper.INVOCATION_SERVICE_NAME);
                    }
                });
             service.query(new ExampleInvocable(), null);
             System.out.println("    Success: Correctly allowed to " +
                    "use the invocation service");
            }
        catch (Exception e)
            {
            // get exception if not allowed to perform the operation
            e.printStackTrace();
            }
 
        // Someone with reader role cannot cannot run invocables
        subject = SecurityExampleHelper.login("JohnBigboote");
        try
            {
            InvocationService service = (InvocationService) Subject.doAs(
                    subject, new PrivilegedExceptionAction()
                {
                public Object run()
                    {
                    return CacheFactory.getService(
                            SecurityExampleHelper.INVOCATION_SERVICE_NAME);
                    }
                });
            service.query(new ExampleInvocable(), null);
            }
        catch (Exception ee)
            {
            System.out.println("    Success: Correctly unable to " +
                    "use the invocation service");
            }
        System.out.println("------InvocationService access control example " +
                "completed------");
        }
    }

10.4.4 Edit the Cluster-Side Cache Configuration File

Edit the examples-cache-config.xml file to add the full path to the invocation service to the invocation-service-proxy stanza under the proxy-config. The invocation-service-proxy stanza contains the configuration information for an invocation service proxy managed by a proxy service.

In this case, the invocation service class name is com.oracle.handson.EntitledInvocationService and its param-type should be com.tangosol.net.InvocationService.

Example 10-21 Invocation Service Proxy Configuration for a Cluster-Side Cache

   ...
      <proxy-config>
       ...
        <invocation-service-proxy>
          <class-name>com.oracle.handson.EntitledInvocationService</class-name>
          <init-params>
            <init-param>
              <param-type>com.tangosol.net.InvocationService</param-type>
              <param-value>{service}</param-value>
            </init-param>
          </init-params>
        </invocation-service-proxy>
      </proxy-config>
   ...

10.4.5 Create a POF Configuration File

Create a POF configuration file named security-pof-config.xml in the C:\home\oracle\labs directory where ExampleInvocable is declared as a user type.

Example 10-22 POF Configuration File with ExampleInvocable User Type

<?xml version="1.0"?>
 
<!DOCTYPE pof-config SYSTEM "pof-config.dtd">
 
<pof-config>
  <user-type-list>
 
    <!-- coherence POF user types -->
    <include>coherence-pof-config.xml</include>
 
    <!-- com.tangosol.examples package -->
    <user-type>
      <type-id>1007</type-id>
      <class-name>com.oracle.handson.ExampleInvocable</class-name>
    </user-type>
 
  </user-type-list>
 
  <allow-interfaces>true</allow-interfaces>
  <allow-subclasses>true</allow-subclasses>
</pof-config>

10.4.6 Run the Access Invocation Service Example

Run the access invocation service example to demonstrate how access to the invocable object can be granted or denied, based on a user's role.

  1. Stop any running cache servers. Start the cache server running the proxy service with security-run-proxy.cmd.

  2. Start the cache server in the cluster with security-cache-server.cmd.

  3. Compile the Security project in JDeveloper if you have not done so already.

  4. Right-click the AccessInvocationServiceExample.java file and select Run.

You should see output similar to Example 10-23 in the JDeveloper Log window. The messages correspond to the users specified in AccessInvocationServiceExample trying to run the invocable object ExampleInvocable.

  • the message Success: Correctly allowed to use the invocation service corresponds to the user with role writer attempting to run ExampleInvocable.

  • the message Success: Correctly unable to use the invocation service corresponds to the user with role reader attempting to run ExampleInvocable.

Example 10-23 JDeveloper Log Window

C:\oracle\product0529\jdk160_18\bin\javaw.exe -client -classpath C:\home\oracle\labs\.adf;C:\home\oracle\labs\Security\classes;C:\home\oracle\labs;C:\oracle\product0529\coherence_3.5\lib\coherence.jar -Dweblogic.webservice.client.proxyusername=tom.pfaeffle -Dweblogic.webservice.client.proxypassword=p3rtha -Djavax.net.ssl.trustStore=C:\oracle\product\wlserver_10.3\server\lib\DemoTrust.jks -Dhttp.proxyHost=www-proxy.us.oracle.com -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts=*.local|*oraclecorp.com|*oracle.com|localhost|localhost.localdomain|127.0.0.1|::1|tpfaeffl-lap7.us.oracle.com|tpfaeffl-lap7 -Dhttps.proxyHost=www-proxy.us.oracle.com -Dhttps.proxyPort=80 -Dhttps.nonProxyHosts=*.local|*oraclecorp.com|*oracle.com|localhost|localhost.localdomain|127.0.0.1|::1|tpfaeffl-lap7.us.oracle.com|tpfaeffl-lap7 -Dtangosol.coherence.distributed.localstorage=false -Dtangosol.coherence.cacheconfig=\home\oracle\labs\client-cache-config.xml com.oracle.handson.AccessInvocationServiceExample
------InvocationService access control example begins------
2010-06-17 12:17:04.362/0.391 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational configuration from "jar:file:/C:/oracle/product0529/coherence_3.5/lib/coherence.jar!/tangosol-coherence.xml"
2010-06-17 12:17:04.362/0.391 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational overrides from "jar:file:/C:/oracle/product0529/coherence_3.5/lib/coherence.jar!/tangosol-coherence-override-dev.xml"
2010-06-17 12:17:04.362/0.391 Oracle Coherence 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded operational overrides from "file:/C:/home/oracle/labs/tangosol-coherence-override.xml"
2010-06-17 12:17:04.362/0.391 Oracle Coherence 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" is not specified
 
Oracle Coherence Version 3.6.0.0 DPR3 Build 16141
 Grid Edition: Development mode
Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
 
2010-06-17 12:17:04.580/0.609 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Loaded cache configuration from "file:/C:/home/oracle/labs/client-cache-config.xml"
2010-06-17 12:17:04.799/0.828 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=ExtendTcpInvocationService:TcpInitiator, member=n/a): Loaded POF configuration from "file:/C:/home/oracle/labs/security-pof-config.xml"
2010-06-17 12:17:04.799/0.828 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=ExtendTcpInvocationService:TcpInitiator, member=n/a): Loaded included POF configuration from "jar:file:/C:/oracle/product0529/coherence_3.5/lib/coherence.jar!/coherence-pof-config.xml"
2010-06-17 12:17:04.877/0.906 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=ExtendTcpInvocationService:TcpInitiator, member=n/a): Started: TcpInitiator{Name=ExtendTcpInvocationService:TcpInitiator, State=(SERVICE_STARTED), ThreadCount=0, Codec=Codec(Format=POF), Serializer=com.tangosol.io.pof.ConfigurablePofContext, PingInterval=0, PingTimeout=5000, RequestTimeout=5000, ConnectTimeout=2000, SocketProvider=SystemSocketProvider, RemoteAddresses=[tpfaeffl-lap7/130.35.99.213:9099], KeepAliveEnabled=true, TcpDelayEnabled=false, ReceiveBufferSize=0, SendBufferSize=0, LingerTimeout=-1}
2010-06-17 12:17:04.877/0.906 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Opening Socket connection to 130.35.99.213:9099
2010-06-17 12:17:04.893/0.922 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Connected to 130.35.99.213:9099
    Success: Correctly allowed to use the invocation service
2010-06-17 12:17:04.955/0.984 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=ExtendTcpInvocationService:TcpInitiator, member=n/a): Started: TcpInitiator{Name=ExtendTcpInvocationService:TcpInitiator, State=(SERVICE_STARTED), ThreadCount=0, Codec=Codec(Format=POF), Serializer=com.tangosol.io.pof.ConfigurablePofContext, PingInterval=0, PingTimeout=5000, RequestTimeout=5000, ConnectTimeout=2000, SocketProvider=SystemSocketProvider, RemoteAddresses=[tpfaeffl-lap7/130.35.99.213:9099], KeepAliveEnabled=true, TcpDelayEnabled=false, ReceiveBufferSize=0, SendBufferSize=0, LingerTimeout=-1}
2010-06-17 12:17:04.955/0.984 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=main, member=n/a): Opening Socket connection to 130.35.99.213:9099
2010-06-17 12:17:04.955/0.984 Oracle Coherence GE 3.6.0.0 DPR3 <Info> (thread=main, member=n/a): Connected to 130.35.99.213:9099
    Success: Correctly unable to use the invocation service
------InvocationService access control example completed------
Process exited with exit code 0.

Example 10-24 lists the output in the shell where the cache server is running the proxy service. Notice that the security exception in the output corresponds to the Success: Correctly unable to use the invocation service message in the JDeveloper Log window, where the user with role reader attempts to run ExampleInvocable.

Example 10-24 Proxy Service Window

...
Started DefaultCacheServer...
 
Password validated for user: role_writer
Password validated for user: role_writer
Password validated for user: role_reader
Password validated for user: role_reader
2010-06-17 12:17:04.971/41.250 Oracle Coherence GE 3.6.0.0 DPR3 <D5> (thread=Proxy:ProxyService:TcpAcceptorWorker:1, member=3): An exception occurred
while processing a InvocationRequest for Service=Proxy:ProxyService:TcpAcceptor: java.lang.SecurityException: Access denied, insufficient privileges
        at com.oracle.handson.SecurityExampleHelper.checkAccess(SecurityExampleHelper.java:91)
        at com.oracle.handson.EntitledInvocationService.query(EntitledInvocationService.java:51)
        at com.tangosol.coherence.component.net.extend.messageFactory.InvocationServiceFactory$InvocationRequest.onRun(InvocationServiceFactory.CDB:12
)
        at com.tangosol.coherence.component.net.extend.message.Request.run(Request.CDB:4)
        at com.tangosol.coherence.component.net.extend.proxy.serviceProxy.InvocationServiceProxy.onMessage(InvocationServiceProxy.CDB:9)
        at com.tangosol.coherence.component.net.extend.Channel$MessageAction.run(Channel.CDB:13)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAs(Subject.java:337)
        at com.tangosol.coherence.component.net.extend.Channel.execute(Channel.CDB:29)
        at com.tangosol.coherence.component.net.extend.Channel.receive(Channel.CDB:26)
        at com.tangosol.coherence.component.util.daemon.queueProcessor.service.Peer$DaemonPool$WrapperTask.run(Peer.CDB:9)
        at com.tangosol.coherence.component.util.DaemonPool$WrapperTask.run(DaemonPool.CDB:32)
        at com.tangosol.coherence.component.util.DaemonPool$Daemon.onNotify(DaemonPool.CDB:63)
        at com.tangosol.coherence.component.util.Daemon.run(Daemon.CDB:42)
        at java.lang.Thread.run(Thread.java:619)