![[z/OS]](../images/ngzos.gif)
Personalización de módulos de correlación LDAP uno a uno a SAF (System Authorization Facility)
Puede personalizar las configuraciones de inicio de sesión JAAS (Java™ Authentication and Authorization) escribiendo un módulo de correlación de inicio de sesión personalizado.
WebSphere Application Server para z/OS tiene la capacidad de proporcionar una correlación de una solicitud entrante de invocación a método remoto (RMI) con credenciales LDAP con la identidad de SAF (System Authorization Facility). El caso utilizado es un WebSphere Application Server en cualquier plataforma que esté configurado para LDAP y envíe una petición de RMI/IIOP a un segundo servidor configurado para un Registro de usuarios de SAF. WebSphere Application Server (release 5.1 y posteriores) envía una solicitud de RMI mediante una señal LTPA que representa la identidad de LDAP en WebSphere Application Server para z/OS que se ha configurado para SAF. La figura siguiente ilustra esta correlación.

El ejemplo siguiente es un módulo de inicio de sesión entrante de JAAS RMI que abre la señal LTPA, extrae el ID de usuario de LDAP, le quita cn=userID y establece WSCredential para el ID de usuario de SAF. El módulo de inicio de sesión entrante de ejemplo que se configura en el segundo servidor sólo está soportado en WebSphere Application Server para z/OS release 6.1 o posteriores.
El ejemplo es, en realidad, un módulo de inicio de sesión hash en el que com.ibm.wsspi.security.token.AttributeNameConstants.WSCREDENTIAL_UNIQUEID se establece en la Identidad de SAF de z/OS deseada. ltpaLoginModule comprueba si hay un com.ibm.wsspi.security.token.AttributeNameConstants.WSCREDENTIAL_UNIQUEID que se haya pasado y utiliza el ID especificado para construir el contexto de seguridad adecuado para el usuario.
Este módulo de ejemplo se configura como un módulo de inicio de sesión JAAS RMI_INBOUND y debe especificarse al principio de la lista, seguido de ltpaLoginModule.
En el ejemplo siguiente, una identidad Java Platform, Enterprise Edition (Java EE) está disponible para controlar la correlación con una identidad de SAF. El ejemplo utiliza el inicio de sesión hash del valor Constants.WSCREDENTIAL en el estado compartido.
// Este programa se puede utilizar, ejecutar, copiar, modificar y
// distribuir sin derechos de copia para fines de desarrollo,
// uso, comercialización o distribución.
//
//
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 demuestra un módulo de inicio de sesión personalizado
* que correlaciona la señal LTPA existente desde el estado compartido con un ID EXCLUSIVO para el inicio de sesión.
* El uso típico es una señal LTPA generada en un reino diferente (quizás un reino no zOS)
* en un registro de usuarios de SAF.
* La señal LTPA se utiliza como señal de identidad fiable.
*
* ***
*
* @author IBM Corporation
* @version 1.0
* @since 1.0
*/
public class SampleLTPASAFMappingModule implements LoginModule
{
/*
* **
* Constante que representa el nombre de este módulo de correlación. Si se utiliza
* este código de ejemplo para crear una clase con otro nombre, este valor debe cambiarse.
*
* De forma predeterminada, este valor se utiliza como parte del símbolo de auditoría de ejemplo y para
* depuraciones.
* ***
*/
private final static String MAPPING_MODULE_NAME = "com.ibm.websphere.security.SampleLTPASAFMappingModule";
private final static int MAXIMUM_NAME_LENGTH = 8;
/*
* **
* Especifica si la depuración está habilitada para este módulo de inicio de sesión. Esto depende del
* valor de la opción "debug" pasada desde LoginContext.
* ***
*/
private boolean debugEnabled = false;
/*
* **
* Especifica el ámbito entrante que debe manejar este módulo de inicio de sesión. Si no hay ningún valor de propiedad entrante
* para onlyThisRealm, este módulo de inicio de sesión procesará todas las solicitudes entrantes
* independientemente del ámbito de identidad entrante en la señal de LTPA.
* ***
*/
private String onlyThisRealm = null;
/*
* **
* Almacena el sujeto pasado desde LoginContext.
* ***
*/
private Subject subject;
/*
* **
* Almacena CallbackHandler pasado desde LoginContext.
* ***
*/
private CallbackHandler callbackHandler;
/*
* **
* Almacena la correlación de estado compartido pasada desde LoginContext.
* ***
*/
private Map sharedState;
/*
* **
* Almacena la correlación de opciones pasada desde LoginContext.
* ***
*/
private Map options;
/*
* **
* Este valor se utiliza para almacenar la ejecución satisfactoria o la anomalía del método login() para que
* los métodos commit() y abort() puedan actuar de forma distinta en los dos casos, si se desea.
* ***
*/
private boolean succeeded = false;
/**
* **Construir un objeto de módulo de correlación no inicializado. ***
*/
public SampleLTPASAFMappingModule()
{
}
/**
* **Inicializar este módulo de inicio de sesión.***
*
* **
* LoginContext realiza una llamada a este método después de que se instancie
* este módulo de inicio de sesión. La información relevante se pasa de LoginContext
* a este módulo de inicio de sesión. Si el módulo de inicio de sesión no comprende los datos
* almacenados en los parámetros sharedState y options,
* se pueden ignorar.
* ***
*
* @param subject
* Sujeto en el que se autentica este LoginContext
* @param callbackHandler
* Un CallbackHandler para comunicarse con el usuario final para recopilar
información de inicio de sesión (por ejemplo, nombre de usuario y contraseña).
* @param sharedState
* Estado compartido con otros módulos de inicio de sesión configurados.
* @param options
* Opciones especificadas en la configuración de inicio de sesión de este módulo de inicio de sesión concreto.
*/
public void initialize(Subject newSubject, CallbackHandler newCallbackHandler, Map newSharedState, Map newOptions)
{
// obtenga el valor de la depuración antes que nada para que se pueda utilizar el rastreo dentro
// de este método
if (newOptions.containsKey("debug"))
{
String debugEnabledString = (String) newOptions.get("debug");
if (debugEnabledString != null && debugEnabledString.toLowerCase().equals("true"))
{
debugEnabled = true;
}
}
if (debugEnabled)
{
debug("initialize() entry");
}
// obtenga el valor del ámbito antes que nada para ver si este módulo de inicio de sesión debe
// procesar sólo un ámbito entrante concreto o si debe procesar todo el ámbito. Se procesará
// un valor nulo para todos los ámbitos.
if (newOptions.containsKey("realm"))
{
onlyThisRealm = (String) newOptions.get("realm");
onlyThisRealm = onlyThisRealm.trim();
}
// este módulo de inicio de sesión no va a utilizar ninguno de estos objetos excepto para el estado compartido,
// pero para obtener coherencia con la mayor parte de módulos de inicio de sesión, guardaremos una referencia a todos ellos
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 si la autenticación ha sido satisfactoria o false
* si este módulo de inicio de sesión debe ignorarse
* @exception LoginException
* si la autenticación falla, lo cual es imposible para este módulo de inicio de sesión
*/
public boolean login() throws LoginException
{
if (debugEnabled)
{
debug("login() entry");
}
// Manejar la devolución de llamada
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[] { "Se ha detectado una excepción en callbackhandler: ", e });
}
return false;
}
byte[] credToken = ((WSCredTokenCallbackImpl) callbacks[0]).getCredToken();
String uid = null;
String realm = null;
String uniqueID = null;
//Esta rutina sólo procesará una señal de LTPA. Si no hay ninguna señal entrante,
//esta rutina devolverá el valor true y permitirá que los otros LM manejen
//esta solicitud
if (credToken != null)
{
try
{
uniqueID = WSSecurityPropagationHelper.validateLTPAToken(credToken);
realm = WSSecurityPropagationHelper.getRealmFromUniqueID (uniqueID);
uid = createSAFIdenityName (uniqueID);
if (debugEnabled)
{
debug("utilizando uniqueID: "+ uniqueID+ " ámbito entrante: "+ realm +
"uid: " + uid );
}
}
catch (Exception e)
{
if (debugEnabled)
{
debug(new Object[] { "Se ha detectado una excepción en callbackhandler: ", e });
}
return false;
}
}
else return true; // dejar que el otro LM maneje esta solicitud.
//onlyThisRealm es un valor de propiedad de entrada para ver si queremos que este módulo de inicio de sesión
//maneje sólo 1 solicitud de ámbito de entrada. Si onlyThisRealm es nulo,
//se procesará toda la solicitud y se continuará. Si onlyThisRealm no
//coincide con la solicitud entrante extraída de LTPA, se deja que el otro
//LM maneje esta solicitud.
//
if ((onlyThisRealm != null) && (!realm.trim().equals(onlyThisRealm))) {
if (debugEnabled)
{
debug("ámbito entrante de "+realm+" no coincide con ámbito de opción de "+onlyThisRealm);
}
return true;
}
try {
// Recupera InitialContext por omisión para este servidor.
javax.naming.InitialContext ctx = new javax.naming.InitialContext();
// Recupera el objeto UserRegistry local.
com.ibm.websphere.security.UserRegistry reg =
(com.ibm.websphere.security.UserRegistry) ctx.lookup("UserRegistry");
// Recupera el uniqueID de registro basado en el uid especificado
// en NameCallback.
String uniqueid = reg.getUniqueUserId(uid);
if (debugEnabled)
{
debug("uniqueid "+uniqueid);
}
// Recupera el nombre de visualización del registro de usuario basado en el uniqueID.
String securityName = reg.getUserSecurityName(uid);
if (debugEnabled)
{
debug("securityName "+securityName);
}
// Recupera los grupos asociados a este uniqueID.
java.util.List groupList = reg.getUniqueGroupIds(uid);
// Crea java.util.Hashtable con la información que ha recopilado
// de UserRegistry.
// Al establecer esta hashtable, el módulo de inicio de sesión de LTPA configurará
// correctamente la Identidad de SAF.
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);
// Añade la tabla de totales de control al estado compartido del asunto.
sharedState.put(com.ibm.wsspi.security.token.AttributeNameConstants.
WSCREDENTIAL_PROPERTIES_KEY,hashtable);
}
catch (Exception e)
{
if (debugEnabled)
{
debug(new Object[] { "Se ha detectado una excepción en callbackhandler: ", e });
}
WSLoginFailedException e2 = new WSLoginFailedException("SampleLTPASAFMappingModule ha detectado un error. "+e);
throw e2;
}
if (debugEnabled)
{
debug("login() exit");
}
return succeeded;
}
/**
* **Método para comprometer el resultado de la autenticación.***
*
* **
* Este módulo de inicio de sesión no tiene que comprometer ningún dato, por lo que simplemente se devolverá.
* ***
*
* @return true si el inicio de sesión original ha sido satisfactorio o false
* si el inicio de sesión original ha fallado
* @exception LoginException
* si la acción de comprometer falla, lo que no puede suceder en este módulo de inicio de sesión
*/
public boolean commit() throws LoginException
{
if (debugEnabled)
{
debug("commit() entry");
}
// el valor de retorno de commit() es el mismo que para la ejecución satisfactoria del inicio de sesión original
boolean returnVal = succeeded;
cleanup();
if (debugEnabled)
{
debug("commit() exit");
}
return returnVal;
}
/**
* **Método para terminar anormalmente el proceso de autenticación (fase 2).***
*
* **
* Independientemente de si el nuevo inicio de sesión original ha sido satisfactorio o no, este método borra
* nuestro estado y vuelve.
* ***
*
* @return true si el inicio de sesión original ha sido satisfactorio o false
* si el inicio de sesión original ha fallado
* @exception LoginException
* si la acción de finalización anormal falla, lo que no puede suceder en este módulo de inicio de sesión
*/
public boolean abort() throws LoginException
{
if (debugEnabled)
{
debug("abort() entry");
}
// el valor de retorno de abort() es el mismo que para la ejecución satisfactoria del inicio de sesión original
boolean returnVal = succeeded;
cleanup();
if (debugEnabled)
{
debug("abort() exit");
}
return returnVal;
}
/**
* **Método que finaliza la sesión de un Sujeto.***
*
* **
* Puesto que nuestro método de compromiso no ha modificado el sujeto, no tenemos que
* finalizar sesión ni borrar los datos y sólo se devolverá true.
* ***
*
* @return true si la finalización de sesión ha sido satisfactoria
* @exception LoginException
* si la acción de finalización de sesión falla, lo que no puede suceder en el módulo de inicio de sesión
*/
public boolean logout() throws LoginException
{
if (debugEnabled)
{
debug("logout() entry");
}
// nuestras variables locales se borran durante la acción de compromiso, por lo que no se necesita ningún borrado más
if (debugEnabled)
{
debug("logout() exit");
}
// puesto que no hay ningún elemento cuya sesión deba finalizar, la ejecución siempre es satisfactoria
return true;
}
/*
* **
* Borra nuestras variables locales; el único borrado obligatorio para
* este módulo de inicio de sesión es establecer la variable success de nuevo en false.
* ***
*/
private void cleanup()
{
if (debugEnabled)
{
debug("cleanup() entry");
}
// no hay ningún método que borrar realmente, por lo que debe restablecer simplemente la variable success
succeeded = false;
if (debugEnabled)
{
debug("cleanup() exit");
}
}
/*
* **
* Método privado para imprimir la información de rastreo. Esta implementación utiliza System.out
* para imprimir la información de rastreo en la salida estándar, pero un sistema de rastreo personalizado puede
* implementarse aquí también.
* ***
*/
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
* Método privado para generar la Identidad de SAF de la identidad de LDAP. Puede ser necesario
* que los estándares de instalación modifiquen esta rutina para extraer la Identidad de SAF de
* la Identidad de LDAP, o esta rutina puede grabarse para correlacionar la identidad LDAP y SAF.
* ***
*/
private String createSAFIdenityName(String name)
{
if (debugEnabled)
{
debug("createSAFIdenityName() entry");
}
if (debugEnabled)
{
debug("Utilizando el nombre='" + name + "' del principal");
}
name = name.toUpperCase();
int index = name.indexOf("/") + 1; // índice del primer carácter después de la primera /
System.out.println(index);
System.out.println(name);
if (index >= name.length())
{
// este bloque maneja el caso en el que la primera / es el último carácter de la serie,
// no debería suceder, pero si sucede, simplemente se puede quitar
name = name.substring(0, index - 1);
if (debugEnabled)
{
debug("Quitando la / final del nombre");
}
}
else
{
// el índice es 0 (si no existe ninguna / en el nombre) o es la posición
// después de la primera / del nombre
//
// en ambos casos, adoptaremos la subserie a partir de ese punto hasta el
final
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(","));
// abrevie el nombre si su longitud supera el máximo definido
if (name.length() > MAXIMUM_NAME_LENGTH)
{
name = name.substring(0, MAXIMUM_NAME_LENGTH);
if (debugEnabled)
{
debug("Nombre de principal de WS abreviado a " + name);
}
}
if (debugEnabled)
{
debug("createSAFIdenityName() exit");
}
return name; //devolver un Nombre de identidad de SAF
}
}
El módulo de correlación de ejemplo crea una correlación entre la solicitud entrante de RMI y las credenciales LDAP y la identidad de SAF. Este módulo puede personalizarse para realizar la correlación.