5 Securing Extend Client Connections

You can use identity tokens and interceptor classes to provide authentication and authorization for Oracle Coherence*Extend clients. Identity tokens protect against unauthorized access to an extend proxy. Interceptor classes control which operations are available to an authenticated client.

This chapter includes the following sections:

Using Identity Tokens to Restrict Client Connections

Identity tokens are used to control which clients can access a cluster. 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.

This section includes the following topics:

Overview of Using Identity Tokens

Identity token security uses an identity transformer implementation to create identity tokens and an identity asserter implementation to validate identity tokens. These implementations are described as follows:

  • Identity transformer – a client-side component that converts a Subject, or Principal, into an identity token that is passed to an extend proxy. An identity token can be any type of object that is useful for identity validation; it is not required to be a well-known security type. In addition, clients can connect to multiple proxy servers and authenticate to each proxy server differently.

  • Identity asserter – 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 validates identity tokens unique for each proxy service to support multiple means of token validation. The token is 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.

Figure 5-1 shows a conceptual view of restricting client access using identity tokens.

Figure 5-1 Conceptual View of Identity Tokens

Description of Figure 5-1 follows
Description of "Figure 5-1 Conceptual View of Identity Tokens"

An identity transformer (DefaultIdentityTransformer) and identity asserter (DefaultIdentityAsserter) are provided and enabled by default. The implementations simply use the Subject (Java) or Principal (.NET) as the identity token. The default behavior is overridden by providing custom identity transformer and identity asserter implementations and enabling them in the operational override file.

Note:

  • At runtime, 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.

  • You can use security object types other than the types that are predefined in Portable Object Format (POF). See Using Custom Security Types.

Creating a Custom Identity Transformer

A default identity transformer implementation (DefaultIdentityTransformer) is provided that simply returns a Subject or Principal that is passed to it. If you do not want to use the default implementation, you can create your own custom transformer implementation.

Note:

At runtime, identity tokens are automatically serialized for known types and sent as part of the extend connection request. For .NET and C++ clients, the type must be a POF type. You can use security object types other than the predefined POF types. See Using Custom Security Types.

For Java and C++, create a custom identity transformer by implementing the IdentityTransformer interface. C# clients implement the IIdentityTransformer interface.

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

Example 5-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");
        }
    }

One possible solution for preexisting client authentication implementations is to add a new Principal to the Subject with the Principal name as the password. Add the password Principal to the Subject during JAAS authentication by modifying an existing JAAS login module or by adding an additional required login module that adds the password Principal. The JAAS API allows multiple login modules, each of which modifies the Subject. Similarly, in .NET, add a password identity to the Principal. The asserter on the cluster side then validates both the Principal and the password Principal. See Creating a Custom Identity Asserter.

Enabling a Custom Identity Transformer

To enable a custom identity transformer implementation, edit the client-side tangosol-coherence-override.xml file and add an <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>

Creating a Custom Identity Asserter

A default identity asserter implementation (DefaultIdentityAsserter) is provided that asserts that an identity token is a Subject or Principal. If you do not want to use the default implementation, you can create your own custom asserter implementation.

For Java and C++, create an identity asserter by implementing the IdentityAsserter interface. C# clients implement the IIdentityAsserter interface.

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

Example 5-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 you create an identity asserter. For example, you can create an asserter that rejects connections based on a list of principals, that checks role principals, or validates the signed principal name. The asserter blocks any connection attempts that do not prove the correct identity.

Enabling a Custom Identity Asserter

To enable a custom identity asserter implementation, edit the cluster-side tangosol-coherence-override.xml file and add an <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>

Using Custom Security Types

Security objects are automatically serialized and deserialized using Portable Object Format (POF) when they are passed between extend clients and extend proxies. Security objects that are predefined in POF require no configuration or programming changes. However, security objects that are not predefined in POF (for example, when an application uses Kerberos authentication) cause an error. For custom security types, an application must convert the custom type or define the type in POF. There are two approaches for using unsupported types.

Converting the Type

The custom identity transformer implementation 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, the custom identity asserter implementation converts the object back (after validation) to a Subject.

For example, a subject may contain credentials that are not serialized. The identity transformer implementation extracts the credential and converts it to a character array, returning that array as the token. On the proxy server, the identity asserter converts the character array to the proper credential type, validates it, and then constructs a Subject to return.

Defining the Custom Type in POF

You can define the custom security types in both the client's and the proxy's POF configuration file. For detailed information about using POF with Java, see Using Portable Object Format in Developing Applications with Oracle Coherence. For more information about using POF with C++ and C#, see Building Integration Objects (C++) and Building Integration Objects (.NET), respectively in Developing Remote Clients for Oracle Coherence.

Understanding Custom Identity Token Interoperability

Solutions that 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 a new custom identity token solution is implemented.

Oracle Coherence Upgrades

Interoperability issues may occur during the process of upgrading. In this scenario, different client versions may interoperate with different proxy server versions. Ensure that a custom identity asserter can handle identity tokens sent by an extend client. Conversely, ensure that a custom identity transformer sends a token that the extend proxy can handle.

Custom Identity Token Rollout

Interoperability issues may occur between extend clients and extend proxies during the roll out a custom identity token solution. In this scenario, as extend proxies are migrated to use a custom identity asserter, some proxies continue to use the default asserter until the rollout operation is completed. Likewise, as extend clients are migrated to use a custom identity transformer, clients continue to use the default transformer until the rollout operation is completed. In both cases, the extend clients and extend proxies must be able to handle the default token type until the rollout operation is complete.

One strategy for such a scenario is to have a custom identity asserter that accepts the default token types temporarily as clients are updated. The identity asserter checks an external source for a policy that indicates whether those tokens are accepted. After all clients have been updated to use a custom token, change the policy to accept the custom tokens.

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 that 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:

You can use security object types other than the types that are predefined in POF. See Using Custom Security Types.

For example, if a user with a trader identity calls CacheFactory.getCache("trade-cache") and a user with the manager identity calls CacheFactory.getCache("trade-cache"), each user gets a different remote cache reference object. Because 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.

For Java and C++ clients, enable subject scope in the client-side tangosol-coherence-override.xml file using the <subject-scope> element within the <security-config> node. 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>
      <subject-scope>true</subject-scope>
   </security-config>
</coherence>

For .NET clients, enable subject scope in the client-side tangosol-coherence-override.xml file using the <principal-scope> element within the <security-config> node. For example:

<?xml version='1.0'?>

<coherence 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>

Implementing Extend Client Authorization

Oracle Coherence*Extend authorization controls which operations can be performed on a cluster based on an extend client's access rights. Authorization logic is implementation-specific and is enabled on a cluster proxy. The code samples in this section are based on the Java authorization example, which is included in the examples that are delivered as part of the distribution. 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.

This section includes the following topics:

Overview of Extend Client Authorization

Interceptor classes provide the ability to implement client authorization. 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. They must provide the necessary authorization logic before passing the request to the proxied resources.

Figure 5-2 shows a conceptual view of extend client authorization.

Figure 5-2 Conceptual View of Extend Client Authorization

Description of Figure 5-2 follows
Description of "Figure 5-2 Conceptual View of Extend Client Authorization"

Create Authorization Interceptor Classes

To create interceptor classes for both a proxied cache service and a proxied invocation service, implement the CacheService and InvocationService interfaces, respectively. Or, as is more common, extend a set of wrapper classes: 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 5-3 is taken from the Oracle Coherence examples. The example 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 allows only a Principal with a specified role name to access the InvocationService methods.

Example 5-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 the EntitledCacheService class requires a named cache implementation. The WrapperNamedCache class is extended and wraps each method of the NamedCache instance. This allows access controls to be applied to different cache operations.

Note:

Much of the functionality that is provided by the WrapperNamedCache class is also covered by the StorageAccessAuthorizer interface, which provides a better and simplified way to authorize cluster operations. See Authorizing Access to Server-Side Operations.

Example 5-4 is a code excerpt taken from the Oracle Coherence examples. The example demonstrates overriding the NamedCache methods and applying access checks before allowing the method to be executed. See the examples for the complete class.

Example 5-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 5-5 is taken from the Oracle Coherence examples. The example demonstrates creating an authorization interceptor class for a proxied cache service by extending the WrapperCacheService class. It wraps all CacheService methods on the proxy and applies access controls based on the Subject passed from an extend client. The implementation allows only a Principal with the specified role to access the CacheService methods

Example 5-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, the proxy then calls the query() method on the proxied InvocationService instance.

Enable Authorization Interceptor Classes

To enable interceptor classes for a proxied cache service and a proxied invocation service, edit a proxy scheme definition and add a <cache-service-proxy> element and <invocation-service-proxy> element, respectively. Use the <class-name> element to enter the fully qualified name of the interceptor class. Specify initialization parameters using the <init-params> element. See cache-service-proxy and invocation-service-proxy in Developing Applications with Oracle Coherence for detailed information about using these elements.

The following example demonstrates enabling interceptor classes for both a proxied cache service and a proxied invocation service. The example uses the interceptor classes from Example 5-3 and Example 5-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>