[z/OS]

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.

Evitar Problemas Evitar Problemas:
  • 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.
gotcha

Sobre Esta Tarefa

No exemplo de código a seguir, a confiança é estabelecida sobre a disponibilidade de uma identidade Java Platform, Enterprise Edition (Java EE) para controlar o mapeamento para uma identidade SAF. Este código usa o valor Constants.WSPRINCIPAL_KEY no estado compartilhado. O valor é colocado no código por um módulo de login do WebSphere Application Server. É possível inserir um módulo de login customizado após o módulo ltpaLoginModule e antes do módulo MapPlatformSubject. Se você incluir um módulo de login customizado, use uma matriz de retorno de chamada ou outros valores de estado compartilhado para obter um valor que seja usado para controlar o mapeamento para o ID do usuário do z/OS.
//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;
        }
    }
O módulo de mapeamento de amostra cria um mapeamento entre a identidade do registro do usuário para o repositório federado e a identidade do z/OS. A identidade para o registro do usuário é usada como o ID do usuário do z/OS. Se você precisar de um mapeamento diferente, poderá customizar este módulo, que é mostrado na cláusula else como parte da amostra de código anterior, para executar o mapeamento.
Nota: Um método de utilitário de repositórios federados, chamado PrincipalUtil.isRACFUser, é usado para identificar se o usuário é do registro RACF. Este método retorna true se o usuário for um usuário RACF. Use este método apenas se o registro do usuário atual estiver configurado nos repositórios federados.

Procedimento

  1. Compile o código Java.
    1. Certifique-se de que o código seja confiável e tratado com a mesma atenção que um módulo autorizado por Authorized Program Facility (APF). A configuração de login do sistema JAAS (Java Authorization and Authentication Service) padrão é acessada no controlador do z/OS.
    2. Certifique-se de que sua configuração de caminho da classe inclua os arquivos Java archive (JAR) corretos: É possível usar um dos seguintes conjuntos de arquivos JAR:
      • WebSphere_Application_Client/plugins/org.eclipse.emf.commonj.sdo.jar, WebSphere_Application_Client/plugins/com.ibm.ws.emf.jar e WebSphere_Application_Client/plugins/com.ibm.ws.runtime.client.jar
      • WAS_HOME/plugins/org.eclipse.emf.commonj.sdo.jar, WAS_HOME/plugins/com.ibm.ws.runtime.wim.base.jar e WAS_HOME/plugins/com.ibm.ws.runtime.jar
  2. Instale a classe no diretório classes do WebSphere Application Server e os gerenciadores de implementação se especificar uma classe de mapeamento diferente do padrão fornecido pela IBM®.
  3. Coloque o arquivo JAR no diretório WAS_HOME/classes para cada nó na célula, incluindo o nó do gerenciador de implementação em uma célula do WebSphere Application Server, Network Deployment.
  4. Reinicie o WebSphere Application Server para que as mudanças entrem em vigor.
  5. Ative a criação de log para o SampleSAFMappingModule ao especificar uma propriedade customizada para este módulo:
    1. No console administrativo do WebSphere Application Server, clique em Segurança > Segurança Global.
    2. Em Autenticação, expanda Java Authentication and Authorization Service.
    3. Clique em Logins do Sistema.
    4. Clique em login_module_name.
    5. Nos módulos de login do JAAS, clique em com.ibm.websphere.wim.SampleVMMSAFMappingModule.
    6. Em Propriedades Customizadas, clique em Novo.
    7. No campo Nome, insira debug e, no campo de valor, insira true.
    8. Clique em Aplicar e, em seguida, clique em Salvar.

Ícone que indica o tipo de tópico Tópico de Tarefa



Ícone de registro de data e hora Última atualização: last_date
http://www14.software.ibm.com/webapp/wsbroker/redirect?version=cord&product=was-nd-mp&topic=tsec_customsaffedrepos
Nome do arquivo: tsec_customsaffedrepos.html