Implementando a Autenticação em Dois Fatores (2FA) no Login do Protheus | Ti Responde 0193

No vídeo de hoje, vamos demonstrar em como criar uma autenticação de dois fatores no momento de logar no Protheus.

A dúvida de hoje, nos perguntaram, se seria possível criar uma customização que gerasse um token e enviasse para o email do usuário no momento que ele esta fazendo login.

 

Pensando nisso, montamos um exemplo, onde vamos demonstrar como foi feito usando o ponto de entrada PswSize e a função StartJob.

 

Segue abaixo o vídeo exemplificando:

 

E abaixo o código fonte desenvolvido:

//Bibliotecas
#Include "TOTVS.ch"
#Include "TopConn.ch"

/*/{Protheus.doc} User Function PswSize
Efetua a validação do usuário digitado
@type  Function
@author Atilio
@since 05/09/2024
@see https://tdn.totvs.com/pages/releaseview.action?pageId=6815189
/*/

User Function PswSize()
    Local aRet      := PARAMIXB
    Local aToken    := {.F., ""}
    Local cToken    := ""
    Local cDigitado := ""

    //Se não veio via JOB / WS, mostra uma mensagem e veio do SIGAMDI
    If ! IsBlind() .And. LjIsMDI()
        //Aciona o disparo do eMail e vai gravar o conteúdo dentro de um arquivo
        aToken := StartJob("u_zV93Mail", GetEnvServer(), .T., aRet[1])
        
        //Se deu certo o disparo do eMail
        If aToken[1]

            //Faz a leitura do arquivo e armazena na variável
            cToken := MemoRead(aToken[2])

            //Apaga o arquivo para prevenir que não haja leitura manual
            FErase(aToken[2])

            //Faz um laço de repetição infinito
            While .T.
                //Solicita a digitação do Token
                cDigitado := FWInputBox("Insira o Token recebido por eMail: ")

                //Se tiver vazio, pergunta se o usuário quer tentar novamente
                If Empty(cDigitado)
                    If FWAlertYesNo("Não foi informado o Token. Você deseja tentar novamente (clique em Sim) ou quer cancelar (clique em Não)?", "Qual opção?")
                        Loop
                    Else
                        Final("Login cancelado pelo usuário")
                    EndIf
                EndIf

                //Se bater a informação digitada, encerra o laço
                If cDigitado == cToken
                    Exit

                //Senão, mostra mensagem e volta o laço
                Else
                    FWAlertWarning("Token inválido, tente novamente!", "Atenção")
                EndIf

            EndDo

        //Se não, volta pro usuário tentar novamente
        Else
            FWAlertError("Infelizmente não foi possível gerar o Token de autenticação. Contate o Administrador do Sistema!", "Falha")
            aRet := {"", ""}
        EndIf
    EndIf

Return aRet

/*/{Protheus.doc} User Function zV93Mail
Função que vai disparar o email, foi usada separadamente por causa do RPCSetEnv
@type  Function
@author Atilio
@since 05/09/2024
@param cParLogin, Caractere, Login que o usuário inseriu na tela de autenticação
@return aRetorno, Array, Retorna um Array com duas posições sendo [1] Se deu certo o disparo do eMail e [2] nome do arquivo temporário com o token
/*/

User Function zV93Mail(cParLogin)
    Local aArea
    Local aRetorno   := {.F., ""}
    Local cEmpAux    := "99"
    Local cFilAux    := "01"
    Local cUsrAux    := ""
    Local cPswAux    := ""
    Local cQryUsr    := ""
    Local cParaMail  := ""
    Local cCorpoMail := ""
    Local lOkMail    := .F.
    Local cArquivo   := ""
    Local cToken     := ""
    Local cPasta     := "\x_temp\"

    //Prepara o ambiente sem usuário mesmo, só para dispararmos eMail
    If Select("SX2") == 0
        RpcSetEnv(cEmpAux, cFilAux, cUsrAux, cPswAux)
    EndIf
    aArea := FWGetArea()

    //Busca o endereço de eMail do usuário e o código dele
    cQryUsr := " SELECT  " + CRLF
    cQryUsr += "     USR_EMAIL, " + CRLF
    cQryUsr += "     USR_NOME, " + CRLF
    cQryUsr += "     USR_ID " + CRLF
    cQryUsr += " FROM  " + CRLF
    cQryUsr += "     SYS_USR " + CRLF
    cQryUsr += " WHERE  " + CRLF
    cQryUsr += "     USR_CODIGO = '" + cParLogin +  "' " + CRLF
    cQryUsr += "     AND D_E_L_E_T_ = ' ' " + CRLF
    TCQuery cQryUsr New Alias "QRY_USR"

    //Se tiver dados
    If ! QRY_USR->(EoF())

        //Se a pasta não existir, cria
        If ! ExistDir(cPasta)
            MakeDir(cPasta)
        EndIf

        //Se tiver campo eMail preenchido
        cParaMail := Alltrim(QRY_USR->USR_EMAIL)
        If ! Empty(cParaMail)

            //Gera um Token com o Código do usuário, valor randômico da Hora e da Data, depois aplica o Embaralha, e o Encode64
            cToken := QRY_USR->USR_ID + RandByTime() + RandByDate()
            cToken := Embaralha(cToken, 0)
            cToken := Encode64(cToken)

            //Pega o próximo alias disponível e define o nome do arquivo dentro de uma pasta da Protheus Data
            cArquivo := GetNextAlias()
            cArquivo := cPasta + cArquivo + ".txt"

            //Realiza o disparo do eMail
            cCorpoMail := '<p>Olá <strong>' + QRY_USR->USR_NOME + '</strong>.</p>'
            cCorpoMail += '<p>Você tentou acessar o ERP Protheus recentemente né?</p>'
            cCorpoMail += '<p>Se sim, seu Token de acesso é: <strong>' + cToken + '</strong></p>'
            cCorpoMail += '<p>Se não, entre em contato urgentemente com o email <a href="mailto:contato@atiliosistemas.com">contato@atiliosistemas.com</a>.</p>'
            cCorpoMail += '<p>Um grande abraço.</p>'
            lOkMail    := GPEMail("Protheus - Token Temporário", cCorpoMail, cParaMail, /*aAnexos*/, /*lExibeHelp*/)

            //Se deu certo o disparo do email, define o retorno e grava num arquivo para leitura posterior
            If lOkMail
                aRetorno[1] := .T.
                aRetorno[2] := cArquivo

                //Grava o token no arquivo
                MemoWrite(cArquivo, cToken)
            EndIf
        EndIf
        
    EndIf
    QRY_USR->(DbCloseArea())

    FWRestArea(aArea)
Return aRetorno

Bom pessoal, por hoje é só.

 

Abraços e até a próxima.

Dan (Daniel Atilio)
Cristão de ramificação protestante. Especialista em Engenharia de Software pela FIB, graduado em Banco de Dados pela FATEC Bauru e técnico em informática pelo CTI da Unesp. Entusiasta de soluções Open Source e blogueiro nas horas vagas. Autor e mantenedor do portal Terminal de Informação.

Deixe uma resposta

Terminal de Informação