![[z/OS]](../images/ngzos.gif)
Configuración de un módulo de correlación SAF (System Authorization Facility) para repositorios federados
Puede configurar el módulo de correlación SAF (System Authorization Facility) para utilizar repositorios federados con el adaptador de registro de usuarios SAF para la autorización SAF. El módulo de correlación SAF correlaciona la identidad del repositorio federado con una identidad SAF.
Antes de empezar
Como los repositorios federados pueden tener una cadena de registros de usuarios, como por ejemplo, SAF, Lightweight Directory Access Protocol (LDAP) o un repositorio basado en archivos, el módulo de correlación distingue claramente la identidad SAF que debe utilizarse con autorización SAF. Para habilitar la autorización SAF para los registros del sistema operativo u otros registros, por ejemplo LDAP a través del repositorio federado, es necesaria la correlación de identidades SAF. Esta correlación de identidades establece la información de credenciales adicionales que es necesaria para SAF.
Puede personalizar las configuraciones de inicio de sesión JAAS (Java™ Authentication and Authorization) escribiendo un módulo de correlación de inicios de sesión personalizado. El módulo ltpaLoginModule y el módulo AuthenLoginModule de WebSphere Application Server utilizan las variables de estado compartido que se proporcionan en el módulo de correlación para establecer la identidad adecuada para la autorización SAF.

- Debe compilar SampleVMMSAFMappingModule si necesita utilizar este módulo de correlación.
- No es necesario configurar un módulo de correlación si está utilizando la característica de correlación de identidad distribuida SAF.
Acerca de esta tarea
//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.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 demuestra un módulo de inicio de sesión personalizado
* que correlaciona el principal de WS existente del estado compartido con un ID
* de usuario de z/OS para un repositorio federado
*
*
*
* Los valores siguientes se establecerán en el estado compartido si la autenticación
* es satisfactoria. Si la autenticación falla, este módulo de inicio de sesión aún indicará
* una ejecución satisfactoria, pero no se establecerán valores en el estado compartido.
*
* AttributeNameConstants.ZOS_USERID
* AttributeNameConstants.ZOS_AUDIT_STRING
* AttributeNameConstants.CALLER_PRINCIPAL_CLASS
*
* Este módulo de inicio de sesión no utiliza llamadas de retorno ni modifica el sujeto
* de ninguna forma.
*
* @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);
/*
* 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.wim.SampleVMMSAFMappingModule";
/*
* Constante que representa la longitud máxima de ZOS_USERID. Las restricciones de denominación de MVS
* la limitan a ocho caracteres.
*
* Cuando se elige la opción useWSPrincipalName, el nombre del principal de WS se
* abrevia a estos caracteres.
*/
private final static int MAXIMUM_NAME_LENGTH = 7;
/*
* Especifica si debe utilizarse o no el funcionamiento de correlación predeterminada de este módulo, que
* es utilizar el nombre del principal de WS para generar el ZOS_USERID. Esto depende del
* valor de la opción "useWSPrincipalName" pasada desde LoginContext.
*/
private boolean useWSPrincipalName = true;
/*
* 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;
/*
* 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;
javax.security.auth.Subject runas_subject, caller_subject;
/*
* 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 SampleVMMSAFMappingModule()
{
}
/**
* 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
* 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");
}
// 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 (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 });
}
}
/**
* Método para correlación el principal de WS con un ID_USUARIO_ZOS
*
* Este método deriva un ZOS_USERID y lo almacena en el estado compartido para que lo utilice un
* módulo de inicio de sesión posterior.
*
* @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");
}
if (sharedState.containsKey(AttributeNameConstants.ID_USUARIO_ZOS))
{
// no deseamos alterar temporalmente este valor si, por algún motivo, otro módulo de inicio de sesión
// ya lo ha colocado en el estado compartido, pero aún lo considera como una ejecución satisfactoria
// porque los criterios de salida de este módulo se han cumplido
if (debugEnabled)
{
debug("ID_USUARIO_ZOS ya existe: no se necesita trabajo adicional");
}
succeeded = true;
}
else if (!sharedState.containsKey(Constants.CLAVE_PRINCIPAL_WS) ||
!sharedState.containsKey(Constants.CLAVE_CREDENCIAL_WS))
{
// si no hay ningún principal ni credencial en el estado compartido, no podemos
// hacer nada, por lo que devolveremos el valor false para informar a LoginContext de que ignore este módulo
if (debugEnabled)
{
debug("El principal o la credencial no está disponible: ignorando este módulo de inicio de sesión");
}
succeeded = false;
}
else
{
if (debugEnabled)
{
debug("El principal y la credencial están disponibles: continúa el inicio de sesión");
}
String name = null;
String audit = null;
String principalClass = null;
// extrae WSPrincipal y WSCredential del estado compartido
WSPrincipal principal = (WSPrincipal) sharedState.get(Constants.CLAVE_PRINCIPAL_WS);
WSCredential credential = (WSCredential) sharedState.get(Constants.CLAVE_CREDENCIAL_WS);
if (useWSPrincipalName)
{
// este módulo de correlación de ejemplo proporciona un método para obtener ZOS_USERID
// del nombre de principal de WS si la propiedad "useWSPrincipalName" se establece en true
if (debugEnabled)
{
debug("Utilizando el nombre de principal de WS para obtener ZOS_USERID");
}
final String principalName=stripRealm(principal.getName());
String realm = getRealm(credential);
// para este ejemplo, se creará un símbolo de auditoría de ejemplo que combine el ZOS_USERID,
// el reino y el nombre de este módulo de correlación
//
// se puede crear un símbolo de auditoría personalizado con los valores disponibles en lugar
de utilizar
// este símbolo de ejemplo
// Un sujeto puede contener más de un principal. Este valor especifica la
// clase del principal que debe devolverse cuando se solicita al sujeto
// el principal llamante. Si una clase de principal personalizado se ha colocado
// en el sujeto, ese nombre de clase debe especificarse aquí.
//
// Se proporcionan dos valores predefinidos para la clase de principal llamante:
//
// - AttributeNameConstants.ZOS_CALLER_PRINCIPAL_CLASS
// clase de principal de z/OS
//
// - AttributeNameConstants.DEFAULT_CALLER_PRINCIPAL_CLASS
// clase de principal predeterminada
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("Utilizando el nombre de lógica personalizada para obtener ZOS_USERID");
}
// Si el funcionamiento proporcionado por este módulo de correlación para obtener el ZOS_USERID del
// nombre de principal de WS no es suficiente, la lógica de correlación personalizada se puede proporcionar aquí.
//
// Para utilizar esta lógica de correlación personalizada, la propiedad "useWSPrincipalName" debe establecerse
// en false.
// name = ...lógica personalizada
// audit = ...lógica personalizada
// principalClass = ...lógica personalizada
// De forma predeterminada, no se proporciona ninguna correlación personalizada, por lo que la ejecución satisfactoria
// de esta vía de acceso tiene el valor false; si se proporciona la correlación personalizada, la siguiente variable
// debe modificarse para representar la ejecución satisfactoria o no de la correlación personalizada
succeeded = false;
}
if (succeeded)
{
// Ahora que tenemos valores para el nombre, la auditoría y la clase de principal, sólo tenemos
// que establecerlos en el estado compartido.
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[] { "Los valores se han almacenado en estado compartido", name, audit, principalClass });
}
}
}
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);
}
}
}
/*
* Método privado para obtener el nombre de reino de la credencial y devolverlo. Este
* conserva el manejo de excepciones que implica la obtención del nombre de reino de la
* lógica principal de login().
*/
private String getRealm(WSCredential credential)
{
if (debugEnabled)
{
debug("getRealm() entry");
}
String realm = null;
try
{
realm = credential.getRealmName();
if (debugEnabled)
{
debug("Se ha obtenido el reino='" + realm + "' de la credencial");
}
}
catch (Exception e)
{
// getRealmName genera CredentialExpiredException y CredentialDestroyedException
if (debugEnabled)
{
//debug(new Object[] { "Se ha detectado una excepción en getRealm: ", e });
}
realm = "UNKNOWN_REALM";
}
if (debugEnabled)
{
debug("getRealm() exit");
}
return realm;
}
/*
* Método privado para generar el ZOS_USERID del name/uniqueName de WSPrincipal.
*/
private String createSAFIdentityName(String name)
{
if (debugEnabled)
{
debug("createSAFIdentityName() entry "+ name);
}
name=stripRealm(name).toUpperCase();
//Obtenga CN.if uniqueName de formato "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(","));
//maneje el caso donde el reino ha añadido :<nombre_reino>\\userid
if(name.indexOf("\\")>0){
name = name.substring((name.indexOf("\\")+1),name.length());
}
// 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("WSPrincipal/uniqueName shortened to " + name);
}
}
if (debugEnabled)
{
debug("createSAFIdentityName() exit");
}
return name; //devolver un Nombre de identidad de SAF
}
/*
* Método de ayudante privado para eliminar la información de reino.
*/
private String stripRealm(String name){
int index = name.indexOf("/") + 1; // índice del primer carácter después de la primera /
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 de principal de WS");
}
}
else
{
name = name.substring(index);
}
return name;
}
/**
* Método de ayudante privado para deducir el ID de z/OS a correlacionar.
* Puede utilizarlo cuando desee correlacionar usuarios RACF y otros usuarios de repositorio de formas concretas.
* Se utiliza un método de programa de utilidad de repositorio federado,isRACFUser para identificar si el usuario es del registro RACF.
*/
private String doCustomMapUser(String principalName){
boolean isRACFUser=true;
String mapID=null;
try{
isRACFUser=PrincipalUtil.isRACFUser(principalName);
}
catch(WIMException we){
debug("Se ha producido una excepción al comprobar isRACFuser"+we);
//Gestionarlo según sea necesario.
}
mapID=isRACFUser==true?createSAFIdentityName(principalName):doMapUser(principalName);
return mapID;
}
/**
* Método de ayudante privado para deducir el ID de z/OS a correlacionar.
* Puede utilizarlo directamente si desea correlacionar el ID de z/OS ID con independencia de los usuarios RACF/No RACF.
*
*/
private String doMapUser(String principalName){
//Si loginUser pertenece a otro registro como LDAP asegúrese de que existe un usuario MVS que coincida con el nombre derivado.
//O sólo establezca una correlación con cualquier usuario RAFC existente como en la línea comentada siguiente.
// name="USER237";
//si el usuario existe en RACF, establezca una correlación con ese usuario.
String uniqueName=null;
String mapID=null;
try {
javax.naming.InitialContext ctx = new javax.naming.InitialContext();
// Recupera el objeto UserRegistry local.
final com.ibm.websphere.security.UserRegistry reg = (com.ibm.websphere.security.UserRegistry) ctx.lookup("UserRegistry");
// Recupera el registro uniqueName basado en el principalName
uniqueName = reg.getUniqueUserId(principalName);
} catch(Exception e) {
debug("Exception thrown while getting uniqueID: "+e);
}
mapID=createSAFIdentityName(uniqueName);
return mapID;
}
}