[z/OS]

System Authorization Facility のカスタム・マッピング・モジュール

カスタマイズされたログイン・マッピング・モジュールを作成することによって、 Java™ Authentication and Authorization Service (JAAS) ログイン構成をカスタマイズできます。

注: SAF 分散 ID マッピング機能を使用する場合、マッピング・モジュールを構成する必要はありません。

WebSphere® Application Server ltpaLoginModule モジュールおよび AuthenLoginModule モジュールは、 共有状態を使用して、LoginModule が状態情報を変更できるようにする機能により、状態情報を保存します。 ltpaLoginModule は次のコードを使用して、 login() メソッドでコールバック配列を初期化します。 コールバック配列は、配列が共有状態の領域で定義されない場合のみ、ltpaLoginModule によって作成されます。

次のコード例では、Java Platform, Enterprise Edition (Java EE) ID の可用性に依存して、System Authorization Facility (SAF) ID に対するマッピングを制御します。 このコードでは、共有状態において Constants.WSPRINCIPAL_KEY 値を使用しています。 この値は、WebSphere Application Server 1 ログイン・モジュールによりコード内に配置されます。 カスタムの LoginModule は、ltpaLoginModule モジュールの後、さらに MapPlatformSubject モジュールの直前に挿入できます。 これを行う場合は、コールバック配列または他の共有状態の値を使用して、z/OS® ユーザー ID へのマッピングの制御に使用する値を取得します。

以下は、SAFMappingModule のサンプルです。
//
// 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.security;

import com.ibm.websphere.security.auth.CredentialDestroyedException;
import com.ibm.websphere.security.auth.WSPrincipal;
import com.ibm.websphere.security.cred.WSCredential;
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 javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.CredentialExpiredException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

/**
 * 
 * SampleSAFMappingModule demonstrates a custom login module
 * that maps the existing WSPrincipal from the shared state to a z/OS
 * user id.
 * 
 *
 * 
 * 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 SampleSAFMappingModule implements LoginModule
{
    /*
     * 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.security.SampleSAFMappingModule";

    /*
     * 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 = 8;

    /*
     * 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;

    /*
     * 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 SampleSAFMappingModule()
    {
    }

    /**
     * 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 directly
                // from the WSPrincipal name if the property "useWSPrincipalName" is set to true
                if (debugEnabled)
                {
                    debug("Using name from WSPrincipal to obtain ZOS_USERID");
                }

                name = createName(principal);

                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
                audit = realm + "/" + name + " MappingModule:" + MAPPING_MODULE_NAME;

                // 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;
                succeeded = true;
            }
            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.
     */
    private String createName(WSPrincipal principal)
    {
        if (debugEnabled)
        {
            debug("createName() entry");
        }

        String name = principal.getName();

        if (debugEnabled)
        {
            debug("Using name='" + name + "' from principal");
        }

        // WSPrincipal.getName() might return REALM/NAME, so parse the String to obtain just the 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
        {
            // index is either 0 (if no / exists in the name), or it is the position
            // after the first / in the name
            //
            // either way, we will take the substring from that point until the end of the string
            name = name.substring(index);
        }

        // 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 name shortened to " + name);
            }
        }

        // MVS ids are all upper case
        name = name.toUpperCase();

        if (debugEnabled)
        {
            debug("createName() exit");
        }
        return name;
    }
}
サンプル・マッピング・モジュールは Lightweight Directory Access Protocol (LDAP) ID および z/OS ID の間のマッピングを作成します。LDAP ID は、z/OS ユーザー ID として 使用されます。 違うマッピングが必要とされる場合、 このモジュールをカスタマイズして (ELSE 節で示されるように)、マッピングを実行することができます。その後、以下を実行します。
  1. Java コードをコンパイルします。コードが信頼され、APF 許可モジュールと同様に注意して扱われていることを確認してください。 デフォルトの Java Authentication and Authorization Service (JAAS) システム・ログイン構成は、z/OS コントローラーからアクセスされます。
  2. IBM® 提供のデフォルト以外のマッピング・クラスを指定する場合は、Application Server およびデプロイメント・マネージャーのクラス・ディレクトリーにこのクラスを インストールする必要があります。WebSphere Application Server Network Deployment セル内のデプロイメント・マネージャー・ノードなど、セル内のそれぞれのノードごとに、Java アーカイブ (JAR) ファイルを WAS_HOME/classes ディレクトリーの中に配置します。

トピックのタイプを示すアイコン 概念トピック



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