5 Securing Extend Client Connections
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. - 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. - 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.
Using Identity Tokens to Restrict Client Connections
This section includes the following topics:
- Overview of Using Identity Tokens
- Creating a Custom Identity Transformer
- Enabling a Custom Identity Transformer
- Creating a Custom Identity Asserter
- Enabling a Custom Identity Asserter
- Using Custom Security Types
- Understanding Custom Identity Token Interoperability
Parent topic: Securing Extend Client Connections
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
, orPrincipal
, 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 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.
Parent topic: Using Identity Tokens to Restrict Client Connections
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.
Parent topic: Using Identity Tokens to Restrict Client Connections
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>
Parent topic: Using Identity Tokens to Restrict Client Connections
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.
Parent topic: Using Identity Tokens to Restrict Client Connections
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>
Parent topic: Using Identity Tokens to Restrict Client Connections
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.
Parent topic: Using Identity Tokens to Restrict Client Connections
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.
Parent topic: Using Identity Tokens to Restrict Client Connections
Associating Identities with Extend Services
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>
Parent topic: Securing Extend Client Connections
Implementing Extend Client Authorization
This section includes the following topics:
- Overview of Extend Client Authorization
- Create Authorization Interceptor Classes
- Enable Authorization Interceptor Classes
Parent topic: Securing Extend Client Connections
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 Conceptual View of Extend Client Authorization"
Parent topic: Implementing 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.
Parent topic: Implementing Extend Client Authorization
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>
Parent topic: Implementing Extend Client Authorization