Skip Headers
Oracle® Coherence Security Guide
Release 3.7

Part Number E18681-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

4 Securing Extend Client Connections

Coherence*Extend includes an additional set of features that are used to secure communication between extend clients and extend proxies. See Oracle Coherence Client Guide for details on creating Coherence*Extend clients.

The following sections are included in this chapter:

4.1 Using Identity Tokens to Restrict Client Connections

Extend client can be restricted from accessing a cluster by using an identity token. The token is sent between extend clients and extend proxies whenever a connection is attempted. Only extend clients that pass a valid identity token are allowed to access the cluster.

On the extend client, identity tokens are created by an identity transformer and sent as part of the connection request. On the cluster side, an identity asserter is used to validate the identity token before the connection is accepted. Coherence*Extend includes a default identity transformer (DefaultIdentityTransformer) and identity asserter (DefaultIdentityAsserter) that use the Subject (Java) or Principal (.NET) for the identity token. The default behavior can be overridden by providing custom identity transformer and identity asserter implementations and enabling them in the Coherence operational override file.

Note:

  • At run time, identity transformer implementation classes must be located on the extend client's classpath and identity asserter implementation classes must be located on the extend proxy server's classpath.

  • See "Using Custom Security Types" for more information on using security object types other than the types that are predefined in POF.

The following topics are included in this section:

4.1.1 Creating a Custom Identity Transformer

An identity transformer is a client-side component that converts a Subject, or Principal, into an identity token that can be passed to an extend proxy. The identity token can be any type of object useful for identity validation; it is not required to be a well-known security type. In addition, the identity token can be specific to a remote service to support clients that connect to multiple proxy servers and authenticate to each proxy server in a unique fashion. At run time, the token is serialized and sent as part of the extend connection request.

Note:

Identity tokens that are of a type that Coherence can serialize are automatically serialized. For .NET and C++ clients, the type must be a POF type. See "Using Custom Security Types" for more information on using security object types other than the types that are predefined in POF.

For Java and C++, the identity transformer must implement the IdentityTransformer interface. C# clients implement the IIdentityTransformer interface.

Example 4-1 is a Java implementation that restricts client access by requiring a client to supply a password to access the proxy. The IdentityTransformer gets a password from a system property on the client and returns it as an identity token.

Example 4-1 A Sample Identity Transformer Implementation

import com.tangosol.net.security.IdentityTransformer;
import javax.security.auth.Subject;
import com.tangosol.net.Service;
 
public class PasswordIdentityTransformer 
        implements IdentityTransformer
    {
    public Object transformIdentity(Subject subject, Service service)
            throws SecurityException
        {
        return System.getProperty("mySecretPassword");
        }
    }

If client authentication is being done, a new Principal could be added to the Subject, with the Principal name as the password. The password Principal could be added to the Subject during JAAS authentication by modifying an existing JAAS login module or by adding an additional required login module that would add the password Principal. JAAS allows multiple login modules each of which can modify the Subject. Similarly, in .NET a password identity could be added to the Principal. The asserter on the cluster-side would then validate the "normal" Principals plus validate the password Principal. See "Creating a Custom Identity Asserter" below.

4.1.2 Enabling a Custom Identity Transformer

An identity transformer implementation is enabled in the client-side tangosol-coherence-override.xml file using the <identity-transformer> element within the <security-config> node. The element must include the full name of the implementation class. For example:

<?xml version='1.0'?>

<coherence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://xmlns.oracle.com/coherence/coherence-operational-config"
   xsi:schemaLocation="http://xmlns.oracle.com/coherence/
   coherence-operational-config coherence-operational-config.xsd">
   <security-config>
      <identity-transformer>
        <class-name>com.my.PasswordIdentityTransformer</class-name>
      </identity-transformer>
   </security-config>
</coherence>

4.1.3 Creating a Custom Identity Asserter

An identity asserter is a cluster-side component that resides on the cache server that is hosting an extend proxy service. The asserter validates an identity token that is created by an identity transformer on the extend client. The asserter can validate identity tokens unique for each proxy service to support multiple means of token validation.

The token gets passed when an extend client initiates a connection. If the validation fails, the connection is refused and a security exception is thrown. The transformer and asserter are also invoked when a new channel within an existing connection is created. For Java and C++, the identity asserter must implement the IdentityAsserter interface. C# clients implement the IIdentityAsserter interface.

Example 4-2 is a Java implementation that checks a security token to ensure a valid password is given. In this case, the password is checked against a system property on the cache server. This asserter implementation is used for the identity transformer sample in Example 4-1.

Example 4-2 A Sample Identity Asserter Implementation

import com.tangosol.net.security.IdentityAsserter;
import javax.security.auth.Subject;
import com.tangosol.net.Service;
 
 
public class PasswordIdentityAsserter 
        implements IdentityAsserter
    {
    public Subject assertIdentity(Object oToken, Service service)
            throws SecurityException
        {
        if (oToken instanceof String)
            {
            if (((String) oToken).equals(System.getProperty("mySecretPassword")))
                {
                return null;
                }
            }
        throw new SecurityException("Access denied");
        }
    }

There are many possible variations when creating an identity asserter. For example, the asserter could reject connections based on a list of Principals, check role Principals, validate signed Principal name, and so on. The asserter can block any connection attempt that do not prove the correct identity.

4.1.4 Enabling a Custom Identity Asserter

An identity asserter implementation is enabled in the cluster-side tangosol-coherence-override.xml file using the <identity-asserter> element within the <security-config> node. The element must include the full name of the implementation class. For example:

<?xml version='1.0'?>

<coherence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://xmlns.oracle.com/coherence/coherence-operational-config"
   xsi:schemaLocation="http://xmlns.oracle.com/coherence/
   coherence-operational-config coherence-operational-config.xsd">
   <security-config>
      <identity-asserter>
        <class-name>com.my.PasswordIdentityAsserter</class-name>
      </identity-asserter>
   </security-config>
</coherence>

4.1.5 Using Custom Security Types

Security objects are automatically serialized/deserialized using Portable Object Format (POF) when they are passed between extend clients and extend proxies. Security objects of types that are predefined in POF can be used without any configuration or programming changes. Security objects of custom types that are not predefined in POF (for example, when using Kerberos authentication) causes an error.

For custom security types, an application can choose to either convert the custom type or define the type in POF:

Convert the Type

In this approach, a custom identity transformer converts a custom security object type to a type that is predefined for POF such as a character array or string before returning it as an object token. On the proxy server, a custom identity asserter would convert it back (after validation) to a subject.

For example, a subject may contain credentials that are not serialized. The identity transformer would extract the credential and convert it to a character array, returning that array as the token. On the proxy server, the identity asserter would convert the character array to the proper credential type, validate it, and then construct a subject to return.

Define the Custom Type in POF

In this approach, custom security types are defined in a POF configuration file. The type must be defined in both the client's POF configuration file and the POF configuration file on the proxy server.

For detailed information on using POF with Java, see Oracle Coherence Developer's Guide. For more information on using POF with C++, see Oracle Coherence Client Guide. For more information on using POF with C#, see Oracle Coherence Client Guide.

4.1.6 Understanding Custom Identity Token Interoperability

Solutions that choose to use a custom identity token must always consider what tokens may be sent by an extend client and what tokens may be received by an extend proxy. This is particularly important during rolling upgrades and when rolling out a custom identity token solution.

Coherence Upgrades

Interoperability issues may occur during the process of upgrading Coherence. In this scenario, there may be different client versions that interoperate with different proxy server versions. In particular:

  • When a 3.5 extend client is connecting to a 3.6 extend proxy, the custom identity asserter that is implemented on the extend proxy must be able to handle identity tokens sent by the 3.5 extend client. A 3.5 extend client sends either a null token or a Subject. The custom identity asserter must be prepared to handle those token types in addition to any custom tokens originating from a 3.6 extend client.

  • Conversely, when a 3.6 extend client is connecting to a 3.5 extend proxy, the client must not use a custom identity transformer that sends a token that the 3.5 extend proxy cannot handle. The client must send either a null token or a Subject.

Custom Identity Token Rollout

Interoperability issues may occur between extend clients and extend proxies when rolling out a custom identity token solution. In this scenario, as extend proxies are migrated to use a custom identity asserter, there are proxies that continue to use the default asserter until the roll out is completed. Likewise, as extend clients are migrated to use a custom identity transformer, there are clients that continue to use the default transformer until the roll out is completed. In both cases, the extend clients and extend proxies must be able to handle a null token or a Subject until the rollout is complete.

A possible strategy for such scenarios may be to have a custom identity asserter that accepts null or Subject tokens temporarily as clients are updated. The identity asserter could check an external source for a policy that indicates whether those tokens are accepted. After all clients have been updated to use a custom token, the policy could be changed and the identity asserter would no longer accept those tokens.

4.2 Associating Identities with Extend Services

Subject scoping allows remote cache and remote invocation service references that are returned to a client to be associated with the identity from the current security context. By default, subject scoping is disabled, which means remote cache and remote invocation service references are globally shared.

With subject scoping enabled, clients use their platform-specific authentication APIs to establish a security context. A subject or principal is obtained from the current security context whenever a client creates a NamedCache and InvocationService instance. All requests are then made for the established subject or principal.

Note:

See "Using Custom Security Types" for more information on using security object types other than the types that are predefined in POF.

For example, if the "trader" user calls CacheFactory.getCache("trade-cache") and the "manager" user calls CacheFactory.getCache("trade-cache"), each user gets a different remote cache reference object. Since an identity is associated with that remote cache reference, authorization decisions can be made based on the identity of the caller. See "Implementing Extend Client Authorization" below for details on implementing authorization.

For Java and C++ clients, this feature is enabled in the client-side tangosol-coherence-override.xml file using the <subject-scope> element within the <security-config> node. The following example enables subject scoping and ensures each subject gets a unique remote cache and remote invocation service reference.

<?xml version='1.0'?>

<coherence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://xmlns.oracle.com/coherence/coherence-operational-config"
   xsi:schemaLocation="http://xmlns.oracle.com/coherence/
   coherence-operational-config coherence-operational-config.xsd">
   <security-config>
      <subject-scope>true</subject-scope>
   </security-config>
</coherence>

For .NET clients, this feature is enabled in the client-side tangosol-coherence-override.xml file using the <principal-scope> element within the <security-config> node. The following example enables subject scoping and ensures each subject gets a unique remote cache and remote invocation service reference.

<?xml version='1.0'?>

<cache-config xmlns="http://schemas.tangosol.com/cache"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://schemas.tangosol.com/cache
  assembly://Coherence/Tangosol.Config/coherence.xsd">
  <security-config>
     <principal-scope>true<principal-scope>
  </security-config>
</coherence>

4.3 Implementing Extend Client Authorization

Authorization is used to control which actions a particular user can perform based on their access control rights. In Coherence*Extend, authorization is implemented with interceptor classes. An extend proxy calls the interceptor classes before a client accesses a proxied resource (cache, cache service, or invocation service). Interceptor classes are implementation specific and must provide the necessary authorization logic before passing the request to the proxied resources.

The following topics are included in this section:

The code samples in this section are based on the Java authorization example, which is included in the Coherence examples that are delivered as part of the documentation library. The example demonstrates a basic authorization implementation that uses the Principal obtained from a client request and a role-based policy to determine whether to allow operations on the requested service. Download the examples for the complete implementation.

4.3.1 Creating Authorization Interceptor Classes

An interceptor class can be created for both a proxied cache service and a proxied invocation service by implementing the CacheService and InvocationService interfaces, respectively. However, a set of wrapper classes are typically extended when implementing authorization: com.tangosol.net.WrapperCacheService (with com.tangosol.net.cache.WrapperNamedCache) and com.tangosol.net.WrapperInvocationService. The wrapper classes delegate to their respective interfaces and provide a convenient way to create interceptor classes that apply access control to the wrapped interface methods.

Example 4-3 is taken from the Coherence examples and demonstrates creating an authorization interceptor class for a proxied cache service by extending WrapperCacheService. It wraps all CacheService methods on the proxy and applies access controls based on the Subject passed from an extend client. The implementation only allows a Principal with the specified role to access the wrapped CacheService.

Example 4-3 Extending the WrapperCacheService Class for Authorization

public class EntitledCacheService
   extends WrapperCacheService
   {
   public EntitledCacheService(CacheService service)
       {
       super(service);
       }

   public NamedCache ensureCache(String sName, ClassLoader loader)
      {
      SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
      return new EntitledNamedCache(super.ensureCache(sName, loader));
      }

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

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

Notice that this class requires a named cache implementation. For this example, the WrapperNamedCache class is extended and wraps each method of the NamedCache instance. This allows access controls to be applied to different cache operations. Example 4-4 is a code excerpt taken from the Coherence examples that demonstrates overriding the NamedCache methods and applying access checks before allowing the method to be executed. See the Coherence examples for the complete class.

Example 4-4 Extending the WrapperNamedCache Class for Authorization

public class EntitledNamedCache
   extends WrapperNamedCache
   {
   public EntitledNamedCache(NamedCache cache)
      {
       super(cache, cache.getCacheName());
      }

   public Object put(Object oKey, Object oValue, long cMillis)
      {
      SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER);      return super.put(oKey, oValue, cMillis);
      }

   public Object get(Object oKey)
      {
      SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
      return super.get(oKey);
      }

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

Example 4-5 is taken from the Coherence examples and demonstrates creating an authorization interceptor class for a proxied invocation service by extending WrapperInvocationService. It wraps all InvocationService methods on the proxy and applies access controls based on the Subject passed from an extend client. The implementation only allows Principals with a specified role name to access the wrapped InvocationService.

Example 4-5 Extending the WrapperInvocationService Class for Authorization

public class EntitledInvocationService
   extends WrapperInvocationService
   {
   public EntitledInvocationService(InvocationService service)
      {
      super(service);
      }

   public void execute(Invocable task, Set setMembers, InvocationObserver 
      observer)
      {
      SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER)
      super.execute(task, setMembers, observer);
      }

   public Map query(Invocable task, Set setMembers)
      {
      SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_WRITER)
      return super.query(task, setMembers);
      }
}

When a client attempts to use a remote invocation service, the proxy calls the query() method on the EntitledInvocationService class, rather than on the proxied InvocationService instance. The EntitledInvocationService class decides to allow or deny the call. If the call is allowed, it then calls the query() method on the proxied InvocationService instance.

4.3.2 Enabling Authorization Interceptor Classes

Interceptor classes for a proxied cache service and a proxied invocation service are enabled in a proxy scheme definition within the <cache-service-proxy> element and <invocation-service-proxy> element, respectively. The <class-name> element is used to enter the fully qualified name of the interceptor class. Initialization parameters required by the class can be specified using the <init-params> element. See "cache-service-proxy" and "invocation-service-proxy" in Oracle Coherence Developer's Guide for detailed information on using these elements.

The following example demonstrates enabling both a proxied cache service and proxied invocation service interceptor class. The example uses the interceptor classes that were created in Example 4-3 and Example 4-5.

<proxy-scheme>
   ...
   <proxy-config>
      <cache-service-proxy>
         <class-name>
            com.tangosol.examples.security.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>
      <invocation-service-proxy>
         <class-name>
            com.tangosol.examples.security.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>