[z/OS]

Angepasste Module für die 1:1-Zuordnung von LDAP und SAF

Sie können JAAS-Anmeldekonfigurationen (Java™ Authentication and Authorization) anpassen, indem Sie ein angepasstes Anmeldezuordnungsmodul schreiben.

WebSphere Application Server for z/OS ist in der Lage, eine eingehende RMI-Anforderung (Remote Method Invocation) mit LDAP-Berechtigungsnachweisen einer SAF-Identität (System Authorization Facility) zuzuordnen. Der Anwendungsfall ist ein WebSphere Application Server auf einer beliebigen Plattform, in dem LDAP konfiguriert ist und der eine RMI/IIOP-Anforderung an einen zweiten Server sendet, in dem die Verwendung einer SAF-Benutzerregistry konfiguriert ist. WebSphere Application Server (ab Release 5.1) sendet eine RMI-Anforderung mit einem LTPA-Token, das die LDAP-Identität darstellt, an den WebSphere Application Server for z/OS, in dem SAF konfiguriert ist. Die folgende Abbildung veranschaulicht diese Zuordnung.

Abbildung 1. Zuordnung einer eingehenden RMI-Anforderung mit LDAP-Berechtigungsnachweisen zu einer SAF-IDWebSphere Application Server (Release 5.1 und höher) sendet eine RMI-Anforderung mit einem LTPA-Token, das die LDAP-ID darstellt, an den für SAF konfigurierten WebSphere Application Server for z/OS.

Das folgende Beispiel zeigt ein JAAS-RMI-Anmeldemodul für eingehende Anforderungen, das das LTPA-Token öffnet, die LDAP-Benutzer-ID extrahiert, "cn=userID" kürzt und WSCredential auf die SAF-Benutzer-ID setzt. Das für den zweiten Server konfigurierte Beispielanmeldemodul für eingehende Anforderungen wird nur in WebSphere Application Server for z/OS ab Release 6.1 unterstützt.

Das Beispiel ist eigentlich ein Hash-Anmeldmodul, in dem com.ibm.wsspi.security.token.AttributeNameConstants.WSCREDENTIAL_UNIQUEID auf die gewünschte z/OS-SAF-Identität gesetzt ist. Das ltpaLoginModule sucht nach einer com.ibm.wsspi.security.token.AttributeNameConstants.WSCREDENTIAL_UNIQUEID, die übergeben wurde, und verwendet die angegebene ID, um den entsprechenden Sicherheitskontext für den Benutzer zu erstellen.

Dieses Beispielmodul ist als JAAS-Anmeldemodul vom Typ "RMI_INBOUND" konfiguriert und muss ganz oben in der Liste, gefolgt von ltpaLoginModule, angegeben werden.

Im folgenden Beispiel ist eine Java EE-ID (Java Platform, Enterprise Edition) verfügbar, um die Zuordnung zu einer SAF-Identität zu steuern. In dem Beispiel wird die Hashanmeldung des Constants.WSCREDENTIAL-Wertes im gemeinsam genutzten Status ("shared") verwendet.

Im Folgenden sehen Sie ein Beispiel für das Programm SampleLTPASAFMappingModule. Sie können dieses Beispielprogramm unverändert ("AS IS") verwenden oder das Programm ändern, kompilieren und im Verzeichnis WAS_HOME/classes installieren.
// Dieses Programm darf für Entwicklungs-, Nutzungs-, Marketing- und
// Vertriebszwecke gebührenfrei genutzt, ausgeführt, kopiert, geändert
// und vertrieben werden.
//
//

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 zeigt ein angepasstes Anmeldemodul, dass das vorhandene
 * LTPA-Token aus dem gemeinsam genutzten Status einer eindeutigen ID zuordnet, um die
 * Anmeldung zu vermeiden.
 Gewöhnlich wird ein eingehendes LTPA-Token aus einem anderen
 * Realm (unter Umständen kein z/OS-Realm) in einer SAF-Benutzerregistry generiert.
 * Das LTPA-Token wird als anerkanntes Identitätstoken verwendet. 
 *
 * ***
 *
 * @author IBM Corporation
 * @version 1.0
 * @since 1.0
 */
public class SampleLTPASAFMappingModule implements LoginModule
{
    /*
     * **
     * Konstante, die den Namen dieses Zuordnungsmoduls repräsentiert.  Wenn mit diesem
     * Beispielcode eine Klasse anderen Namens erstellt wird, muss dieser Wert geändert werden.
     *
     * Dieser Wert wird standardmäßig als Teil des Beispielprüftoken und für das Debugging
     * verwendet.
     * ***
     */
    private final static String MAPPING_MODULE_NAME = "com.ibm.websphere.security.SampleLTPASAFMappingModule";

    private final static int MAXIMUM_NAME_LENGTH = 8;

    /*
     * **
     * Gibt an, ob für dieses Anmeldemodul das Debugging aktiviert ist.  Dies
     * hängt vom Wert Option "debug" ab, den der LoginContext übergeben hat.
     * ***
     */
    private boolean debugEnabled = false;
 /*
     * **
     * Gibt den eingehenden Realm an, den dieses Anmeldmodul bearbeiten soll.  Wenn keine
     * Eigenschaftseinstellung für eingehende Anforderungen für onlyThisRealm vorhanden ist, verarbeitet dieses
     * Anmeldmodul alle eingehenden Anforderungen, unabhängig vom eingehenden Identitätsrealm im LTPA-Token. 
     * ***
     */
    private String onlyThisRealm = null;

    /*
     * **
     * Speichert das vom LoginContext übergebene Subject.
     * ***
     */
    private Subject subject;

    /*
     * **
     * Speichert den vom LoginContext übergebenen CallbackHandler.
     * ***
     */
    private CallbackHandler callbackHandler;

    /*
     * **
     * Speichert die vom LoginContext übergebene Map für gemeinsamen Status.
     * ***
     */
    private Map sharedState;

    /*
     * **
     * Speichert die vom LoginContext übergebene Optionszuordnung.
     * ***
     */
    private Map options;

    /*
     * **
     * Mit diesem Wert wird der Erfolg oder das Scheitern der Methode login() gespeichert,
     * damit commit() und abort() bei Bedarf entsprechend ausgeführt werden können.
     * ***
     */
    private boolean succeeded = false;

    /**
     * **Nicht initialisiertes Zuordnungsmodulobjekt erstellen.***
     */
    public SampleLTPASAFMappingModule()
    {
    }

    /**
     * **Dieses Anmeldemodul initialisieren.***
     *
     * **
     * Dieser Aufruf wird vom LoginContext nach der Instanzierung dieses
     * Anmeldemoduls ausgeführt. Die relevanten Informationen werden vom
     * LoginContext an dieses Anmeldemodul übergeben. Falls das Anmeldemodul
     * die in den Parametern sharedState und options gespeicherten Daten
     * nicht versteht, können diese ignoriert werden.
     * ***
     *
     * @param subject
     *                das von diesem LoginContext authentifizierte Subject
     * @param callbackHandler
     *                Ein CallbackHandler für die Kommunikation mit dem
     *                Endbenutzer zum Abrufen der Anmeldeinformationen
     *                (Benutzername und Kennwort)
     * @param sharedState
     *                Der mit anderen konfigurierten Anmeldemodulen gemeinsam genutzte Status.
     * @param options
     *                Die in der Anmeldekonfiguration für dieses spezielle Anmeldemodul angegebenen Optionen
     */
    public void initialize(Subject newSubject, CallbackHandler newCallbackHandler, Map newSharedState, Map newOptions)
    {
        // Abrufen des Wertes für das Debug vor Ausführung aller weiteren Schritte, damit in dieser
        // Methode der Trace verwendet werden kann
        if (newOptions.containsKey("debug"))
        {
            String debugEnabledString = (String) newOptions.get("debug");
            if (debugEnabledString != null && debugEnabledString.toLowerCase().equals("true"))
            {
                debugEnabled = true;
            }
        }
	if (debugEnabled)
        {
            debug("initialize() entry");
        }
		// Wert für den Realm zuerst abrufen, um festzustellen, ob dieses Anmeldemodul nur einen
	// bestimmten eingehenden Realm oder alle Realms verarbeiten soll. Ein Nullwert gibt an,
	// dass alle Realms verarbeitet werden. 
		if (newOptions.containsKey("realm"))
        {
            onlyThisRealm = (String) newOptions.get("realm");
	    	    onlyThisRealm = onlyThisRealm.trim();
	}


        // Dieses Anmeldemodul verwendet keines dieser Objekte mit Ausnahme von sharedState. Aus Gründen
        // der Konsistenz mit den meisten Anmeldemodulen wird eine Referenz auf alle Objekte gespeichert.
        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, wenn die Authentifizierung erfolgreich ist, oder false,
     *         wenn das Anmeldemodul ignoriert werden soll.
     * @exception LoginException
     *         wenn die Authentifizierung scheitert, was bei diesem Anmeldemodul nicht möglich ist
     */
    public boolean login() throws LoginException
    {
        if (debugEnabled)
        {
            debug("login() entry");
        }

		// Callback bearbeiten 
		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;

		// Diese Routine verarbeitet nur ein LTPA-Token. Wenn kein eingehendes Token
	// vorhanden ist, gibt diese Routine "true" zurück und lässt die Bearbeitung
	// dieser Anforderung durch das andere Anmeldemodul zu.
		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; // Die Anforderung vom anderen Anmeldemodul bearbeiten lassen. 
	

		// onlyThisRealm ist eine Eigenschaftseinstellung für eingehende Anforderungen, mit der festgestellt
	// werden kann, ob dieses Anmeldemodul nur eine eingegebene Realmanforderung bearbeiten soll.
 // Wenn onlyThisRealm null ist, werden alle Anforderungen verarbeitet. Wenn
 // onlyThisRealm nicht mit der eingehenden Anforderung übereinstimmt, die aus LTPA extrahiert
	// wird, wird die Verarbeitung dieser Anforderung durch das andere Anmeldemodul zugelassen. 
	//
		if ((onlyThisRealm != null) && (!realm.trim().equals(onlyThisRealm))) {
            if (debugEnabled)
            {
                debug("inbound realm of "+realm+" does not match option realm of "+onlyThisRealm);
            }
            return true;
	}

	try  { 
	    	    // Standard-InitialContext für den Server abrufen
	    javax.naming.InitialContext ctx = new javax.naming.InitialContext();

	    		// Lokales UserRegistry-Objekt abrufen
	    		com.ibm.websphere.security.UserRegistry reg = 
		    (com.ibm.websphere.security.UserRegistry) ctx.lookup("UserRegistry");				

	
  	   	   // Ruft die uniqueID der Registry ab, die auf dem im 
           // NameCallback angegebenen Benutzer-ID basiert.
	   	   String uniqueid = reg.getUniqueUserId(uid);
	   if (debugEnabled)
            {
                debug("uniqueid "+uniqueid);
            }
	   // Ruft den auf der uniqueID basierenden Anzeigenamen von der Benutzerregistry ab.
	   		String securityName = reg.getUserSecurityName(uid);
	   if (debugEnabled)
            {
                debug("securityName "+securityName);
            }
	
	   	   // Gruppen für diese eindeutige ID abrufen
	   	   java.util.List groupList = reg.getUniqueGroupIds(uid);

   	   	   // Erstellt die java.util.Hashtable mit den Informationen, die Sie 
           // in der UserRegistry-Implementierung erfasst haben.
	   	   // Durch die Definition dieser Hashtabelle kann das LTPA-Anmeldemodul die
	  // SAF-Identität ordnungsgemäß setzen. 
   	   	   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);

	   	   // Hashtabelle dem gemeinsam genutzten Status des Subject hinzufügen
	   	   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;
    }

    /**
     * **Methode zum Festschreiben des Authentifizierungsergebnisses.***
     *
     * **
     * Dieses Anmeldemodul muss keine Daten festschreiben, sodass  es zurückkehren kann.
     * ***
     *
     * @return true, wenn die ursprüngliche Anmeldung erfolgreich ist, oder false
     *         beim Scheitern der ursprünglichen Anmeldung
     * @exception LoginException
     *         wenn das Festschreiben scheitert, was bei diesem Anmeldemodul nicht möglich ist
     */
    public boolean commit() throws LoginException
    {
        if (debugEnabled)
        {
            debug("commit() entry");
        }

        // Der Rückgabewert von commit() entspricht einer erfolgreichen ursprünglichen Anmeldung.
        boolean returnVal = succeeded;

        cleanup();

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

    /**
     * **Methode zum Abbrechen des Authentifizierungsprozesses (Phase 2).***
     *
     * **
     * Diese Methode bereinigt unabhängig vom Erfolg oder Scheitern der ursprünglichen
     * Anmeldung den Status und kehrt zurück.
     * ***
     *
     * @return true, wenn die ursprüngliche Anmeldung erfolgreich ist, oder false
     *         beim Scheitern der ursprünglichen Anmeldung
     * @exception LoginException
     *         wenn der Abbruch scheitert, was bei diesem Anmeldemodul nicht möglich ist
     */
    public boolean abort() throws LoginException
    {
        if (debugEnabled)
        {
            debug("abort() entry");
        }

        // Der Rückgabewert von abort() entspricht einer erfolgreichen ursprünglichen Anmeldung.
        boolean returnVal = succeeded;

        cleanup();

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

    /**
     * **Methode zum Abmelden von einem Subject.***
     *
     * **
     * Da das Subject von der Methode commit nicht geändert wurde, ist keine Abmeldung
     * oder Bereinigung erforderlich. Das Modul kann zurückkehren.
     * ***
     *
     * @return true bei erfolgreicher Abmeldung
     * @exception LoginException
     *         wenn die Abmeldung scheitert, was bei diesem Anmeldemodul nicht möglich ist
     */
    public boolean logout() throws LoginException
    {
        if (debugEnabled)
        {
            debug("logout() entry");
        }

        // Die lokalen Variablen wurden beim Festschreiben bereinigt. Es ist keine weitere Bereinigung erforderlich.

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

        // Da keine Abmeldung erforderlich ist, ist nur ein Erfolg möglich.
        return true;
    }

    /*
     * **
     * Bereinigung der lokalen Variablen. Für dieses Anmeldemodul muss nur
     * die Variable für Erfolg zurück auf false gesetzt werden.
     * ***
     */
    private void cleanup()
    {
        if (debugEnabled)
        {
            debug("cleanup() entry");
        }

        // Es ist tatsächlich keine Bereinigung notwendig, daher wird einfach die Variable für Erfolg zurückgesetzt.
        succeeded = false;

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

    /*
     * **
     * Private Methode zum Ausgeben von Traceinformationen.  Diese Implementierung verwendet
     * System.out für die Ausgabe der Traceinformationen an die Standardausgabe. Sie können
     * hier auch ein eigenes Tracesystem implementieren.
     * ***
     */
    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);
            }
        }
    }
    /*
     * **
     * Unerledigte Aufgabe
     * Private Methode zum Generieren der SAF-Identität aus der LDAP-Identität. Diese Routine muss
     * möglicherweise von den Installationsstandards geändert werden, um die SAF-Idenität aus
     * der LDAP-Identität zu extrahieren. Die Routine kann auch so geschrieben werden, dass die
     * LDAP-Identität der SAF-Identität zugeordnet wird.
     * ***
     */
    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 des ersten Zeichens nach dem ersten /
        System.out.println(index);
        System.out.println(name);
        if (index >= name.length())
        {
            // Diesen Block gibt es für den Fall, dass der erste / das letzte Zeichen der String ist.
            // In diesem höchst unwahrscheinlichen Fall wird das Zeichen einfach gelöscht.
            name = name.substring(0, index - 1);

            if (debugEnabled)
            {
                debug("Stripping trailing / from name");
            }
        }
        else
        {
            // Der Index ist 0 (wenn der Name keinen / enthält) oder gibt die
            // Position des ersten / im Namen an.
            //
            // Hier wird in jedem Fall die Unterzeichenfolge von diesem Punkt bis zum Ende der Zeichenfolge verwendet.
            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(","));
        
        // Kürzen des Namens, falls er die definierte Maximallänge überschreitet
        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; //Namen der SAF-Identität zurückgeben
    }
}

Das Beispielzuordnungsmodul erstellt eine Zuordnung aus einer eingehenden RMI-Anforderung mit LDAP-Berechtigungsnachweisen zu einer SAF-Identität. Dieses Modul kann so angepasst werden, dass es die Zuordnung durchführt.


Symbol, das den Typ des Artikels anzeigt. Konzeptartikel



Symbol für Zeitmarke Letzte Aktualisierung: 25.05.2016
http://www14.software.ibm.com/webapp/wsbroker/redirect?version=cord&product=was-nd-mp&topic=csec_LDAP_SAF_loginmods
Dateiname:csec_LDAP_SAF_loginmods.html