[z/OS]

LDAP から System Authorization Facility (SAF) マッピング・モジュールへの 1 対 1 のカスタム・マッピング

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

WebSphere® Application Server for z/OS® には、LDAP クレデンシャルを持つリモート・メソッド呼び出し (RMI) インバウンド要求から System Authorization Facility (SAF) ID にマッピングを提供する機能があります。 ユース・ケースは、LDAP に構成されるすべてのプラットフォームでの WebSphere Application Server であり、RMI/IIOP 要求を SAF ユーザー・レジストリーに構成されている 2 番目のサーバーに送信します。WebSphere Application Server (リリース 5.1 以降) は、LDAP ID を表す LTPA トークンを使用して RMI 要求を、SAF に構成されている WebSphere Application Server for z/OS に送信します。以下の図は、このマッピングを示しています。

図 1. LDAP クレデンシャルを持つ RMI インバウンド要求から SAF ID へのマッピングLDAP に構成されており、SAF ユーザー・レジストリーに構成されている 2 番目のサーバーに RMI/IIOP 要求を送信する、任意のプラットフォーム上の WebSphere Application Server。WebSphere Application Server (リリース 5.1 以上) は、LDAP ID を表す LTPA トークンを使用して、SAF に構成されている WebSphere Application Server for z/OS に RMI 要求を送信します。

以下のサンプルは、LTPA トークンをオープンする JAAS RMI インバウンド・ログイン・モジュールです。 このサンプルでは、LDAP ユーザー ID を抽出し、cn=userID をストリップし、WSCredential を SAF ユーザー ID に設定します。2 番目のサーバーに構成されるサンプルのインバウンド・ログイン・モジュールは、WebSphere Application Server for z/OS リリース 6.1 以降でのみサポートされます。

このサンプルは、実際は、com.ibm.wsspi.security.token.AttributeNameConstants.WSCREDENTIAL_UNIQUEID が、必要な z/OS SAF ID に設定される、ハッシュ・ログイン・モジュールです。 ltpaLoginModule は、渡された com.ibm.wsspi.security.token.AttributeNameConstants.WSCREDENTIAL_UNIQUEID をチェックし、指定された ID を使用して、ユーザーに適切なセキュリティー・コンテキストをビルドします。

このサンプル・モジュールは RMI_INBOUND JAAS ログイン・モジュールとして構成されます。 また、次に ltpaLoginModule がくるリストの先頭で、このモジュールを指定する必要があります。

次のサンプルでは、SAF ID へのマッピングを制御するために、Java Platform, Enterprise Edition (Java EE) ID を使用できます。 サンプルでは、Constants.WSCREDENTIAL 値のハッシュ・ログインを共有状態で使用します。

次のコードは、SampleLTPASAFMappingModule プログラムのサンプルです。 このサンプル・プログラムを「そのまま」使用するかまたは変更して、コンパイルし、 WAS_HOME/classes ディレクトリーにインストールできます。
// This program may be used, executed, 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;
import com.ibm.wsspi.security.token.WSSecurityPropagationHelper;
import com.ibm.websphere.security.auth.callback.WSCredTokenCallbackImpl;
import com.ibm.websphere.security.auth.WSLoginFailedException;

/**
 * **
 * SampleLTPASAFMappingModule demonstrates a custom login module
 * that maps the existing LTPA Token from the shared state to a UNIQUE ID to avoid the Login.  * Typical use is a inbound LTPA Token generated from a different realm (perhaps a non zOS Realm)
 * into a SAF User Registry.
 * The LTPA Token is used as the trusted identity token.
 *
 * ***
 *
 * @author IBM Corporation
 * @version 1.0
 * @since 1.0
 */
public class SampleLTPASAFMappingModule 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.SampleLTPASAFMappingModule";

    private final static int MAXIMUM_NAME_LENGTH = 8;

    /*
     * **
     * 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;
 /*
     * **
     * Specifies the inbound realm that this Login Module is to handle.  If there is no incoming 
     * property setting for onlyThisRealm, then this Login module will process all inbound
     * request regardless of the incoming idenity realm within the LTPA token. 
     * ***
     */
    private String onlyThisRealm = null;

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

    /**
     * **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");
        }
		// obtain the value for realm before anything else to see if this login module should 
		// only process a particular inbound realm or if it should process all realm. A null value 
		// singles all realms will be processed. 
		if (newOptions.containsKey("realm"))
        {
            onlyThisRealm = (String) newOptions.get("realm");
	    	    onlyThisRealm = onlyThisRealm.trim();
	}


        // 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 (debugEnabled)
        {
            debug(new Object[] { "initialize() exit", subject, callbackHandler, sharedState, options });
        }
    }

    /**
     *
     * @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");
        }

	// Handle the callback 
		javax.security.auth.callback.Callback callbacks[] = 
            new javax.security.auth.callback.Callback[1];
		callbacks[0] = new com.ibm.websphere.security.auth.callback.WSCredTokenCallbackImpl("");

	try
	{
		                callbackHandler.handle(callbacks);
	} 
	catch (Exception e)
	{
	     if (debugEnabled)
             {
                 debug(new Object[] { "Caught exception in callbackhandler: ", e });
             }
             return false;
	} 
	
		byte[] credToken = ((WSCredTokenCallbackImpl) callbacks[0]).getCredToken();
        String uid = null;
	String realm = null;
		String uniqueID = null;

		//This routine will only process a LTPA token. If there is no inbound token, 
		//then this routine will return true and allow the other LM should handle
	//this request
	if (credToken != null)
	{
		try
		{
						uniqueID = WSSecurityPropagationHelper.validateLTPAToken(credToken);
			realm = WSSecurityPropagationHelper.getRealmFromUniqueID (uniqueID);
                        uid = createSAFIdenityName (uniqueID);
			if (debugEnabled)
                        {
                            debug("using uniqueID: "+ uniqueID+ " inbound realm: "+ realm +
				  "uid: " + uid );
                        }
		}
		catch (Exception e)
		{
		   if (debugEnabled)
                   {
                       debug(new Object[] { "Caught exception in callbackhandler: ", e });
                   }
                   return false;
		}	
	}
		else return true; // let the other LM handle this request. 
	

		//onlyThisRealm is a input property setting to see if we want this login module
		//to handle only 1 inputed realm request. If onlyThisRealm is null, 
		//then we will process all request and continue on. If the onlyThisRealm does
		//not match the inbound request extracted from LTPA, then we will let the other
		//LM handle this request. 
	//
		if ((onlyThisRealm != null) && (!realm.trim().equals(onlyThisRealm))) {
            if (debugEnabled)
            {
                debug("inbound realm of "+realm+" does not match option realm of "+onlyThisRealm);
            }
            return true;
	}

	try {
	    		// Retrieves the default InitialContext for this server.
	    javax.naming.InitialContext ctx = new javax.naming.InitialContext();

	    		// Retrieves the local UserRegistry object.
	    		com.ibm.websphere.security.UserRegistry reg =
		    (com.ibm.websphere.security.UserRegistry) ctx.lookup("UserRegistry");				

	
  	   		// Retrieves the registry uniqueID based on the uid that is specified 
           // in the NameCallback.
	   		String uniqueid = reg.getUniqueUserId(uid);
	   if (debugEnabled)
            {
                debug("uniqueid "+uniqueid);
            }
	   		// Retrieves the display name from the user registry based on the uniqueID.
	   		String securityName = reg.getUserSecurityName(uid);
	   if (debugEnabled)
            {
                debug("securityName "+securityName);
            }
	
	   		// Retrieves the groups associated with this uniqueID.
	   		java.util.List groupList = reg.getUniqueGroupIds(uid);

   	   		// Creates the java.util.Hashtable with the information that you gathered 
           // from the UserRegistry.
	   	   // By setting this hashtable, the LTPA login module will propertly
	   	   // setup the SAF Identity. 
   	   		java.util.Hashtable hashtable = new java.util.Hashtable();
           			hashtable.put(com.ibm.wsspi.security.token.AttributeNameConstants.
              WSCREDENTIAL_UNIQUEID, uniqueid);
           			hashtable.put(com.ibm.wsspi.security.token.AttributeNameConstants.
              WSCREDENTIAL_SECURITYNAME, securityName);
	   			hashtable.put(com.ibm.wsspi.security.token.AttributeNameConstants.
              WSCREDENTIAL_GROUPS, groupList);

	   		// Adds the hashtable to the shared state of the Subject.
	   	   sharedState.put(com.ibm.wsspi.security.token.AttributeNameConstants.
              WSCREDENTIAL_PROPERTIES_KEY,hashtable);
        }
	catch (Exception e)
	{
	   if (debugEnabled)
           {
               debug(new Object[] { "Caught exception in callbackhandler: ", e });
           }
	   	   WSLoginFailedException e2 = new WSLoginFailedException("SampleLTPASAFMappingModule detected an error. "+e);
           throw e2;
        }		
        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);
            }
        }
    }
    /*
     * **
     * TODO
     * Private method to generate the SAF Idenity from the LDAP identity. This routine may
     * need to be modify by your installation standards to extract the SAF Idenity from
     * the LDAP IDentity. OR this routine could be written to map the LDAP to SAF identity.
     * ***
     */
    private String createSAFIdenityName(String name)
    {
        if (debugEnabled)
        {
            debug("createSAFIdenityName() entry");
        }

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

        name = name.toUpperCase();
        int index = name.indexOf("/") + 1; // index of the first character after the first /
        System.out.println(index);
        System.out.println(name);
        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 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);
        }
        System.out.println(name);
        
        if (name.indexOf("CN=") >= 0) 
        		name = name.substring((name.indexOf("CN=")+3),name.length());
        
        
        if (name.indexOf(",") > 0)
            name = name.substring(0,name.indexOf(","));
        
        // 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);
            }
        }

        if (debugEnabled)
        {
            debug("createSAFIdenityName() exit");
        }
        return name; //return a SAF Idenity Name
    }
}

サンプルのマッピング・モジュールは、LDAP クレデンシャルを持つ RMI インバウンド要求から SAF ID へのマッピングを作成します。マッピングの実行のためにこのモジュールをカスタマイズすることができます。


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



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