[z/OS]

統合リポジトリー用のカスタム System Authorization Facility (SAF) マッピング・モジュールの構成

System Authorization Facility (SAF) マッピング・モジュールを構成すると、SAF 許可用の SAF ユーザー・レジストリー・アダプターを持つ 統合リポジトリーを使用できます。SAF マッピング・モジュールは、統合リポジトリーの ID を SAF ID にマップします。

始める前に

統合リポジトリー には、SAF、Lightweight Directory Access Protocol (LDAP)、ファイル・ベース・リポジトリーなどのユーザー・レジストリーのチェーンを含めることができるため、マッピング・モジュールでは、SAF 許可で使用する必要がある SAF ID が明確に区別されます。オペレーティング・システム・レジストリー、または統合リポジトリーを介した LDAP などのその他のレジストリー用に SAF 許可を使用可能にするには、SAF ID のマッピングが必要になります。この ID マッピングでは、SAF に必要な追加の クレデンシャル情報が設定されます。

カスタマイズされたログイン・マッピング・モジュールを作成することによって、 Java™ Authentication and Authorization Service (JAAS) ログイン構成をカスタマイズできます。WebSphere® Application Server の ltpaLoginModule モジュールおよび AuthenLoginModule モジュールは、 マッピング・モジュールで提供される共用状態変数を使用して、SAF 許可に適切な ID を設定します。

トラブルの回避 (Avoid trouble) トラブルの回避 (Avoid trouble):
  • このマッピング・モジュールを使用する必要がある場合は、SampleVMMSAFMappingModule をコンパイルする必要があります。
  • SAF 分散 ID マッピング機能を使用している場合は、マッピング・モジュールを構成する必要はありません。
gotcha

このタスクについて

次のコード例では、Java Platform, Enterprise Edition (Java EE) ID の可用性に依存して、SAF ID に対するマッピングを制御します。 このコードでは、共有状態において Constants.WSPRINCIPAL_KEY 値を使用しています。 この値は、WebSphere Application Server ログイン・モジュールによりコード内に配置されます。 カスタムのログイン・モジュールは、ltpaLoginModule モジュールの後、さらに MapPlatformSubject モジュールの直前に挿入できます。 カスタムのログイン・モジュールを追加する場合は、コールバック配列または他の共有状態の値を使用して、z/OS® ユーザー ID へのマッピングの制御に使用する値を取得します。
//This program may be used, run, copied, modified and
//distributed without royalty for the purpose of developing,
//using, marketing, or distributing.

package com.ibm.websphere.wim;

import com.ibm.websphere.security.auth.WSPrincipal;
import com.ibm.websphere.security.cred.WSCredential;
import com.ibm.websphere.wim.util.PrincipalUtil;
import com.ibm.websphere.wim.exception.WIMException;
import com.ibm.websphere.wim.ras.WIMLogger;
import com.ibm.wsspi.security.auth.callback.Constants;
import com.ibm.wsspi.security.token.AttributeNameConstants;

import java.lang.reflect.Array;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;

import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

/**
 *
 * SampleVMMSAFMappingModule demonstrates a custom login module
 * that maps the existing WSPrincipal from the shared state to a z/OS
 * user id for a Federated Repository.
 *
 *
 *
 * The following values will be set into the shared state if authentication
 * succeeds.  If authentication fails, this login module will still indicate
 * success, but no values are set into the shared state.
 *
 *   AttributeNameConstants.ZOS_USERID
 *   AttributeNameConstants.ZOS_AUDIT_STRING
 *   AttributeNameConstants.CALLER_PRINCIPAL_CLASS
 *
 * This login module does not use any callbacks, nor does it modify the Subject
 * in any way.
 *
 * @author IBM Corporation
 * @version 1.0
 * @since 1.0
 */
public class SampleVMMSAFMappingModule implements LoginModule
{
    public final static String CLASSNAME = SampleVMMSAFMappingModule.class.getName();
    private static final Logger trcLogger = WIMLogger.getTraceLogger(CLASSNAME);
    boolean logEnabled = trcLogger.isLoggable(Level.FINER);

    /*
     * Constant that represents the name of this mapping module.  Whenever this sample
     * code is used to create a class with a different name, this value should be changed.
     *
     * By default, this value is used as part of the sample audit token, and for debugging
     * purposes.
     */
    private final static String MAPPING_MODULE_NAME = "com.ibm.websphere.wim.SampleVMMSAFMappingModule";

    /*
     * Constant that represents the maximum length of the ZOS_USERID.  Current MVS naming
     * restrictions limit this to eight characters.
     *
     * When the option to useWSPrincipalName is chosen, the name from the WSPrincipal is
     * shortened to this many characters.
     */
    private final static int MAXIMUM_NAME_LENGTH = 7;

    /*
     * Specifies whether or not to use this module's default mapping behavior, which is
     * to use the WSPrincipal name to generate the ZOS_USERID.  This depends on the
     * value of the "useWSPrincipalName" option passed in from the LoginContext.
     */
    private boolean useWSPrincipalName = true;

    /*
     * Specifies whether debugging is enabled for this Login Module.  This depends on the
     * value of the "debug" option passed in from the LoginContext.
     */
    private boolean debugEnabled = false;

    /*
     * Stores the Subject passed from the LoginContext.
     */
    private Subject subject;

    /*
     * Stores the CallbackHandler passed from the LoginContext.
     */
    private CallbackHandler callbackHandler;

    /*
     * Stores the shared state Map passed from the LoginContext.
     */
    private Map sharedState;

    javax.security.auth.Subject runas_subject, caller_subject;
    /*
     * Stores the options Map passed from the LoginContext.
     */
    private Map options;

    /*
     * This value is used to store the success or failure of the login() method so
     * that commit() and abort() can act differently in the two cases if so desired.
     */
    private boolean succeeded = false;


    /**
     * Construct an uninitialized mapping module object.
     */
    public SampleVMMSAFMappingModule()
    {
    }

    /**
     * Initialize this login module.
     *
     * This is called by the LoginContext after this login module is
     * instantiated. The relevant information is passed from the LoginContext
     * to this login module. If the login module does not understand any of the data
     * stored in the sharedState and options parameters,
     * they can be ignored.
     *
     * @param subject
     *                The subject that this LoginContext is authenticating
     * @param callbackHandler
     *                A CallbackHandler for communicating with the end user 
     *                to gather login information (e.g., username and password).
     * @param sharedState
     *                The state shared with other configured login modules.
     * @param options
     *                The options specified in the login configuration for this particular login module.
     */
    public void initialize(Subject newSubject, CallbackHandler newCallbackHandler,
            Map newSharedState, Map newOptions)
    {
        // obtain the value for debug before anything else so that tracing can be used within
        // this method
        if (newOptions.containsKey("debug"))
        {
            String debugEnabledString = (String) newOptions.get("debug");
            if (debugEnabledString != null && debugEnabledString.toLowerCase().equals("true"))
            {
                debugEnabled = true;
            }
        }

        if (debugEnabled)
        {
            debug("initialize() entry");
        }

        // this login module is not going to use any of these objects except for the sharedState,
        // but for consistency with most login modules, we will save a reference to all of them
        this.subject = newSubject;
        this.callbackHandler = newCallbackHandler;
        this.sharedState = newSharedState;
        this.options = newOptions;

        if (options.containsKey("useWSPrincipalName"))
        {
            String useWSPrincipalNameString = (String) options.get("useWSPrincipalName");
            if (useWSPrincipalNameString != null && useWSPrincipalNameString.toLowerCase().equals("false"))
            {
                useWSPrincipalName = false;
            }
        }

        if (debugEnabled)
        {
            debug(new Object[] { "initialize() exit", subject, callbackHandler, sharedState, options });
        }
    }

    /**
     * Method to map the WSPrincipal to a ZOS_USERID
     *
     * This method derives a ZOS_USERID and stores it into the Shared State for use by a later
     * Login Module.
     *
     * @return true if the authentication succeeded, or false
     *         if this Login Module should be ignored
     * @exception LoginException
     *         if the authentication fails, which is impossible for this Login Module
     */
    public boolean login() throws LoginException
    {
        if (debugEnabled)
        {
            debug("login() entry");
        }

        if (sharedState.containsKey(AttributeNameConstants.ZOS_USERID))
        {
            // we don't want to override this value if, for whatever reason, another Login Module
            // has already placed it into the shared state, but we still consider this a success
            // because the exit criteria for this module has been met
            if (debugEnabled)
            {
                debug("ZOS_USERID already exists:  so no additional work is needed");
            }
            succeeded = true;
        }
        else if (!sharedState.containsKey(Constants.WSPRINCIPAL_KEY) ||
                !sharedState.containsKey(Constants.WSCREDENTIAL_KEY))
        {
            // if there isn't a Principal or Credential in the shared state, we can't do
            // anything so we'll return false to inform the LoginContext to ignore this module
            if (debugEnabled)
            {
                debug("Principal or Credential is unavailable:  skipping this Login Module");
            }
            succeeded = false;
        }
        else
        {
            if (debugEnabled)
            {
                debug("Principal and Credential are available:  continue with login");
            }

            String name = null;
            String audit = null;
            String principalClass = null;

            // extract the WSPrincipal and WSCredential from the shared state
            WSPrincipal principal = (WSPrincipal) sharedState.get(Constants.WSPRINCIPAL_KEY);
            WSCredential credential = (WSCredential) sharedState.get(Constants.WSCREDENTIAL_KEY);

            if (useWSPrincipalName)
            {
                // this sample mapping module provides a method to obtain the ZOS_USERID from the uniqueID obtained
                // from the WSPrincipal name if the property "useWSPrincipalName" is set to true
                if (debugEnabled)
                {
                    debug("Using name from WSPrincipal to obtain ZOS_USERID");
                }

                final String principalName=stripRealm(principal.getName());
                String realm = getRealm(credential);

                // for this example, a sample audit token will be created that combines the ZOS_USERID,
                // the realm, and the name of this mapping module
                //
                // a custom audit token can be created using any available data rather than using
                // this sample token

                // A Subject may contain more than one Principal.  This value specifies the
                // class of the Principal to be returned when the Subject is asked for the
                // Caller Principal.  If a custom Principal class has been placed into the
                // Subject, that class name can be specified here.
                //
                // Two predefined values for the Caller Principal Class are provided:
                //
                // - AttributeNameConstants.ZOS_CALLER_PRINCIPAL_CLASS
                //   the z/OS Principal class
                //
                // - AttributeNameConstants.DEFAULT_CALLER_PRINCIPAL_CLASS
                //   the default Principal class
                principalClass = AttributeNameConstants.DEFAULT_CALLER_PRINCIPAL_CLASS;

                //name=doMapUser(principalName);
                name=doCustomMapUser(principalName);

                succeeded = true;
                audit = realm + "/" + name + " MappingModule:" + MAPPING_MODULE_NAME;
                }

                else
                {
                    if (debugEnabled)
                    {
                        debug("Using Custom logic to obtain ZOS_USERID");
                    }
                    // if the behavior provided by this mapping module to obtain the ZOS_USERID from the
                    // WSPrincipal name is not enough, custom mapping logic can be provided here
                    //
                    // to use this custom mapping logic, the property "useWSPrincipalName" must be set
                    // to false

                    // name = ...custom logic
                    // audit = ...custom logic
                    // principalClass = ...custom logic

                    // by default, no custom mapping is provided, so the success of this code path
                    // is false; if custom mapping is provided, the following variable should be
                    // modified to represent the success or failure of the custom mapping
                    succeeded = false;
                }

                if (succeeded)
                {
                    // now that we have values for name, audit, and principalClass, we just need to set
                    // them into the shared state
                    sharedState.put(AttributeNameConstants.ZOS_USERID, name);
                    sharedState.put(AttributeNameConstants.ZOS_AUDIT_STRING, audit);
                    sharedState.put(AttributeNameConstants.CALLER_PRINCIPAL_CLASS, principalClass);
                    if (debugEnabled)
                    {
                        debug(new Object[] { "Values have been stored into the shared state", name, audit, principalClass });
                    }
                }
            }

            if (debugEnabled)
            {
                debug("login() exit");
            }
            		return succeeded;
        }

        /**
         * Method to commit the authentication result.
         *
         * This Login Module does not need to commit any data, so we will simply return.
         *
         * @return true if the original login succeeded, or false
         *         if the original login failed
         * @exception LoginException
         *         if the commit fails, which cannot happen in this Login Module
         */
        public boolean commit() throws LoginException
        {
            if (debugEnabled)
            {
                debug("commit() entry");
            }

            // the return value of commit() is the same as the success of the original login
            boolean returnVal = succeeded;

            cleanup();

            if (debugEnabled)
            {
                debug("commit() exit");
            }
            return returnVal;
        }

        /**
         * Method to abort the authentication process (Phase 2).
         *
         * No matter whether our original login succeeded or failed, this method cleans up
         * our state and returns.
         *
         * @return true if the original login succeeded, or false
         *         if the original login failed
         * @exception LoginException
         *         if the abort fails, which cannot happen in this Login Module
         */
        	public boolean abort() throws LoginException
        {
            if (debugEnabled)
            {
                debug("abort() entry");
            }

            // the return value of abort() is the same as the success of the original login
            boolean returnVal = succeeded;

            cleanup();

            if (debugEnabled)
            {
                debug("abort() exit");
            }
            return returnVal;
        }

        /**
         * Method which logs out a Subject.
         *
         * Since our commit method did not modify the Subject, we don't have anything to
         * logout or clean up and can just return true.
         *
         * @return true if the logout succeeded
         * @exception LoginException
         *         if the logout fails, which cannot happen in the Login Module
         */
        	public boolean logout() throws LoginException
        {
            if (debugEnabled)
            {
                debug("logout() entry");
            }

            // our local variables were cleanup up during the commit, so no further cleanup is needed

            if (debugEnabled)
            {
                debug("logout() exit");
            }

            // since there is nothing to logout, we always succeed
            return true;
        }

        /*
         * Cleans up our local variables; the only cleanup required for
         * this Login Module is to set our success variable back to false.
         */
        private void cleanup()
        {
            if (debugEnabled)
            {
                debug("cleanup() entry");
            }

            // there's nothing to cleanup, really, so just reset our success variable
            succeeded = false;

            if (debugEnabled)
            {
                debug("cleanup() exit");
            }
        }

        /*
         * Private method to print trace information.  This implementation uses System.out
         * to print trace information to standard output, but a custom tracing system can
         * be implemented here as well.
         */
        private void debug(Object o)
        {
            System.out.println("Debug: " + MAPPING_MODULE_NAME);
            if (o != null)
            {
                if (o.getClass().isArray())
                {
                    int length = Array.getLength(o);
                    for (int i = 0; i < length; i++)
                    {
                        System.out.println("¥t" + Array.get(o, i));
                    }
                }
                else
                {
                    System.out.println("¥t" + o);
                }
            }
        }

        /*
         * Private method to obtain the realm name from the Credential and return it.  This
         * keeps the exception handling involved with obtaining the realm name out of the main
         * login() logic.
         */
        private String getRealm(WSCredential credential)
        {
            if (debugEnabled)
            {
                debug("getRealm() entry");
            }

            String realm = null;

            try
            {
                realm = credential.getRealmName();

                if (debugEnabled)
                {
                    debug("Got realm='" + realm + "' from credential");
                }
            }
            catch (Exception e)
            {
                // getRealmName throws CredentialExpiredException and CredentialDestroyedException
                if (debugEnabled)
                {
                    //debug(new Object[] { "Caught exception in getRealm: ", e });
                }
                realm = "UNKNOWN_REALM";
            }

            if (debugEnabled)
            {
                debug("getRealm() exit");
            }
            return realm;
        }

        /*
         * Private method to generate the ZOS_USERID from the WSPrincipal name/uniqueName.
         */
        private String createSAFIdentityName(String name)
        {
            if (debugEnabled)
            {
                debug("createSAFIdentityName() entry "+ name);
            }

            name=stripRealm(name).toUpperCase();
            //Get the CN.if uniqueName of format "uid=test,o=com" strip to "test".
            if (name.indexOf("=") >= 0)
                name = name.substring((name.indexOf("=")+1),name.length());

            if (name.indexOf(",") > 0)
                name = name.substring(0,name.indexOf(","));

            //handle case where realm appended :<realmname>¥¥userid
            if(name.indexOf("¥¥")>0){
            name = name.substring((name.indexOf("¥¥")+1),name.length());
            }

            // shorten the name if its length exceeds the defined maximum
            if (name.length() > MAXIMUM_NAME_LENGTH)
            {
                name = name.substring(0, MAXIMUM_NAME_LENGTH);

                if (debugEnabled)
                {
                    debug("WSPrincipal/uniqueName shortened to " + name);
                }
            }

            if (debugEnabled)
            {
                debug("createSAFIdentityName() exit");
            }
            return name; //return a SAF Idenity Name
        }
        /*
         * Private Helper method to Strip realm information if any.
         */
        private String stripRealm(String name){
            int index = name.indexOf("/") + 1; // index of the first character after the first /
            if (index >= name.length())
            {
                // this block handles the case where the first / is the last character in the String,
                // it really shouldn't happen, but if it does we can just strip it off
                name = name.substring(0, index - 1);

                if (debugEnabled)
                {
                    debug("Stripping trailing / from WSPrincipal name");
                }
            }
            else
            {
                name = name.substring(index);
            }

            return name;

        }


        /**
         * Private helper method to deduce the z/OS ID to be mapped.
         * You can use this when you want to map RACF users & other repository users in specific ways.
         * This uses a Federated Repository util method, isRACFUser to identify if the user is from RACF registry.
         */
        private String  doCustomMapUser(String principalName){
            boolean isRACFUser=true;
            String  mapID=null;
            try{
            isRACFUser=PrincipalUtil.isRACFUser(principalName);
            }
            catch(WIMException we){
               debug("Exception happened while checking isRACFuser"+we);
               //Handle it as per need.
            }
            mapID=isRACFUser==true?createSAFIdentityName(principalName):doMapUser(principalName);

            return mapID;
        }

        /**
         * Private helper method to deduce the z/OS ID to be mapped.
         * You can use this directly if you want to map z/OS ID irrespective of RACF/Non-RACF users.
         *
         */
        private String  doMapUser(String principalName){
            //If loginUser belongs to other registry like LDAP make sure an MVS user exists to match the derived name.
            //Or just map to any existing RACF user as in the commented line below.
            // name="USER237";
            //if user exists in RACF then Map that user.
            String uniqueName=null;
            String mapID=null;
            try {
                javax.naming.InitialContext ctx = new javax.naming.InitialContext();
                		// Retrieves the local UserRegistry object.
                final com.ibm.websphere.security.UserRegistry reg = (com.ibm.websphere.security.UserRegistry) ctx.lookup("UserRegistry");
                // Retrieves the registry uniqueName based on the principalName
                uniqueName = reg.getUniqueUserId(principalName);
            } catch ( Exception e ) {
                debug("Exception thrown while getting uniqueID: "+e);
            }
            mapID=createSAFIdentityName(uniqueName);

            return mapID;
        }
    }
サンプル・マッピング・モジュールは、統合リポジトリーのユーザー・レジストリーの ID および z/OS ID の間のマッピングを作成します。ユーザー・レジストリーの ID は、z/OS ユーザー ID として 使用されます。別のマッピングが必要な場合は、前出のコーディング・サンプルの一部として ELSE 節に示されているこのモジュールをカスタマイズして、マッピングを実行します。
注: 統合リポジトリー・ユーティリティー・メソッド PrincipalUtil.isRACFUser を 使用して、RACF® レジストリーのユーザーかどうかが識別されます。このメソッドは、ユーザーが RACF ユーザーの場合に true を戻します。このメソッドは、現在のユーザー・レジストリーが統合リポジトリーで構成されている場合にのみ使用します。

手順

  1. Java コードをコンパイルします。
    1. コードが信頼され、Authorized Program Facility (APF) 許可モジュールと同様に注意して扱われていることを確認してください。 デフォルトの Java Authentication and Authorization Service (JAAS) システム・ログイン構成は、z/OS コントローラーからアクセスされます。
    2. クラスパス設定に適切な Java アーカイブ (JAR) ファイルが含まれていることを確認します。 以下のいずれかの JAR ファイルのセットを使用できます。
      • WebSphere_Application_Client/plugins/org.eclipse.emf.commonj.sdo.jarWebSphere_Application_Client/plugins/com.ibm.ws.emf.jar、 および WebSphere_Application_Client/plugins/com.ibm.ws.runtime.client.jar
      • WAS_HOME/plugins/org.eclipse.emf.commonj.sdo.jarWAS_HOME/plugins/com.ibm.ws.runtime.wim.base.jar、および WAS_HOME/plugins/com.ibm.ws.runtime.jar
  2. IBM® 提供のデフォルト以外のマッピング・クラスを使用する場合は、WebSphere Application Server およびデプロイメント・マネージャーの classes ディレクトリーにクラスをインストールします。
  3. WebSphere Application Server Network Deployment セル内のデプロイメント・マネージャー・ノードなど、セル内のノードごとに、JAR ファイルを WAS_HOME/classes ディレクトリーに配置します。
  4. 変更を有効にするには、WebSphere Application Server を再始動してください。
  5. 以下のように、このモジュールにカスタム・プロパティーを指定して、SampleSAFMappingModule のロギングを使用可能にします。
    1. WebSphere Application Server 管理コンソールで、「セキュリティー」>「グローバル・セキュリティー」とクリックします。
    2. 「認証」の下の「Java 認証・承認サービス」を展開します。
    3. システム・ログイン」をクリックします。
    4. login_module_name」をクリックします。
    5. 「JAAS ログイン・モジュール」で「com.ibm.websphere.wim.SampleVMMSAFMappingModule」をクリックします。
    6. 「カスタム・プロパティー」で「新規」をクリックします。
    7. 「名前」フィールドに debug と入力し、「値」フィールドに true と入力します。
    8. 「適用」をクリックしてから、「保存」をクリックします。

トピックのタイプを示すアイコン タスク・トピック



タイム・スタンプ・アイコン 最終更新: last_date
http://www14.software.ibm.com/webapp/wsbroker/redirect?version=cord&product=was-nd-mp&topic=tsec_customsaffedrepos
ファイル名:tsec_customsaffedrepos.html