![[z/OS]](../images/ngzos.gif)
Configurando um Módulo de Mapeamento System Authorization Facility (SAF) Customizado para Repositórios Federados
É possível configurar o módulo de mapeamento System Authorization Facility (SAF) para usar repositórios federados com o adaptador de registro do usuário SAF para autorização SAF. O módulo de mapeamento SAF mapeia a identidade no repositório federado para uma identidade SAF.
Antes de Iniciar
Como os repositórios federados podem ter uma cadeia de registros do usuário, como SAF, Lightweight Directory Access Protocol (LDAP), ou um repositório baseado em arquivo, o módulo de mapeamento distingue claramente a identidade SAF que deve ser usada com a autorização SAF. Para ativar a autorização SAF para registros do sistema operacional ou outros repositórios, por exemplo, LDAP por meio do repositório federado, você deve o mapeamento de identidade SAF. Este mapeamento de identidade configura as informações de credenciais adicionais que são necessárias para o SAF.
Você pode customizar configurações de login do JAAS (Java™ Authentication and Authorization) gravando um módulo de mapeamento de login customizado. O módulo WebSphere Application Server ltpaLoginModule e o módulo AuthenLoginModule usam as variáveis de estado compartilhadas fornecidas no módulo de mapeamento para configurar a identidade apropriada para a Autorização SAF.

- Você deverá compilar o SampleVMMSAFMappingModule se precisar usar este módulo de mapeamento.
- Não será necessário configurar um módulo de mapeamento se estiver usando o recurso de mapeamento de identidade distribuída do SAF.
Sobre Esta Tarefa
//Este programa pode ser usado, executado, copiado, modificado e
// distribuído sem royalty para a finalidade de desenvolvimento,
// utilização, marketing ou distribuição.
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 demonstra um módulo de login customizado
* que mapeia o WSPrincipal existente do estado compartilhado para um
* ID do usuário do z/OS para um Repositório Federado.
*
*
*
* Os valores a seguir serão definidos para o estado compartilhado se a autenticação
* for bem-sucedida. Se a autenticação falhar, esse módulo de login ainda indicará
* sucesso, mas nenhum valor será definido para o estado compartilhado.
*
* AttributeNameConstants.ZOS_USERID
* AttributeNameConstants.ZOS_AUDIT_STRING
* AttributeNameConstants.CALLER_PRINCIPAL_CLASS
*
* Esse módulo de login não utiliza retornos de chamada ou modifica o Subject
* de forma alguma.
*
* @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 o nome desse módulo de mapeamento. Sempre que essa amostra
* de código for utilizada para criar uma classe com um nome diferente, esse valor deve ser alterado.
*
* Por padrão, esse valor é utilizado como parte da amostra do token de auditoria, e para
* propósitos de depuração.
*/
private final static String MAPPING_MODULE_NAME = "com.ibm.websphere.wim.SampleVMMSAFMappingModule";
/*
* Constante que representa o comprimento máximo de ZOS_USERID. Restrições atuais de
* nomenclatura do MVS limitam esse valor para oito caracteres.
*
* Quando a opção para useWSPrincipalName é escolhida, o nome do WSPrincipal é
* reduzido para essa quantidade de caracteres.
*/
private final static int MAXIMUM_NAME_LENGTH = 7;
/*
* Especifica se o comportamento de mapeamento padrão do módulo deve ou não ser utilizado, que é
* utilizar o nome WSPrincipal para gerar o ZOS_USERID. Isso depende do
* valor da opção "useWSPrincipalName" transmitida pelo LoginContext.
*/
private boolean useWSPrincipalName = true;
/*
* Especifica se a depuração é ativada para esse Módulo de Login. Isso depende do
* valor da opção "debug" transmitida pelo LoginContext.
*/
private boolean debugEnabled = false;
/*
* Armazena o Assunto transmitido a partir do LoginContext.
*/
private Subject subject;
/*
* Armazena o CallbackHandler transmitido pelo LoginContext.
*/
private CallbackHandler callbackHandler;
/*
* Armazena o Mapa de estado compartilhado transmitido pelo LoginContext.
*/
private Map sharedState;
javax.security.auth.Subject runas_subject, caller_subject;
/*
* Armazena o Mapa de opções transmitido pelo LoginContext.
*/
private Map options;
/*
* Esse valor é utilizado para armazenar o sucesso ou falha do método login() para
* que commit() e abort() possam agir de modo diferente nos dois casos, se desejado.
*/
private boolean succeeded = false;
/**
* Construa um objeto de módulo de mapeamento não inicializado.
*/
public SampleVMMSAFMappingModule()
{
}
/**
* Inicialize esse módulo de login.
*
* Ele é chamado pelo LoginContext depois que esse módulo de login é
* instanciado. As informações relevantes são transmitidas do LoginContext
* para esse módulo de login. Se o módulo de login não entender os dados
* armazenados no sharedState e parâmetros opcionais,
* eles podem ser ignorados.
*
* @param subject
* O assunto que esse LoginContext está autenticando
* @param callbackHandler
* Um CallbackHandler para comunicação com o usuário final
* para coletar informações de login (ex:, nome do usuário e senha).
* @param sharedState
* O estado compartilhado com outros módulos de login configurados.
* @param options
* As opções especificadas nas configurações de login desse módulo de login específico.
*/
public void initialize(Subject newSubject, CallbackHandler newCallbackHandler,
Map newSharedState, Map newOptions)
{
// obtenha primeiramente o valor para depuração, para que o rastreio possa ser utilizado
// nesse 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");
}
// nenhum desses objetos será utilizado por esse módulo de login, com exceção do sharedState,
// mas para manter consistência com a maioria dos módulos de login, manteremos uma referência a todos eles
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 mapear WSPrincipal para um ZOS_USERID
*
* Esse método deriva um ZOS_USERID e o armazena no Estado Compartilhado para ser utilizado por um
* Módulo de Login posterior.
*
* @retornar verdadeiro se a autenticação for bem-sucedida, ou falso
* se esse Módulo de Login deve ser ignorado
* @exception LoginException
* se a autenticação falhar, o que é impossível para esse Módulo de Login
*/
public boolean login() throws LoginException
{
if (debugEnabled)
{
debug("login() entry");
}
if (sharedState.containsKey(AttributeNameConstants.ZOS_USERID))
{
// não queremos substituir esse valor se, por qualquer motivo, outro Módulo de Login
// já tiver o colocado no estado compartilhado, mas ainda o consideramos um sucesso
// porque os critérios de saída desse módulo foram atendidos
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))
{
// se não houver um Proprietário ou uma Credencial no estado compartilhado, nada pode ser
// feito, portanto retornaremos false para informar ao LoginContext para ignorar esse módulo
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;
// extrair o WSPrincipal e o WSCredential do estado compartilhado
WSPrincipal principal = (WSPrincipal) sharedState.get(Constants.WSPRINCIPAL_KEY);
WSCredential credential = (WSCredential) sharedState.get(Constants.WSCREDENTIAL_KEY);
if (useWSPrincipalName)
{
// este módulo de mapeamento de amostra fornece um método para obter ZOS_USERID do uniqueID obtido
// do nome WSPrincipal se a propriedade "useWSPrincipalName" for definida para true
if (debugEnabled)
{
debug("Using name from WSPrincipal to obtain ZOS_USERID");
}
final String principalName=stripRealm(principal.getName());
String realm = getRealm(credential);
// nesse exemplo, uma amostra de token de auditoria será criada, combinando o ZOS_USERID,
// a região e o nome desse módulo de mapeamento
//
// um token de auditoria personalizado pode ser criado utilizando quaisquer dados disponíveis ao invés
// dessa amostra de token
// Um Subject pode conter mais de um Proprietário. Esse valor especifica a
// classe do Proprietário a ser retornada quando o Subject é solicitado pelo
// Proprietário Responsável pela Chamada. Se uma classe de Proprietário personalizada tiver sido colocada no
// Subject, esse nome de classe pode ser especificado aqui.
//
// Dois valores pré-definidos para a Classe de Proprietário do Responsável pela Chamada são fornecidos:
//
// - AttributeNameConstants.ZOS_CALLER_PRINCIPAL_CLASS
// a classe do Proprietário do z/OS
//
// - AttributeNameConstants.DEFAULT_CALLER_PRINCIPAL_CLASS
// a classe de Proprietário padrão
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("Using Custom logic to obtain ZOS_USERID");
}
// se o comportamento fornecido por esse módulo de mapeamento para obter ZOS_USERID do
// nome do WSPrincipal não for suficiente, lógica de mapeamento personalizada pode ser fornecida aqui
//
// para utilizar essa lógica de mapeamento personalizada, a propriedade "useWSPrincipalName" deve ser definida
// para false
// name = ...lógica personalizada
// audit = ...lógica personalizada
// principalClass = ...lógica personalizada
// por padrão, nenhum mapeamento personalizado é fornecido, portanto o sucesso desse caminho de código
// é falso; se mapeamento personalizado for fornecido, a variável a seguir deve ser
// modificada para representar o sucesso ou falha do mapeamento personalizado
succeeded = false;
}
if (succeeded)
{
// agora que temos valores para name, audit, e principalClass, precisamos apenas defini-los
// para o estado compartilhado
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[] { "Valores foram armazenados no estado compartilhado", name, audit, principalClass });
}
}
}
if (debugEnabled)
{
debug("login() exit");
}
return succeeded;
}
/**
* Método para consolidar o resultado da autenticação.
*
* Esse Módulo de Login não precisa consolidar dados, portanto simplesmente retornaremos.
*
* @retornar verdadeiro se o login original foi bem-sucedido, ou falso
* se o login original falhar
* @exception LoginException
* se a consolidação falhar, o que não pode ocorrer nesse Módulo de Login
*/
public boolean commit() throws LoginException
{
if (debugEnabled)
{
debug("commit() entry");
}
// o valor de retorno de commit() é igual ao sucesso do login original
boolean returnVal = succeeded;
cleanup();
if (debugEnabled)
{
debug("commit() exit");
}
return returnVal;
}
/**
* Método para interromper o processo de autenticação (Fase 2).
*
* Independente de nosso login original ter tido sucesso ou falhado, esse método limpa
* nosso estado e retorna.
*
* @retornar verdadeiro se o login original foi bem-sucedido, ou falso
* se o login original falhar
* @exception LoginException
* se a interrupção falhar, o que não pode ocorrer nesse Módulo de Login
*/
public boolean abort() throws LoginException
{
if (debugEnabled)
{
debug("abort() entry");
}
// o valor de retorno de abort() é igual ao sucesso do login original
boolean returnVal = succeeded;
cleanup();
if (debugEnabled)
{
debug("abort() exit");
}
return returnVal;
}
/**
* Método que efetua logout de um Subject.
*
* Como nosso método commit não modificou o Subject, não há do que
* efetuar logout ou limpar e podemos apenas retornar true.
*
* @retornar verdadeiro se o logout for bem-sucedido
* @exception LoginException
* se o logout falhar, o que não pode ocorrer no Módulo de Login
*/
public boolean logout() throws LoginException
{
if (debugEnabled)
{
debug("logout() entry");
}
// nossas variáveis locais foram limpas durante o commit, portanto não há necessidade de limpeza adicional
if (debugEnabled)
{
debug("logout() exit");
}
// como não há do que efetuar logout, sempre obtemos sucesso
return true;
}
/*
* Limpa nossas variáveis locais; a única limpeza requerida para
* esse Módulo de Login é definir nossa variável de sucesso novamente para false.
*/
private void cleanup()
{
if (debugEnabled)
{
debug("cleanup() entry");
}
// não há o que limpar, portanto iremos apenas reconfigurar nossa variável de sucesso
succeeded = false;
if (debugEnabled)
{
debug("cleanup() exit");
}
}
/*
* Método privado para imprimir informações de rastreio. A implementação utiliza System.out
* para imprimir informações de rastreio de saída padrão, mas um sistema de rastreio personalizado pode
* ser implementado aqui também.
*/
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 obter o nome da região da Credencial e retorná-lo. Este
* mantém a manipulação da exceção envolvida com a obtenção do nome da região da
* lógica login() principal.
*/
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 emite CredentialExpiredException e 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/uniqueName.
*/
private String createSAFIdentityName(String name)
{
if (debugEnabled)
{
debug("createSAFIdentityName() entry "+ name);
}
name=stripRealm(name).toUpperCase();
//Obter CN.if uniqueName no formato "uid=test,o=com" strip para "test".
if (name.indexOf("=") >= 0)
name = name.substring((name.indexOf("=")+1),name.length());
if (name.indexOf(",") > 0)
name = name.substring(0,name.indexOf(","));
//tratar caso no qual a região foi anexada :<realmname>\\userid
if(name.indexOf("\\")>0){
name = name.substring((name.indexOf("\\")+1),name.length());
}
// reduzir o nome se seu comprimento exceder o 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; //retorne um Nome de Identidade SAF
}
/*
* Private Helper method to Strip realm information if any.
*/
private String stripRealm(String name){
int index = name.indexOf("/") + 1; // índice do primeiro caractere após o primeiro /
if (index >= name.length())
{
// esse bloco manipula o caso em que o primeiro / é o último caractere da Cadeia,
// isso realmente não deve ocorrer, mas se ocorrer podemos apenas o ignorar
name = name.substring(0, index - 1);
if (debugEnabled)
{
debug("Stripping trailing / from WSPrincipal name");
}
}
else
{
name = name.substring(index);
}
return name;
}
/**
* Private helper method to deduce the z/OS ID to be mapped.
* You can use this when you want to map RACF users & other repository users in specific ways.
* This uses a Federated Repository util method, isRACFUser to identify if the user is from RACF registry.
*/
private String doCustomMapUser(String principalName){
boolean isRACFUser=true;
String mapID=null;
try{
isRACFUser=PrincipalUtil.isRACFUser(principalName);
}
catch(WIMException we){
debug("Exception happened while checking isRACFuser"+we);
//Tratar conforme necessidade.
}
mapID=isRACFUser==true?createSAFIdentityName(principalName):doMapUser(principalName);
return mapID;
}
/**
* Private helper method to deduce the z/OS ID to be mapped.
* You can use this directly if you want to map z/OS ID irrespective of RACF/Non-RACF users.
*
*/
private String doMapUser(String principalName){
//Se loginUser pertencer a outro registro como LDAP, certifique-se de que exista um usuário do MVS para corresponder ao nome derivado.
//Ou apenas mapeie para um usuário do RACF existente na linha comentada abaixo.
// name="USER237";
//se usuário existir no RACF, mapeie este usuário.
String uniqueName=null;
String mapID=null;
try {
javax.naming.InitialContext ctx = new javax.naming.InitialContext();
// Recupera o objeto UserRegistry local.
final com.ibm.websphere.security.UserRegistry reg = (com.ibm.websphere.security.UserRegistry) ctx.lookup("UserRegistry");
// Recupera uniqueName do registro com base no principalName
uniqueName = reg.getUniqueUserId(principalName);
} catch (Exception e) {
debug("Exception thrown while getting uniqueID: "+e);
}
mapID=createSAFIdentityName(uniqueName);
return mapID;
}
}