ヘッダーをスキップ
Oracle® Fusion Middleware Oracle Coherenceチュートリアル
12c (12.1.3)
E56204-01
  ドキュメント・ライブラリへ移動
ライブラリ
製品リストへ移動
製品
目次へ移動
目次

前
 
次
 

10 セキュリティに関する作業

この演習では、Oracle Coherence*Extendクライアントにセキュリティを適用する方法について説明します。ここでは、Coherence APIのSecurityHelperPofPrincipalIdentityTransformerIdentityAsserterおよびWrapperCacheServiceの使用について取り上げます。

Coherence*Extendでは、Coherenceキャッシュに対する広範なアクセスが許可されます。これには、デスクトップ・アプリケーション、リモート・サーバーおよびWide Area Network (WAN)接続を介して配置されているマシンが含まれます。

この章には次の項が含まれます:

10.1 概要

Coherence*Extendは、クラスタの外部で実行されるExtendクライアントと、1台以上のキャッシュ・サーバーでホストされ、クラスタ内で実行されるプロキシ・サービスで構成されます。クライアントAPIにより、すべてのリクエストがプロキシにルーティングされます。プロキシは、パーティション化またはレプリケートされたキャッシュ・サービス、起動サービスなどのCoherenceのクラスタ化されたサービスに委任することによって、クライアント・リクエストに応答します。

Extendクライアントはクラスタの外部に存在するため、クラスタへのアクセス保護の問題が非常に重要になります。この章では、クライアントとクラスタ間のアクセス保護に使用可能な3つの技術を説明します。その技術とは、IDトークンベース・パスワード、資格が付与されるキャッシュ・サービスおよび起動サービスの使用などです。

これらのセキュリティ技術およびExtendクライアントの詳細な解説は、このチュートリアルの対象範囲ではありません。これらのトピックの詳細は、『Oracle Fusion Middleware Oracle Coherenceの保護』を参照してください。

10.2 トークンベース・セキュリティの有効化

トークンベースのセキュリティを実装すると、Extendクライアントとクラスタ内の拡張プロキシ間のアクセスを有効化できます。一般に、Extendクライアントと拡張プロキシ間のアクセスを有効化するには、次のファイルが必要です。

トークンベース・セキュリティを追加するには、IDトランスフォーマおよびアサータの実装を指定する必要があります。このトランスフォーマは、クライアント側でトークンを生成し、アサータはクラスタ側でそれを検証します。

トークンベース・セキュリティを使用してクラスタにアクセスするExtendクライアントのアプリケーションを作成し、実行する手順は次のとおりです。

  1. セキュリティ・ヘルパー・ファイルの使用

  2. IDトランスフォーマの作成

  3. アイデンティティ・アサータの作成

  4. パスワード・ファイルの作成

  5. IDトランスフォーマおよびアサータの有効化

  6. Extendクライアントのキャッシュ構成ファイルの作成

  7. 拡張プロキシのキャッシュ構成ファイルの作成

  8. プロキシ・サービスを含むキャッシュ・サーバーの起動構成の作成

  9. キャッシュ・サーバーの起動構成の作成

  10. パスワード・サンプルの実行

10.2.1 セキュリティ・ヘルパー・ファイルの使用

この章の例では、ロールベース・セキュリティ・ポリシーおよびキャッシュへのアクセス制御を定義するヘルパー・ファイルを参照します。これらの例に使用するための簡略マッピングのファイルが用意されています。

キャッシュ・アクセスはユーザーのロールによって決定されます。セキュリティ・ヘルパー・ファイルによっていくつかのロール(role_readerrole_writerおよびrole_admin)が定義されます。BuckarooBanzaiROLE_ADMINにマッピングするなど、様々なユーザーのロールへのマッピングが定義されます。ROLE_ADMIN9にマッピングするなど、ロールの整数IDへのマッピングが定義されます。また、ヘルパー・ファイルでは、例で使用されるキャッシュ名および起動サービス名も定義されます。

このファイルの主要機能は、loginおよびcheckAccessのメソッドです。loginメソッドは、ユーザー名を取得して、簡略化された識別名(DN)を構成します。その後、ロールを名前と関連付けます。PofPrincipalは、Principal実装を提供します。

checkAccessメソッドは、認可コードが配置されている場所を示します。これによって、提供されたユーザー・ロールに基づいてユーザーがキャッシュにアクセス可能であるかどうかが判定されます。

新規のプロジェクトおよびセキュリティ・ヘルパー・ファイルを作成するには:

  1. Eclipse IDEで、Java EEパースペクティブを選択してSecurityという名前の新規アプリケーション・クライアント・プロジェクトを作成します。「Configuration」ドロップダウン・リストから「CoherenceConfig」を選択します。「Application Client」モジュール・ページで、「Create a default main」が選択されていないことを確認します。

    「Coherence」ページで、「Coherence12.1.3」のみを選択します。

    プロジェクト作成の詳細は、「Eclipse IDEでの新規プロジェクトの作成」を参照してください。

  2. SecurityExampleHelper.javaという名前で新規Javaファイルを作成します。パッケージ・パスがcom.oracle.handsonであることを確認します。

    Javaクラスの作成の詳細は、「Javaクラスの作成」を参照してください。

  3. 例10-1に示されたコードをファイルにコピーします。

    例10-1 セキュリティ・ヘルパー・ファイル

    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)
             {
             checkAccess(sRoleRequired, SecurityHelper.getCurrentSubject());
             }
     
         /**
         * Assert that a Subject contains a Principal representing the required
         * role.
         *
         * @param sRoleRequired  the role required for the operation
         *
         * @param subject        the Subject requesting access
         *
         * @throws SecurityException if a Subject is null or does not have the
         * specified role Principal
         */
         public static void checkAccess(String sRoleRequired, Subject subject)
             {
             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 IDトランスフォーマの作成

IDトランスフォーマ(com.tangosol.net.security.IdentityTransformer)は、クライアント側のコンポーネントで、サブジェクトまたはプリンシパルをIDトークンに変換します。このトークンは、Coherenceでシリアライズ可能なデータ型である必要があります。Coherenceは、実行時にこのトークンを自動的にシリアライズして、接続リクエストの一部としてプロキシに送信します。

IDトランスフォーマの実装を作成するには:

  1. Securityプロジェクトに、PasswordIdentityTransformerという名前のJavaクラスを新規作成します。

    詳細は、「Javaクラスの作成」を参照してください。

  2. IdentityTransformerインタフェースをインポートします。PasswordIdentityTransformerクラスがIdentityTransformerインタフェースを実装することを確認します。

  3. transformIdentityメソッドを実装して、次のタスクを実行するようにします。

    • サブジェクトが存在し、完全であるかどうかのテスト

    • サブジェクトからのプリンシパル名の取得およびString配列への保存

    • POFデータ型としてシリアライズ可能な、パスワードとプリンシパル名の組合せによるトークンの構成

例10-2は、考えられるPasswordIdentityTransformerクラスの実装を示しています。

例10-2 IDトランスフォーマの実装例

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;
import com.tangosol.net.Service;
 
/**
* 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, Service service)
            throws SecurityException
        {
  // The service is not needed so the service argument is being ignored.
  // It could be used, for example, if there were different token types
  // required per service.

        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アイデンティティ・アサータの作成

アイデンティティ・アサータ(com.tangosol.net.security.IdentityAsserter)は、拡張プロキシ・サービスをホストするキャッシュ・サーバー上のクラスタ側のコンポーネントです。アサータは、ExtendクライアントでIDトランスフォーマによって作成されたトークンに、クラスタへのアクセスに必要な資格証明が格納されているかを検証します。

アイデンティティ・アサータの実装を作成するには:

  1. Securityプロジェクトに、PasswordIdentityAsserterという名前のJavaクラスを新規作成します。

    詳細は、「Javaクラスの作成」を参照してください。

  2. IdentityAsserterインタフェースをインポートします。PasswordIdentityAsserterクラスがIdentityAsserterインタフェースを実装することを確認します。

  3. 次のタスクが実行されるように、assertIdentityメソッドを実装します。

    • トークンに正しいパスワードが格納されていることおよびパスワードがトークン内の最初の名前であることを検証します。

例10-3は、考えられるPasswordIdentityAsserterクラスの実装を示しています。

例10-3 アイデンティティ・アサータの実装例

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;
import com.tangosol.net.Service;
 
 
/**
* 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, Service service)
            throws SecurityException
        {
    // The service is not needed so the service argument is being ignored.
    // It could be used, for example, if there were different token types
    // required per service.
        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 パスワード・ファイルの作成

キャッシュの参照の取得時にパスワードを必要とするJavaファイルを作成します。SecurityExampleHelper.login("BuckarooBanzai")を使用して、トークンを生成するSecurityExampleHelperファイルのloginメソッドをコールします。実行時に、ユーザー名がSecurityExampleHelperクラスで定義されたサブジェクトと関連付けられます。トークンは、PasswordIdentityTransformerクラスによってこのサブジェクトから生成され、PasswordIdentityAsserterクラスによって接続リクエストの一部として検証されます。検証が成功すると、プロキシへの接続およびキャッシュの参照の権限が付与されます。Subject.doasメソッドを使用して、セキュリティ・コンテキストでサブジェクトを利用できるようにします。

  1. PasswordExampleという名前のSecurityプロジェクトに、mainメソッドを使用して新たなJavaクラスを作成します。

    詳細は、「Javaクラスの作成」を参照してください。

  2. キャッシュの参照を取得するmainメソッドを実装します。

  3. SecurityExampleHelper.loginメソッドを使用して、ユーザーBuckarooBanzaiのサブジェクトを取得します。

  4. doAsメソッドを実装して、Javaセキュリティ・コンテキストのサブジェクト・パートを作成します。このサブジェクトは、後続のいずれのコードでも利用できます。この例では、定義されたロールを基準にして、ユーザーがキャッシュにアクセスできるかどうかを検証するためにdoAsメソッドが実装されます。

例10-4は、考えられるPasswordExampleの実装を示しています。

例10-4 パスワード・サンプルを実行する実装例

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;
import com.tangosol.net.Service;
 
 
/**
* 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 IDトランスフォーマおよびアサータの有効化

IDトランスフォーマ(Extendクライアント上でSubjectをトークンに変換するクラス)およびアイデンティティ・アサータ(クラスタ上でトークンを検証するクラス)を定義するクラスを識別する、オペレーション・オーバーライド・ファイル(tangosol-coherence-override.xml)を構成します。

  1. 「Project Explorer」ウィンドウから、tangosol-coherence-override.xmlファイルを開きます。ファイルはSecurity/appClientModuleディレクトリの下位にあります。

  2. security-configスタンザ内のidentity-transformer要素とidentity-asserter要素を使用して、PasswordIdentityTransformerPasswordIdentityAsserterのそれぞれの実装クラスへのフルパスを識別します。subject-scopeパラメータをtrueに設定して、現在のセキュリティ・コンテキストのIDをキャッシュおよびクライアントに返されるリモート起動サービス参照に関連付けます。

例10-5は、考えられるtangosol-coherence-override.xmlファイルの実装を示しています。

例10-5 IDトランスフォーマとアサータの指定

<?xml version="1.0" encoding="UTF-8"?>

<?xml version="1.0" encoding="UTF-8"?>
<coherence xmlns="http://xmlns.oracle.com/coherence/coherence-operational-config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-operational-config http://xmlns.oracle.com/coherence/coherence-operational-config/1.2/coherence-operational-config.xsd">
 <!--coherence-version:12.1.3-->
  <security-config>
    <identity-asserter>
      <class-name>com.oracle.handson.PasswordIdentityAsserter</class-name>
    </identity-asserter>
    <identity-transformer>
      <class-name>com.oracle.handson.PasswordIdentityTransformer</class-name>
    </identity-transformer>
    <subject-scope>true</subject-scope>
  </security-config>
</coherence>

10.2.6 Extendクライアントのキャッシュ構成ファイルの作成

Extendクライアントのキャッシュ構成ファイルは、キャッシュ操作をクラスタ内の拡張プロキシにルーティングします。実行時に、キャッシュ操作はローカルで実行されるのではなく、拡張プロキシ・サービスに送信されます。

Extendクライアントのキャッシュ構成ファイルを作成するには:

  1. 「Project explorer」ウィンドウから、coherence-cache-config.xmlファイルを開きます。ファイルはSecurity/appClientModuleディレクトリの下位にあります。

  2. ファイルをclient-cache-config.xmlという名前で保存します。

  3. Extendクライアント・キャッシュ構成を作成します。次のリストは、主な要素の一部を取り上げています。

    • cache-name要素を使用して、キャッシュの名前としてsecurityを定義します。クラスタ側のキャッシュ構成でもsecurityという名前のキャッシュが定義されている必要があります。

    • remote-cache-schemeタンザを使用して、リモート・キャッシュに関する詳細を定義します。

    • tcp-initiatorスタンザのaddress要素とport要素を使用して、ポート9099localhostアドレスのリスニングを行う拡張プロキシ・サービスを識別します。

    • defaultsと値pofserializerを使用して、カスタムPOF構成ファイルのシリアライザ(この章の後述部分で作成します)をコールします。

例10-6は、考えられるclient-cache-config.xmlファイルの実装を示しています。

例10-6 Extendクライアント・キャッシュ構成ファイルのサンプル

<?xml version="1.0"?>
<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
              xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd">
  <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 拡張プロキシのキャッシュ構成ファイルの作成

拡張プロキシ・サービスのキャッシュ構成ファイルを作成するには:

  1. 「Project explorer」ウィンドウから、coherence-cache-config.xmlファイルを開きます。ファイルはSecurity/appClientModuleディレクトリの下位にあります。

  2. ファイルをexamples-cache-config.xmlという名前で保存します。

  3. 拡張プロキシ・キャッシュ構成ファイルを構成します。次のリストは、主な要素の一部を取り上げています。

    • cache-name要素を使用して、キャッシュの名前としてsecurityを定義します。Extendクライアントのキャッシュ構成でもsecurityという名前のキャッシュが定義されている必要があります。

    • acceptor-configスタンザのaddress要素とport要素を使用して、ポート9099localhostアドレスのリスニングを行う拡張プロキシ・サービスを識別します。

    • システム・プロパティtangosol.coherence.extend.enabledautostart要素を使用して、キャッシュ・サーバーでのプロキシ・サービスの実行を防止します。

    • defaultsと値pofserializerを使用して、カスタムPOF構成ファイルのシリアライザ(この章の後述部分で作成します)をコールします。

例10-7は、考えられるexamples-cache-config.xmlファイルの実装を示しています。

例10-7 プロキシ・サーバーのキャッシュ構成ファイルのサンプル

<?xml version="1.0"?>
 
<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
              xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd">
  <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>
          </local-address>
        </tcp-acceptor>
      </acceptor-config>
 
       <autostart>true</autostart>
    </proxy-scheme>
  </caching-schemes>
</cache-config>

10.2.8 キャッシュ・サーバーの起動構成の作成

キャッシュ・サーバー・クラスタ・ノードの起動構成を作成します。この構成には、プロキシ・サービスおよびクラスタ側のキャッシュ構成ファイルを指定するシステム・プロパティを含める必要があります。また、クラス・パス上のアプリケーション・クラス・ファイルおよびXML構成ファイルも含める必要があります。

キャッシュ・サーバーの起動ファイルを作成するには:

  1. Eclipseで、起動構成を作成します。Securityプロジェクトを右クリックして、「Run As」「Run Configurations」を選択します。「Name」フィールドにSecurityCacheServerと入力します。

  2. 「Main」タブの「Project」フィールドで「Browse」をクリックして「Security」プロジェクトを選択します。「Include system libraries when searching for a main class」を選択して、「Main class」フィールドの「Search」ボタンをクリックします。「Select Main Type」ダイアログ・ボックスの「Select type」フィールドにDefaultCacheServerを入力します。「DefaultCacheServer - com.tangosol.net」を選択して「OK」をクリックします。「Apply」をクリックします。

  3. 「Coherence」タブに、クラスタ側のキャッシュ構成ファイルの名前と絶対パスを入力します(この例ではC:\home\oracle\workspace\Security\appClientModule\examples-cache-config.xml)。「Local storage」フィールドで、「Enabled (cache server)」を選択します。「Cluster port」フィールドに3155などの一意の値を入力します。「Apply」をクリックします。

  4. 「Classpath」タブが図10-1のように表示されます。

    図10-1 セキュリティ・キャッシュ・サーバーのクラスパス

    セキュリティ・キャッシュ・サーバーのクラスパス
    「図10-1 セキュリティ・キャッシュ・サーバーのクラスパス」の説明

  5. 「Common」タブの「Shared file」フィールドで、「Browse」をクリックしてSecurityプロジェクトを選択します。「Apply」をクリックします。

10.2.9 キャッシュ・サーバーでのプロキシ・サービスの起動構成の作成

クラスタ内のキャッシュ・サーバー上で拡張プロキシサービスを起動する構成を作成します。Extendクライアントはこのサービスに接続します。この構成には、プロキシ・サービスおよびクラスタ側のキャッシュ構成ファイルを指定するシステム・プロパティを含める必要があります。また、クラス・パス上のアプリケーション・クラス・ファイルおよびXML構成ファイルも含める必要があります。

この例の場合、プロキシ・サービスの起動が構成されるキャッシュ・サーバーの構成は、キャッシュ・サーバーの起動構成と同じですが、拡張プロキシを有効にするシステム・プロパティが含まれます。

プロキシ・サービスを含むキャッシュ・サーバーの起動ファイルを作成するには:

  1. Eclipseで、起動構成を作成します。Securityプロジェクトを右クリックして、「Run As」「Run Configurations」を選択します。「Name」フィールドにSecurityRunProxyと入力します。

  2. 「Main」タブの「Project」フィールドで「Browse」をクリックして「Security」プロジェクトを選択します。「Include system libraries when searching for a main class」を選択して、「Main class」フィールドの「Search」ボタンをクリックします。「Select Main Type」ダイアログ・ボックスの「Select type」フィールドにDefaultCacheServerを入力します。「DefaultCacheServer - com.tangosol.net」を選択して「OK」をクリックします。「Apply」をクリックします。

  3. 「Coherence」タブに、クラスタ側のキャッシュ構成ファイルの名前と絶対パスを入力します(この例ではC:\home\oracle\workspace\Security\appClientModule\examples-cache-config.xml)。「Local storage」フィールドで、「Enabled (cache server)」を選択します。「Cluster port」フィールドに3155などの一意の値を入力します。「Apply」をクリックします。

  4. 「Arguments」タブで、システム・プロパティ-Dtangosol.coherence.extend.enabled=trueを入力して、「VM arguments」フィールドのプロキシ・サービスを指定します。

    図10-2 セキュリティ・プロキシ・サーバーの「Arguments」タブ

    セキュリティ・プロキシ・サーバーの「Arguments」タブ
    「図10-2 セキュリティ・プロキシ・サーバーの「Arguments」タブ」の説明

  5. 「Classpath」タブが図10-3のように表示されます。

    図10-3 プロキシ・サーバーのクラスパス

    プロキシ・サーバーのクラスパス
    「図10-3 プロキシ・サーバーのクラスパス」の説明

  6. 「Common」タブの「Shared file」フィールドで、「Browse」をクリックしてSecurityプロジェクトを選択します。「Apply」をクリックします。

10.2.10 パスワード・サンプルの実行

パスワード・サンプルを実行して、トークンの生成、検証、プロキシ・サービスへの引渡しを行います。

  1. PasswordExample.javaファイルの実行構成を作成します。

    1. 「Project Explorer」PasswordExample.javaを右クリックして、「Run As」「Run Configurations」を選択します。

    2. 「New launch configuration」アイコンをクリックします。PasswordExample「Name」フィールドに、Security「Project」フィールドに、com.oracle.handson.PasswordExample「Main class」フィールドに表示されていることを確認します。「Apply」をクリックします。

    3. 「Coherence」タブの「Cache configuration descriptor」フィールドにクライアント・キャッシュ構成ファイルへのパスC:\home\oracle\workspace\Security\appClientModule\client-cache-config.xmlを入力します「Local cache」フィールドで、「Disabled (cache client)」を選択します。「Cluster port」フィールドに3155などの一意の値を入力します。

    4. 「Classpath」タブが図10-4のように表示されます。

      図10-4 PasswordExampleプログラムの「Classpath」タブ

      PasswordExampleプログラムの「Classpath」タブ
      「図10-4 PasswordExampleプログラムの「Classpath」タブ」の説明

    5. 「Common」タブの「Shared file」フィールドで、「Browse」をクリックしてSecurityプロジェクトを選択します。「Apply」をクリックします。

  2. 実行中のキャッシュ・サーバーがあれば停止します。詳細は、「キャッシュ・サーバーの停止」を参照してください。

  3. セキュリティ・プロキシ・サーバー、セキュリティ・キャッシュ・サーバー、続いてPasswordExample.javaプログラムを実行します。

    1. プロジェクトを右クリックして、「Run As」「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスから、SecurityRunProxy構成を実行します。

    2. プロジェクトを右クリックして、「Run As」「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスから、SecurityCacheServer構成を実行します。

    3. 「Project Explorer」PasswordExample.javaファイルを右クリックして、「Run As」「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスでPasswordExampleを選択し、「Run」をクリックします。

Eclipseコンソールに、PasswordExampleプログラムの出力が例10-8のように表示されます。これにはパスワード・サンプルの開始、プロキシ・サーバーへのソケットのオープンおよびこの例の完了が示されます。

例10-8 Eclipseコンソールでのパスワード・サンプルの出力

------password example begins------
2014-01-09 11:05:04.690/0.380 Oracle Coherence 12.1.3.0.0 <Info> (thread=main, member=n/a): Loaded operational configuration from "jar:file:/C:/OracleCoherence/coherence/lib/coherence.jar!/tangosol-coherence.xml"
2014-01-09 11:05:04.800/0.490 Oracle Coherence 12.1.3.0.0 <Info> (thread=main, member=n/a): Loaded operational overrides from "jar:file:/C:/OracleCoherence/coherence/lib/coherence.jar!/tangosol-coherence-override-dev.xml"
... 
Oracle Coherence Version 12.1.3.0.0 Build 49331
 Grid Edition: Development mode
Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
 
...
2014-01-09 11:05:06.472/2.162 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=main, member=n/a): Connecting Socket to 10.159.156.144:9099
2014-01-09 11:05:06.473/2.163 Oracle Coherence GE 12.1.3.0.0 <Info> (thread=main, member=n/a): Connected Socket to 10.159.156.144:9099
------password example succeeded------
------password example completed------

プロキシ・サーバー・シェルの応答は、例10-9のように表示されます。これには、識別名からのCNおよびOUの値およびパスワードが検証されたかどうかがリスト表示されます。

例10-9 プロキシ・サービス・シェルを実行するキャッシュ・サーバーからの応答

...
Started DefaultCacheServer...
 
2014-01-09 11:04:35.313/34.291 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=Cluster, member=1): Member(Id=2, Timestamp=2014-01-09 11:04:35.113, Address=10.159.156.144:8090, MachineId=47251, Location=site:,machine:TPFAEFFL-LAP,process:3896, Role=CoherenceServer) joined Cluster with senior member 1
2014-01-09 11:04:35.405/34.383 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=Invocation:Management, member=1): Member 2 joined Service Management with senior member 1
2014-01-09 11:04:36.175/35.153 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=DistributedCache:PartitionedPofCache, member=1): Member 2 joined Service PartitionedPofCache with senior member 1
2014-01-09 11:04:37.243/36.221 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=DistributedCache:PartitionedPofCache, member=1): Transferring 129B of backup[1] for PartitionSet{128..256} to member 2
2014-01-09 11:04:37.409/36.387 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=DistributedCache:PartitionedPofCache, member=1): Transferring primary PartitionSet{0..127} to member 2 requesting 128
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 クラスタに対するロールベース・アクセス制御の組込み

この項では、クラスタへのアクセスにロールベースのポリシーを使用するサンプルを作成する方法を説明します。このコードは、特定のロールに割り当てられたユーザーIDを使用して、ログインし、サブジェクトを取得します。そのサブジェクトのコンテキストで実行中のキャッシュ参照を取得して、様々なキャッシュ操作を試行します。ユーザーに付与されたロールに応じて、キャッシュ操作が許可または拒否されます。このサンプルのロールのマッピングおよびロールベースの認可は簡略化されており、実際のセキュリティ保護での使用は想定していません。

たとえば、ライター・ロールのユーザーは、putgetのメソッドを使用できます。リーダー・ロールのユーザーは、getメソッドを使用できますが、putメソッドは使用できません。ライター・ロールのユーザーは、キャッシュを破棄できませんが、管理ロールのユーザーはできます。

サブジェクトのコンテキストでキャッシュ参照が作成されると、IDが永続的にその参照と関連付けられることに注意してください。キャッシュ参照の使用すべてがそのIDの代理として行われます。

この例では、前の項で作成したPasswordIdentityTransformerクラスとPasswordIdentityAsserterクラスを使用します。PasswordIdentityTransformerクラスは、パスワード、ユーザーIDおよびロールを格納するセキュリティ・トークンを生成します。PasswordIdentityAsserterクラス(プロキシで実行)は、セキュリティ・トークンを検証してパスワードを強制し、適切なユーザーIDとロールでサブジェクトを構成します。セキュリティ・トークンの生成とアサーションは自動的に実行されます。

サンプルを作成するには:

  1. キャッシュ・メソッドへのアクセスの資格が付与されるユーザー・ロールの定義

  2. キャッシュ・サービスへの資格の適用

  3. アクセス制御のサンプル・プログラムの作成

  4. クラスタ側のキャッシュ構成ファイルの編集

  5. アクセス制御のサンプルの実行

10.3.1 キャッシュ・メソッドへのアクセスの資格が付与されるユーザー・ロールの定義

ユーザー・ロールを基準にして、キャッシュ・メソッドへのアクセスを可能にするJavaファイルを作成します。これを行うには、Coherence*Extendを使用し、クライアントから渡されたSubjectオブジェクトを使用して、ラップされたNamedCacheにアクセス権を適用できます。この実装では、指定されたロールのクライアントのみに、ラップされたNamedCacheへのアクセスが許可されます。

この項で作成するクラスは、com.tangosol.net.cache.WrapperNamedCacheクラスを拡張します。このクラスは、NamedCacheインタフェースのメソッドの保護を可能にする便利なファンクションです。

キャッシュ・メソッドへのアクセスが可能なユーザー・ロールを決定するため、キャッシュ・メソッドの実装のそれぞれにSecurityExampleHelper.checkAccessメソッドのコールをインクルードします。checkAccessに対する引数として、メソッドへのアクセスを許可するユーザー・ロールを指定します。superのコールによって実装を終了します。

たとえば、次のコードでは、adminロールのユーザーがキャッシュを破棄できることが指定されます。

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

このサンプルでは、readerロールのユーザーにaggregateメソッドのコールが許可されます。

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

キャッシュ・メソッドをコールできるユーザーを決定するファイルを作成するには:

  1. SecurityプロジェクトにEntitledNamedCacheという名前のJavaクラスを新規作成します。

    詳細を確認する場合は、「Javaクラスの作成」を参照してください。

  2. このクラスがWrapperNamedCacheをインポートして拡張することを確認します。

  3. FilterMapListenerValueExtractorCollectionComparatorMapServiceおよびSetの各クラスをインポートします。WrapperNamedCache内のメソッド(および拡張によるEntitledNamedCache)ではこれらのデータ型の引数が使用されます。

  4. 特定のロールのユーザーのみにメソッドのコールが許可されるようにして、EntitledNamedCacheにメソッドを実装します。

例10-10は、考えられるEntitledNamedCacheの実装を示しています。

例10-10 資格が付与される名前付きキャッシュ

package com.oracle.handson;
 
import com.tangosol.net.NamedCache;
import com.tangosol.net.security.SecurityHelper;
 
import com.tangosol.net.Service;
import com.tangosol.net.cache.WrapperNamedCache;
 
import com.tangosol.util.Filter;
import com.tangosol.util.MapEvent;
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;
 
import javax.security.auth.Subject;
 
 
/**
* 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(new EntitledMapListener(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(new EntitledMapListener(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(new EntitledMapListener(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);
        }
 
    // ----- inner class ----------------------------------------------------
 
    /**
    * Example MapListener that adds authorization to map events.
    */
    public class EntitledMapListener
            implements MapListener
        {
        // ----- constructors -------------------------------------------
 
        /**
        * Construct an EntitledMapListener with the current subject.
        * The subject will not be available in the security context
        * when events are received by the proxy at runtime.
        *
        * @param  listener  the MapListener
        */
        public EntitledMapListener(MapListener listener)
            {
            m_listener = listener;
            m_subject  = SecurityHelper.getCurrentSubject();
            }
 
 
        // ----- MapListener interface ----------------------------------
 
        /**
        * {@inheritDoc}
        */
        public void entryInserted(MapEvent mapEvent)
            {
            try
                {
                SecurityExampleHelper.checkAccess(
                    SecurityExampleHelper.ROLE_WRITER, m_subject);
 
                }
            catch (SecurityException e)
                {
                System.out.println("Access denied for entryInserted");
                return;
                }
            m_listener.entryInserted(mapEvent);
            }
 
        /**
        * {@inheritDoc}
        */
        public void entryUpdated(MapEvent mapEvent)
            {
            try
                {
                SecurityExampleHelper.checkAccess(
                    SecurityExampleHelper.ROLE_WRITER, m_subject);
 
                }
            catch (SecurityException e)
                {
                System.out.println("Access denied for entryUpdated");
                return;
                }
            m_listener.entryUpdated(mapEvent);
            }
 
        /**
        * {@inheritDoc}
        */
        public void entryDeleted(MapEvent mapEvent)
            {
            try
                {
                SecurityExampleHelper.checkAccess(
                    SecurityExampleHelper.ROLE_WRITER, m_subject);
 
                }
            catch (SecurityException e)
                {
                System.out.println("Access denied for entryDeleted");
                return;
                }
            m_listener.entryDeleted(mapEvent);
            }
 
 
        //  ----- data members ------------------------------------------
 
        /**
        * Subject from security context when the MapListener was registered
        */
        private Subject m_subject;
 
        /**
        * Registered listener
        */
        private MapListener m_listener;
        }
 
    // ----- helper methods -------------------------------------------------
 
    /**
    * Return the wrapped NamedCache.
    *
    * @return  the wrapped CacheService
    */
    public NamedCache getNamedCache()
        {
        return (NamedCache) getMap();
        }
    }

10.3.2 キャッシュ・サービスへの資格の適用

Coherence*Extendを使用し、クライアントから渡されたSubjectを使用して、ラップされたCacheServiceにアクセス資格を適用する方法を示すファイルを作成します。この実装は、前の項で作成したEntitledNamedCacheにキャッシュ操作のアクセス制御を委譲します。

作成するクラスは、com.tangosol.net.WrapperCacheServiceクラスを拡張します。これは、CacheServiceのメソッドの保護を可能にする便利なファンクションです。また、プロキシ上のキャッシュ・サービスとクライアントのリクエスト間で委譲を行うメカニズムも提供します。

メソッドensureCachereleaseCacheおよびdestroyCacheを実装して、これらの使用が特定ロールのユーザーのみに許可されるように保証します。実装には、特定のユーザー・ロールを引数としてSecurityExampleHelper.checkAccessメソッドのコールをインクルードします。たとえば、次のコードでは、adminロールのユーザーのみにキャッシュの破棄が許可されることが保証されます。

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

キャッシュ・サービスへのアクセスに資格を適用するファイルを作成するには:

  1. SecurityプロジェクトにEntitledCacheServiceという名前のJavaクラスを新規作成します。

  2. このクラスがWrapperCacheServiceクラスをインポートして拡張することを確認します。

  3. ensureCachereleaseCacheおよびdestroyCacheのメソッドを実装して、これらの使用が特定ロールのユーザーのみに許可されるように保証します。

例10-11は、考えられるEntitledCacheServiceの実装を示しています。

例10-11 資格が付与されるキャッシュ・サービス

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 アクセス制御のサンプル・プログラムの作成

アクセス制御のサンプルを実行するファイルを作成します。ロール・ポリシーは、SecurityExampleHelperクラスで定義されます。EntitledCacheServiceクラスとEntitledNamedCacheクラスによってポリシーが強制されます。

このプログラムは、SecurityHelperFile.loginメソッドに対する引数として様々なユーザーを指定して、キャッシュの読取り、書込みおよび破棄の操作の実行を試行する必要があります。EntitledCacheServiceクラスとEntitledNamedCacheクラスで定義された資格ポリシーに従って、操作の成功、または失敗が決まります。

  1. AccessControlExampleという名前のSecurityプロジェクトに、mainメソッドを使用して新たなJavaクラスを作成します。

    詳細は、「Javaクラスの作成」を参照してください。

  2. キャッシュにアクセスするmainメソッドを実装します。

  3. SecurityExampleHelperファイルで定義されたユーザーをloginメソッドの引数として指定します。

  4. ユーザーごとに、キャッシュに対する読取り(get)、書込み(put)および destroy操作を実行し、応答とする成功または失敗のメッセージを指定します。

例10-12は、考えられるAccessControlExample.javaクラスの実装を示しています。

例10-12 アクセス制御のサンプルを実行するサンプル・プログラム

package com.oracle.handson;
 
import com.tangosol.net.CacheFactory;
import com.tangosol.net.InvocationService;
import com.tangosol.net.NamedCache;
import com.tangosol.net.Service;
 
import com.tangosol.util.MapEvent;
import com.tangosol.util.MapListener;
 
import java.security.PrivilegedExceptionAction;
 
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 クラスタ側のキャッシュ構成ファイルの編集

クラスタ側のキャッシュ構成ファイルexamples-cache-config.xmlを編集します。cache-service-proxyスタンザのproxy-configで、キャッシュ・サービスのクラス名のフルパスを指定します。cache-service-proxyスタンザには、プロキシ・サービスによって管理されるキャッシュ・サービス・プロキシの構成情報が格納されます。

この例のキャッシュ・サービス・クラス名はcom.oracle.handson.EntitledCacheServiceプロキシで、param-typeは、com.tangosol.net.CacheServiceです。

例10-13は、構成に追加するXMLコードを示しています。

例10-13 クラスタ側のキャッシュ構成のキャッシュ・サービス・プロキシ構成

...
      <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 アクセス制御サンプルの実行

アクセス制御サンプルを実行し、ユーザーのロールを基準にしてキャッシュへのアクセスがどのように許可または拒否されるかを示します。

  1. AccessControlExample.javaの実行構成を作成します。

    1. 「Project Explorer」で、AccessControlExampleを右クリックします。「Run As」「Run Configurations」を選択します。

    2. 「Oracle Coherence」「New launch configuration」アイコンを選択します。AccessControlExample「Name」フィールドに、Security「Project」フィールドに、com.oracle.handson.AccessControlExample「Main class」フィールドに表示されていることを確認します。「Apply」をクリックします。

    3. 「Coherence」タブの「Cache configuration descriptor」フィールドにclient-cache-config.xmlファイルへのパスを入力します。「Local cache」フィールドで、「Disabled (cache client)」を選択します。「Cluster port」フィールドに3155と入力します。

    4. 「Classpath」タブで、図10-5のように「User Entries」が表示されていることを確認します。「Apply」「Close」をクリックします。

      図10-5 AccessControlExampleプログラムの「Classpath」タブ

      AccessControlExampleプログラムの「Classpath」タブ
      「図10-5 AccessControlExampleプログラムの「Classpath」タブ」の説明

    5. 「Common」タブの「Shared file」フィールドで、「Browse」をクリックしてSecurityプロジェクトを選択します。「Apply」をクリックします。

  2. 実行中のキャッシュ・サーバーがあれば停止します。詳細は、「キャッシュ・サーバーの停止」を参照してください。

  3. セキュリティ・プロキシ・サーバー、セキュリティ・キャッシュ・サーバー、続いてAccessControlExample.javaプログラムを実行します。

    1. プロジェクトを右クリックして、「Run As」「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスから、SecurityRunProxy構成を実行します。

    2. プロジェクトを右クリックして、「Run As」「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスから、SecurityCacheServer構成を実行します。

    3. 「Project Explorer」AccessControlExample.javaファイルを右クリックして、「Run As」「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスから、AccessControlExample構成を実行します。

Eclipseコンソールに、例10-14のような出力が表示されます。メッセージは、AccessControlExampleで指定され、キャッシュの読取り、書込みおよび破棄操作を実行する様々なユーザーに対応します。

  • メッセージ「Success: read and write allowed」は、キャッシュの読取りおよび書込みを試行するロールwriterのユーザーに対応します。

  • メッセージ「Success: read allowed」は、キャッシュの読取りを試行するロールreaderのユーザーに対応します。

  • メッセージ「Success: Correctly cannot write」は、キャッシュへの書込みを試行するロールreaderのユーザーに対応します。

  • メッセージ「Success: Correctly cannot destroy the cache」は、キャッシュの破棄を試行するロールwriterのユーザーに対応します。

  • メッセージ「Success: Correctly allowed to destroy the cache」は、キャッシュの破棄を試行するロールadminのユーザーに対応します。

例10-14 Eclipseコンソールのアクセス制御サンプルの出力

------cache access control example begins------
2014-01-16 13:15:02.802/0.413 Oracle Coherence 12.1.3.0.0 <Info> (thread=main, member=n/a): Loaded operational configuration from "jar:file:/C:/OracleCoherence/coherence/lib/coherence.jar!/tangosol-coherence.xml"
2014-01-16 13:15:02.901/0.512 Oracle Coherence 12.1.3.0.0 <Info> (thread=main, member=n/a): Loaded operational overrides from "jar:file:/C:/OracleCoherence/coherence/lib/coherence.jar!/tangosol-coherence-override-dev.xml"
...
2014-01-16 13:15:04.593/2.204 Oracle Coherence GE 12.1.3.0.0 <Info> (thread=main, member=n/a): Connected Socket to 130.35.99.90:9099
    Success: read and write allowed
2014-01-16 13:15:05.695/3.306 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=main, member=n/a): Connecting Socket to 130.35.99.90:9099
2014-01-16 13:15:05.696/3.307 Oracle Coherence GE 12.1.3.0.0 <Info> (thread=main, member=n/a): Connected Socket to 130.35.99.90:9099
    Success: read allowed
    Success: Correctly cannot write
    Success: Correctly cannot destroy the cache
2014-01-16 13:15:06.178/3.789 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=main, member=n/a): Connecting Socket to 130.35.99.90:9099
2014-01-16 13:15:06.179/3.790 Oracle Coherence GE 12.1.3.0.0 <Info> (thread=main, member=n/a): Connected Socket to 130.35.99.90:9099
    Success: Correctly allowed to destroy the cache
------cache access control example completed------

例10-15は、キャッシュ・サーバーでプロキシ・サービスが実行される場合のシェルの出力をリストしています。出力内のセキュリティの例外は、Eclipseコンソールのメッセージ「Success: Correctly cannot write」および「Success: Correctly cannot destroy the cache」に対応することに注意します。

例10-15 プロキシ・サービスを実行中のキャッシュ・サーバーの出力

 
Started DefaultCacheServer...
 
2014-01-16 13:14:46.473/25.406 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=Cluster, member=1): Member(Id=2, Timestamp=2014-01-16 13:14:46.303, Address=130.35.99.90:8090, MachineId=47251, Location=site:,machine:TPFAEFFL-LAP,process:6516, Role=CoherenceServer) joined Cluster with senior member 1
2014-01-16 13:14:46.564/25.497 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=Invocation:Management, member=1): Member 2 joined Service Management with senior member 1
2014-01-16 13:14:48.361/27.294 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=Cluster, member=1): TcpRing disconnected from Member(Id=2, Timestamp=2014-01-16 13:14:46.303, Address=130.35.99.90:8090, MachineId=47251, Location=site:,machine:TPFAEFFL-LAP,process:6516, Role=CoherenceServer) due to a peer departure; removing the member.
2014-01-16 13:14:48.361/27.294 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=Cluster, member=1): Member(Id=2, Timestamp=2014-01-16 13:14:48.361, Address=130.35.99.90:8090, MachineId=47251, Location=site:,machine:TPFAEFFL-LAP,process:6516, Role=CoherenceServer) left Cluster with senior member 1
2014-01-16 13:14:48.362/27.295 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=Invocation:Management, member=1): Member 2 left service Management with senior member 1
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
2014-01-16 13:15:05.706/44.639 Oracle Coherence GE 12.1.3.0.0 <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:105)
    at com.oracle.handson.SecurityExampleHelper.checkAccess(SecurityExampleHelper.java:59)
    at com.oracle.handson.EntitledNamedCache.put(EntitledNamedCache.java:68)
    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:356)
    at com.tangosol.coherence.component.net.extend.Channel.execute(Channel.CDB:48)
    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:65)
    at com.tangosol.coherence.component.util.Daemon.run(Daemon.CDB:51)
    at java.lang.Thread.run(Thread.java:724)
 
2014-01-16 13:15:05.742/44.675 Oracle Coherence GE 12.1.3.0.0 <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:105)
    at com.oracle.handson.SecurityExampleHelper.checkAccess(SecurityExampleHelper.java:59)
    at com.oracle.handson.EntitledCacheService.destroyCache(EntitledCacheService.java:60)
    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:12)
    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:356)
    at com.tangosol.coherence.component.net.extend.Channel.execute(Channel.CDB:48)
    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:65)
    at com.tangosol.coherence.component.util.Daemon.run(Daemon.CDB:51)
    at java.lang.Thread.run(Thread.java:724)
 
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
Password validated for user: CN=BuckarooBanzai,OU=Yoyodyne
2014-01-16 13:15:06.196/45.129 Oracle Coherence GE 12.1.3.0.0 <Error> (thread=Proxy:ProxyService:TcpAcceptor, member=1): Error unregistering channel from receiver: NamedCacheProxy(NamedCache=security): java.lang.SecurityException: Access denied, insufficient privileges
    at com.oracle.handson.SecurityExampleHelper.checkAccess(SecurityExampleHelper.java:105)
    at com.oracle.handson.SecurityExampleHelper.checkAccess(SecurityExampleHelper.java:59)
    at com.oracle.handson.EntitledNamedCache.removeMapListener(EntitledNamedCache.java:86)
    at com.tangosol.coherence.component.net.extend.proxy.NamedCacheProxy.unregisterChannel(NamedCacheProxy.CDB:21)
...

10.4 起動可能オブジェクトに対するロールベース・アクセス制御の組込み

起動サービス・クラスタ・サービスを使用すると、Extendクライアントによるクラスタ上の起動可能オブジェクトの実行が可能になります。このサンプルは、ロールベース・ポリシーを使用して起動可能オブジェクトの実行が可能なユーザーをどのように決定できるかを示しています。

たとえば、ライター・ロールのユーザーは、起動可能オブジェクトを実行できます。リーダー・ロールのユーザーはできません。

このサンプルでは、クライアント・プログラムからコールできる起動可能オブジェクトを作成します。起動可能オブジェクトはシリアライズ可能であるため、POF構成ファイルにこれをリストする必要もあります。また、ユーザーのロールを基準にして、特定のユーザーがサービス上のメソッドを実行できるかどうかをテストする起動サービス・プログラムも作成します。

前出のサンプルと同様、この例でもPasswordIdentityTransformerクラスを使用して、パスワード、ユーザーIDおよびロールが格納されるセキュリティ・トークンを使用します。パスワードを強制し、適正なユーザーIDとロールでサブジェクトを構成するセキュリティ・トークンの検証には、PasswordIdentityAsserter(プロキシで実行)が使用されます。セキュリティ・トークンの生成とアサーションは自動的に実行されます。

サンプルを作成するには:

  1. 起動可能オブジェクトの作成

  2. 資格が付与される起動サービスの作成

  3. アクセス起動サービスのサンプル・プログラムの作成

  4. クラスタ側のキャッシュ構成ファイルの編集

  5. POF構成ファイルの作成

  6. サーバーの実行構成の編集

  7. アクセス起動サービス・サンプルの実行

10.4.1 起動可能オブジェクトの作成

資格が付与された起動サービスによって使用される簡略な起動可能オブジェクトの実装を作成します。たとえば、整数の増分および戻りを実行する起動可能オブジェクトを作成できます。

起動可能オブジェクトを作成するには:

  1. SecurityプロジェクトにExampleInvocableという名前のJavaクラスを新規作成します。

    詳細は、「Javaクラスの作成」を参照してください。

  2. InvocableインタフェースとInvocationServiceインタフェースをインポートします。このクラスはシリアライズ可能オブジェクトを処理するため、PortableObjectPofReaderおよびPofWriterのクラスをインポートします。

  3. ExampleInvocableクラスがInvocableおよびPortableObjectを実装することを保証します。

  4. ExampleInvocableクラスを実装して、整数を増分し、結果を返します。

  5. PofReader.readExternalメソッドとPofWriter.writeExternalメソッドを実装します。

例10-16は、考えられるExampleInvocable.javaの実装を示しています。

例10-16 起動可能オブジェクトのサンプル

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

  /**   *    */     private static final long serialVersionUID = 1L; 

    /**
     * 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 資格が付与される起動サービスの作成

このサンプルは、リモート起動サービスをラップしてアクセス制御を提供する方法を示しています。Coherence*Extendを使用すると、クライアントから渡されるSubjectを使用して、ラップされるInvocationServiceにアクセス資格を適用できます。この実装では、ラップされた起動サービスへのアクセスが指定されたロールのクライアントのみに許可されます。

作成するクラスは、com.tangosol.net.WrapperInvocationServiceクラスを拡張します。これは、InvocationServiceのメソッドの保護を可能にする便利なファンクションです。また、プロキシ上の起動サービスとクライアントのリクエスト間で委譲を行うメカニズムも提供します。

資格が付与される起動サービスを作成するには:

  1. SecurityプロジェクトにEntitledInvocationServiceという名前のJavaクラスを新規作成します。

  2. InvocableInvocationObserverInvocationServiceWrapperInvocationServiceMapおよびSetのインタフェースをインポートします。EntitledInvocationServiceクラスがWrapperInvocationServiceクラスを拡張することを確認します。

  3. queryメソッドとexecuteメソッドを実装します。指定されたユーザー・ロール(このサンプルではROLE_WRITER)がこれらの操作にアクセスできるかどうかを判別するため、この実装に、SecurityExampleHelper.checkAccessメソッドのコールをインクルードします。

例10-17は、考えられるEntitledInvocationService.javaの実装を示しています。

例10-17 資格が付与される起動サービスのサンプル

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 アクセス起動サービスのサンプル・プログラムの作成

アクセス起動サービス・サンプルを実行するプログラムを作成します。このプログラムの目的は、SecurityExampleHelperクラスで定義されている多様なユーザーが起動可能オブジェクトにアクセスして実行できるかどうかをテストすることです。ロールベース・ポリシーの強制は、EntitledInvocationServiceクラスによって指定されます。

アクセス起動サービスのサンプルを実行するプログラムを作成するには:

  1. AccessInvocationServiceExample.javaという名前のSecurityプロジェクトに、mainメソッドを使用してJavaクラスを作成します。

    詳細は、「Javaクラスの作成」を参照してください。

  2. クラスの中から、ExampleInvocableCacheFactoryおよびInvocationServiceをインポートします。

  3. accessInvocationServiceメソッドを起動するmainメソッドを実装します。

  4. accessInvocationServiceクラスを実装して、SecurityExampleHelperクラスで定義された様々なユーザーが、サービスへのログイン、およびExampleInvocableで定義されたオブジェクトの実行を試行できるようにします。SecurityExampleHelper.loginメソッドを使用して、起動可能サービスにそれぞれのユーザーがアクセスできるかどうかをテストします。

例10-18は、考えられるAccessInvocationServiceExample.javaの実装を示しています。

例10-18 アクセス起動サービス・サンプルを実行するサンプル・プログラム

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 クラスタ側のキャッシュ構成ファイルの編集

examples-cache-config.xmlファイルを編集して、起動サービスへのフルパスをinvocation-service-proxyスタンザのproxy-configに追加します。invocation-service-proxyスタンザには、プロキシ・サービスによって管理される起動サービス・プロキシの構成情報が格納されます。

このサンプルの起動サービス・クラス名はcom.oracle.handson.EntitledInvocationServiceで、そのparam-typeは、com.tangosol.net.InvocationServiceです。

例10-19 クラスタ側キャッシュの起動サービス・プロキシ構成

   ...
      <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 POF構成ファイルの作成

ユーザー定義型としてExampleInvocableを宣言するPOFファイルを作成します。

  1. 「Project Explorer」で、Security\appClientModulepof-config.xmlファイルを探し、Eclipse IDEで開きます。

  2. ユーザー定義型としてExampleInvocableを宣言するコードを入力して、ファイルを保存します。

    このファイルの内容は、例10-20のようになります。このファイルは、C:\home\oracle\workspace\Security\appClientModuleフォルダに保存されます。

例10-20 ExampleInvocableユーザー定義型のPOF構成ファイル

<?xml version="1.0"?>
<pof-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns="http://xmlns.oracle.com/coherence/coherence-pof-config"    xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-pof-config http://xmlns.oracle.com/coherence/coherence-pof-config/1.2/coherence-pof-config.xsd">
  <user-type-list>
  <!-- include all "standard" 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 サーバーの実行構成の編集

クラスローダーは、coherence.jarファイル内での参照前に、カスタムPOF構成ファイルpof-config.xmlを検出する必要があります。このようにされないと、カスタムPOF構成ファイルが無視され、coherence.jarファイル内のデフォルト・ファイルが使用されることになります。

アプリケーションにC:\home\oracle\workspace\Security\appModuleのXML構成ファイルを使用させるには、サーバーのクラス・パスの「Bootstrap Entries」セクションにCoherence12.1.3ライブラリが表示されていないことを確認します。また、coherence.jarファイルをUser EntriesセクションのSecurityフォルダの後に配置します。

サーバーのクラスパスの「Bootstrap Entries」セクションにCoherence12.1.3ライブラリが表示されている場合は、次の手順に従ってそのライブラリを削除します。

  1. 「Project Explorer」でプロジェクトを右クリックして、「Run As」「Run Configurations」を選択します。

  2. SecurityRunProxyを選択します。「Classpath」タブで「Coherence12.1.3」が表示されている場合は、「Bootstrap Entries」のリストからそれを削除します。「Add External Jars」をクリックして、Oracle_Home\coherence\libフォルダからcoherence.jarファイルを選択します。「Apply」をクリックします。

  3. SecurityCacheServerを選択します。「Classpath」タブで「Coherence12.1.3」が表示されている場合は、「Bootstrap Entries」のリストからそれを削除します。「Add External Jars」をクリックして、Oracle_Home\coherence\libフォルダからcoherence.jarファイルを選択します。「Apply」をクリックします。


注意:

クラス・パス上のcoherence.jarファイルの前に、XML構成ファイル(C:\home\oracle\workspace\SecurityおよびSecurity\build\classesフォルダ内)が表示されていることを確認します。クラスローダーは、coherence.jarファイル内での参照前に、カスタムPOF構成ファイルpof-config.xmlを検出する必要があります。


終了すると、SecurityRunProxyおよびSecurityCacheServer「Classpath」タブが図10-6のように表示されます。

図10-6 キャッシュ・サーバーおよびプロキシ・サービスを実行するサーバーのクラスパス

サーバーのクラスパス
「図10-6 キャッシュ・サーバーおよびプロキシ・サービスを実行するサーバーのクラスパス」の説明

10.4.7 アクセス起動サービス・サンプルの実行

アクセス起動サービス・サンプルを実行し、ユーザーのロールを基準にして起動可能オブジェクトへのアクセスがどのように許可または拒否されるかを示します。

  1. AccessInvocationServiceExample.javaファイルの実行構成を作成します。

    1. 「Project Explorer」AccessInvocationServiceExample.javaを右クリックして、「Run As」「Run Configurations」を選択します。

    2. 「Oracle Coherence」「New launch configuration」アイコンをクリックします。AccessInvocationServiceExample「Name」フィールドに、Security「Project」フィールドに、com.oracle.handson.AccessInvocationServiceExample.java「Main class」フィールドに表示されていることを確認します。「Apply」をクリックします。

    3. 「Coherence」タブの「Cache configuration descriptor」フィールドにclient-cache-config.xmlファイルへのパスを入力します。「Local cache」フィールドで、「Disabled (cache client)」を選択します。

    4. 「Classpath」タブで「Coherence12.1.3」が表示されている場合は、「Bootstrap Entries」リストからそれを削除します。「Add External Jars」をクリックして、coherence.jarファイルを「User Entries」に追加します。Securityフォルダを「User Entries」の先頭に移動して、coherence.jarファイルを移動します(表示されていない場合)。終了すると、「Classpath」タブが図10-7のように表示されます。「Apply」「Close」をクリックします。

      図10-7 AccessInvocationServiceExampleプログラムの「Classpath」タブ

      AccessInvocationServiceExampleプログラムの「Classpath」タブ
      「図10-7 AccessInvocationServiceExampleプログラムの「Classpath」タブ」の説明

  2. 実行中のキャッシュ・サーバーがあれば停止します。詳細は、「キャッシュ・サーバーの停止」を参照してください。

  3. セキュリティ・プロキシ・サーバー、セキュリティ・キャッシュ・サーバー、続いてAccessInvocationServiceExample.javaプログラムを実行します。

    1. プロジェクトを右クリックして、「Run As」「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスから、SecurityRunProxy構成を実行します。

    2. プロジェクトを右クリックして、「Run As」「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスから、SecurityCacheServer構成を実行します。

    3. 「Project Explorer」AccessInvocationServiceExample.javaファイルを右クリックして、「Run As」「Run Configurations」を選択します。「Run Configurations」ダイアログ・ボックスでAccessControlExampleを選択し、「Run」をクリックします。

Eclipseコンソールに、例10-21のような出力が表示されます。このメッセージは、AccessInvocationServiceExampleクラスで指定され、起動可能オブジェクトExampleInvocableを実行しようとするユーザーに対応します。

  • メッセージ「Success: Correctly allowed to use the invocation service」は、ExampleInvocableを実行しようとするロールwriterのユーザーに対応します。

  • メッセージ「Success: Correctly unable to use the invocation service」は、ExampleInvocableを実行しようとするロールreaderのユーザーに対応します。

例10-21 Eclipseコンソールのクライアント・プログラムの応答

------InvocationService access control example begins------
2014-01-16 14:20:34.509/0.370 Oracle Coherence 12.1.3.0.0 <Info> (thread=main, member=n/a): Loaded operational configuration from "jar:file:/C:/OracleCoherence/coherence/lib/coherence.jar!/tangosol-coherence.xml"
2014-01-16 14:20:34.609/0.470 Oracle Coherence 12.1.3.0.0 <Info> (thread=main, member=n/a): Loaded operational overrides from "jar:file:/C:/OracleCoherence/coherence/lib/coherence.jar!/tangosol-coherence-override-dev.xml"
2014-01-16 14:20:34.689/0.550 Oracle Coherence 12.1.3.0.0 <Info> (thread=main, member=n/a): Loaded operational overrides from "file:/C:/home/oracle/workspace_jan_2014/Security/build/classes/tangosol-coherence-override.xml"
2014-01-16 14:20:34.699/0.560 Oracle Coherence 12.1.3.0.0 <D5> (thread=main, member=n/a): Optional configuration override "cache-factory-config.xml" is not specified
2014-01-16 14:20:34.699/0.560 Oracle Coherence 12.1.3.0.0 <D5> (thread=main, member=n/a): Optional configuration override "cache-factory-builder-config.xml" is not specified
2014-01-16 14:20:34.699/0.560 Oracle Coherence 12.1.3.0.0 <D5> (thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" is not specified
 
Oracle Coherence Version 12.1.3.0.0 Build 49721
 Grid Edition: Development mode
Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
 
2014-01-16 14:20:35.039/0.900 Oracle Coherence GE 12.1.3.0.0 <Info> (thread=main, member=n/a): Loaded cache configuration from "file:/C:/home/oracle/workspace_jan_2014/Security/appClientModule/client-cache-config.xml"
2014-01-16 14:20:35.699/1.560 Oracle Coherence GE 12.1.3.0.0 <Info> (thread=main, member=n/a): Created cache factory com.tangosol.net.ExtensibleConfigurableCacheFactory
2014-01-16 14:20:36.187/2.048 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=main, member=n/a): Connecting Socket to 130.35.99.90:9099
2014-01-16 14:20:36.190/2.051 Oracle Coherence GE 12.1.3.0.0 <Info> (thread=main, member=n/a): Connected Socket to 130.35.99.90:9099
    Success: Correctly allowed to use the invocation service
2014-01-16 14:20:37.094/2.955 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=main, member=n/a): Connecting Socket to 130.35.99.90:9099
2014-01-16 14:20:37.094/2.955 Oracle Coherence GE 12.1.3.0.0 <Info> (thread=main, member=n/a): Connected Socket to 130.35.99.90:9099
    Success: Correctly unable to use the invocation service
------InvocationService access control example completed------

例10-22は、キャッシュ・サーバーでプロキシ・サービスが実行される場合のシェルの出力をリストしています。出力のセキュリティの例外は、ロールreaderのユーザーがExampleInvocableを実行しようとした場合のEclipseコンソールのメッセージ「Success: Correctly unable to use the invocation service」に対応することに注意します。

例10-22 Eclipseコンソールのプロキシ・サービスの応答

Started DefaultCacheServer...
... 
2014-01-16 14:18:33.043/27.491 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=Invocation:Management, member=1): Member 2 left service Management with senior member 1
Password validated for user: role_writer
Password validated for user: role_writer
Password validated for user: role_reader
Password validated for user: role_reader
2014-01-16 14:20:37.094/151.542 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=Proxy:ProxyService:TcpAcceptorWorker:1, member=1): 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:105)
    at com.oracle.handson.SecurityExampleHelper.checkAccess(SecurityExampleHelper.java:59)
    at com.oracle.handson.EntitledInvocationService.query(EntitledInvocationService.java:50)
    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:356)
    at com.tangosol.coherence.component.net.extend.Channel.execute(Channel.CDB:48)
    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:65)
    at com.tangosol.coherence.component.util.Daemon.run(Daemon.CDB:51)
    at java.lang.Thread.run(Thread.java:724)