4 Authorizing Access to Server-Side Operations

This chapter provides instructions for securing access to Coherence operations using authorization.

This chapter includes the following sections:

4.1 Overview of Access Control Authorization

Access control authorization allows applications to define their own authorization logic to limit access to cluster operations. Authorization is based on identities that are represented as a Principal within a Subject. Applications are responsible for ensuring that the Subject is present for caller threads. If the Subject is missing or cannot be retrieved, then the operation fails with a SecurityException error.

Applications implement the StorageAccessAuthorizer interface to provide authorization logic. The implementations are declared in the operational override configuration file and must also be enabled on a partitioned cache by configuring the backing map of a distributed scheme in a cache configuration file. Access control authorization is only available for partitioned caches.

The StorageAccessAuthorizer interface provides methods that are used to perform read, write, read any, and write any authorization checks. Coherence assumes that there is a logical consistency between authorization decisions made by StorageAccessAuthorizer implementations. That is, for a given Subject, the write authorization implies the read authorization for a given entry; the read any authorization implies read authorization for all entries; and, the write any authorization implies write and read authorization for all entries.

Table 4-1 lists which authorization checks are caused by NamedCache API and BinaryEntry API methods.

Table 4-1 Authorization Checks for Common Methods

Authorization Check NamedCache API Methods BinaryEntry API Methods

None

  • containsKey

  • containsValue

  • isEmpty

  • size

  • lock

  • unlock

 

Read

  • get

  • getAll

  • getValue

  • getBinaryValue

  • extract

  • getOriginalValue

  • getOriginalBinaryValue

Write

  • invoke

  • put

  • putAll

  • remove

  • removeAll

  • setValue

  • update

  • updtaeBinaryValue

  • remove

  • expire

Read Any

  • addMapListenerFoot 1

  • aggregate

  • entrySet

  • keySet

  • removeMapListener1

 

Write Any

  • addIndex

  • clear

  • invokeAll

  • removeIndex

  • values

 

Footnote 1

If a listener is a MapTriggerListener, then a Write Any authorization check is performed instead.

4.2 Creating Access Control Authorization Implementations

To create access control authorization implementations, create a class that implements the com.tangosol.net.security.StorageAccessAuthorizer interface. The implementation should define which callers (based on the Subject) are authorized to access entries and backing map contexts (BinaryEntry and BackingMapManagerContext, respectively).

Note:

The BinaryEntry and BackingMapManagerContext API provide the ability to retrieve the cache name, the service name, and full access to the service and cluster registries.

Example 4-1 Provides a sample StorageAccessAuthorizer implementation that emits a log message for each authorization request. It is based on the AuditingAuthorizer class that is provided with Coherence and used by the default access controller implementation.

Example 4-1 Sample StorageAccessAuthorizer Implementation

package com.examples.security;
 
import com.tangosol.net.BackingMapContext;
import com.tangosol.net.CacheFactory;
import com.tangosol.net.security.StorageAccessAuthorizer;
import com.tangosol.util.BinaryEntry;
 
import javax.security.auth.Subject;
 
public class MyLogAuthorizer implements StorageAccessAuthorizer
{
    public MyLogAuthorizer()
        {
        this(false);
        }
 
    public MyLogAuthorizer(boolean fStrict)
        {
        f_fStrict = fStrict;
        }
 
    @Override
    public void checkRead(BinaryEntry entry, Subject subject, int nReason)
        {
        logEntryRequest(entry, subject, false, nReason);
 
        if (subject == null && f_fStrict)
            {
            throw new SecurityException("subject is not provided");
            }
        }
 
    @Override
    public void checkWrite(BinaryEntry entry, Subject subject, int nReason)
        {
        logEntryRequest(entry, subject, true, nReason);
 
        if (subject == null && f_fStrict)
            {
            throw new SecurityException("subject is not provided");
            }
        }
 
    @Override
    public void checkReadAny(BackingMapContext context, Subject subject, 
        int nReason)
        {
        logMapRequest(context, subject, false, nReason);
 
        if (subject == null && f_fStrict)
            {
            throw new SecurityException("subject is not provided");
            }
        }
 
    @Override
    public void checkWriteAny(BackingMapContext context, Subject subject, 
        int nReason)
        {
        logMapRequest(context, subject, true, nReason);
 
        if (subject == null && f_fStrict)
            {
            throw new SecurityException("subject is not provided");
            }
        }
 
    protected void logEntryRequest(BinaryEntry entry, Subject subject, 
        boolean fWrite, int nReason)
        {
        CacheFactory.log('"' + (fWrite ? "Write" : "Read") 
            + "\" request for key=\""
            + entry.getKey()
            + (subject == null ?
                "\" from unidentified user" :
                "\" on behalf of " + subject.getPrincipals())
            + " caused by \"" + nReason + "\""
            , CacheFactory.LOG_INFO);
        }
 
    protected void logMapRequest(BackingMapContext context, Subject subject,
        boolean fWrite, int nReason)
        {
        CacheFactory.log('"' + (fWrite ? "Write-any" : "Read-any") 
            + "\" request for cache \""
            + context.getCacheName() + '"'
            + (subject == null ?
                " from unidentified user" :
                " on behalf of " + subject.getPrincipals())
            + " caused by \"" + nReason + "\""
            , CacheFactory.LOG_INFO);
        }
 
    private final boolean f_fStrict;
    }

4.3 Declaring Access Control Authorization Implementations

To declare access control authorizer implementations, edit the operational override file and include a <storage-authorizers> element, within the <cluster-config> element, and declare each authorization implementation using a <storage-authorizer> element. For details on the <storage-auhorizer> element, see Developing Applications with Oracle Coherence. Each declaration must include a unique id attribute that is used by a partitioned cache to select an implementation. For example:

<cluster-config>
   <storage-authorizers>
      <storage-authorizer id="LogAuthorizer">
         <class-name>package.MyLogAuthorizer</class-name>
      </storage-authorizer>
   </storage-authorizers>
</cluster-config>

As an alternative, the <storage-authorizer> element supports the use of a <class-factory-name> element to use a factory class that is responsible for creating instances and a <method-name> element to specify the static factory method on the factory class that performs object instantiation. For example:

<cluster-config>
   <storage-authorizers>
      <storage-authorizer id="LogAuthorizer">
         <class-factory-name>package.MyAuthorizerFactory</class-factory-name>
         <method-name>getAuthorizer</method-name>
      </storage-authorizer>
   </storage-authorizers>
</cluster-config>

Any initialization parameters that are required for an implementation can be specified using the <init-params> element. For example:

<cluster-config>
   <storage-authorizers>
      <storage-authorizer id="LogAuthorizer">
         <class-name>package.MyLogAuthorizer</class-name>
         <init-params>
            <init-param>
               <param-name>f_fStrict</param-name>
               <param-value>true</param-value>
            </init-param>
         </init-params>
      </storage-authorizer>
   </storage-authorizers>
</cluster-config>

4.4 Enabling Access Control Authorization on a Partitioned Cache

To enable access control authorization on a partitioned cache, edit the cache configuration file and add a <storage-authorizer> element, within the <backing-map-scheme> element of a distributed scheme, whose value is the id attribute value of an authorization implementation that is declared in the operational override file. For example:

<distributed-scheme>
   ...
   <backing-map-scheme>
      <storage-authorizer>LogAuthorizer</storage-authorizer>
      <local-scheme/>
   </backing-map-scheme>
   <autostart>true</autostart>
</distributed-scheme>