first commit
This commit is contained in:
0
app/Module/Auth/blank_file
Executable file
0
app/Module/Auth/blank_file
Executable file
158
app/Module/Auth/v0/Controllers/AuthController.php
Executable file
158
app/Module/Auth/v0/Controllers/AuthController.php
Executable file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
namespace Zampet\Module\Auth\v0\Controllers;
|
||||
|
||||
use Zampet\Helpers\Sanitizer;
|
||||
use Zampet\Services\JWTService;
|
||||
use AxiumPHP\Helpers\RequestHelper;
|
||||
use Zampet\Module\Auth\v0\DTO\UsuarioDTO;
|
||||
use Zampet\Module\Auth\v0\Services\AuthService;
|
||||
|
||||
class AuthController {
|
||||
/**
|
||||
* Lida com a requisição de registro (criação) de um novo usuário.
|
||||
*
|
||||
* Este método atua como um **controlador** para o endpoint de registro. Ele recebe os dados do formulário, realiza uma validação de presença básica nos campos obrigatórios, sanitiza os dados e delega a lógica de negócio e persistência para o `AuthService`. A resposta final é formatada e enviada como JSON.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Coleta de Dados:** Obtém os dados do formulário (`fullName`, `email`, `password`) via requisição POST.
|
||||
* 2. **Validação de Presença:** Itera sobre os campos obrigatórios (`fullName`, `email`, `password`). Se qualquer um estiver vazio, o método envia uma resposta JSON com o código **400 Bad Request** e encerra a execução.
|
||||
* 3. **Sanitização e DTO:** Os dados são sanitizados (string, email) e encapsulados em um **`UsuarioDTO`**.
|
||||
* 4. **Delegação ao Serviço:** O método `register` do `AuthService` é chamado com o DTO. O serviço é responsável por validações de complexidade, checagem de duplicidade, criptografia de senha e persistência no banco de dados.
|
||||
* 5. **Envio da Resposta JSON:** O resultado do serviço (`serviceResult`) é formatado e enviado de volta ao cliente. O código de resposta HTTP e o status JSON são extraídos do array retornado pelo serviço.
|
||||
*
|
||||
* @return void Este método não retorna um valor, pois seu objetivo é enviar uma resposta JSON e encerrar a execução do script.
|
||||
*/
|
||||
public function register(): void {
|
||||
// Receber dados do formulário de registro
|
||||
$form = RequestHelper::getFilteredInput(form_type: INPUT_POST);
|
||||
|
||||
// Dados Obrigatórios
|
||||
$required_fields = [
|
||||
'fullName',
|
||||
'email',
|
||||
'cpf',
|
||||
'birthDate',
|
||||
'password'
|
||||
];
|
||||
|
||||
// Verificar se todos os campos obrigatórios estão presentes
|
||||
foreach ($required_fields as $field) {
|
||||
if (empty($form[$field])) {
|
||||
RequestHelper::sendJsonResponse(
|
||||
response_code: 400,
|
||||
status: 'error',
|
||||
message: "Field '{$field}' is required"
|
||||
);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Chama o serviço de autenticação para registrar o usuário
|
||||
$serviceResult = (new AuthService())->register(usuarioDTO:
|
||||
new UsuarioDTO(data: [
|
||||
'nome_completo' => Sanitizer::string(value: $form['fullName']),
|
||||
'email' => Sanitizer::email(value: $form['email']),
|
||||
'senha' => Sanitizer::string(value: $form['password']),
|
||||
'documentcpf' => Sanitizer::document(value: $form['cpf']),
|
||||
'data_nascimento' => Sanitizer::string(value: $form['birthDate'])
|
||||
])
|
||||
);
|
||||
|
||||
// Enviar a resposta JSON
|
||||
RequestHelper::sendJsonResponse(
|
||||
response_code: $serviceResult['response_code'],
|
||||
status: $serviceResult['status'],
|
||||
message: $serviceResult['message'],
|
||||
output: $serviceResult['output'] ?? []
|
||||
);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lida com a requisição de login de um usuário via POST.
|
||||
*
|
||||
* Este método atua como um **controlador** para o endpoint de login. Ele processa as credenciais enviadas pelo usuário, realiza uma validação de presença básica e delega o processo de autenticação a um serviço. A resposta da autenticação é então formatada e enviada de volta ao cliente como um JSON.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Coleta de Dados:** Obtém os dados do formulário (`email` e `password`) enviados via requisição POST.
|
||||
* 2. **Validação de Presença:** Itera sobre os campos obrigatórios. Se qualquer 3campo estiver vazio, uma resposta JSON com o código **400 Bad Request** é enviada e a execução é encerrada.
|
||||
* 3. **Delegação ao Serviço:** O método `login` do **`AuthService`** é chamado com o e-mail e a senha fornecidos. O serviço é responsável por toda a lógica de autenticação (busca, verificação de senha, revogação de tokens e geração de novo JWT).
|
||||
* 4. **Envio da Resposta JSON:** O resultado do serviço (`serviceResult`) é formatado para uma resposta JSON padronizada usando **`RequestHelper::sendJsonResponse`**, incluindo o código HTTP, status, mensagem e os dados de saída (como o token JWT). A execução é encerrada.
|
||||
*
|
||||
* @return void Este método não retorna um valor, pois seu objetivo é enviar uma resposta JSON e encerrar a execução do script.
|
||||
*/
|
||||
public function login(): void {
|
||||
// Receber dados do formulário de login
|
||||
$form = RequestHelper::getFilteredInput(form_type: INPUT_POST);
|
||||
|
||||
// Dados Obrigatórios
|
||||
$required_fields = [
|
||||
'email',
|
||||
'password'
|
||||
];
|
||||
|
||||
// Verificar se todos os campos obrigatórios estão presentes
|
||||
foreach ($required_fields as $field) {
|
||||
if (empty($form[$field])) {
|
||||
RequestHelper::sendJsonResponse(
|
||||
response_code: 400,
|
||||
status: 'error',
|
||||
message: "Field '{$field}' is required"
|
||||
);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Chamar o serviço de autenticação para fazer login
|
||||
$serviceResult = (new AuthService())->login(email: $form['email'], password: $form['password']);
|
||||
|
||||
// Enviar a resposta JSON
|
||||
RequestHelper::sendJsonResponse(
|
||||
response_code: $serviceResult['response_code'],
|
||||
status: $serviceResult['status'],
|
||||
message: $serviceResult['message'],
|
||||
output: $serviceResult['output'] ?? []
|
||||
);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lida com a requisição de logout, revogando o token de autenticação do usuário.
|
||||
*
|
||||
* Este método atua como um **controlador** para o endpoint de logout. Ele extrai o token JWT do cabeçalho da requisição, delega a lógica de revogação para a camada de serviço e envia uma resposta JSON formatada de volta ao cliente.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Obtenção do Token:** O token Bearer é extraído do cabeçalho da requisição usando `JWTService::getBearerToken()`.
|
||||
* 2. **Delegação ao Serviço:** O método `logout` do **`AuthService`** é chamado com o token. O serviço é responsável por revogar o token no banco de dados e gerenciar a transação.
|
||||
* 3. **Envio da Resposta JSON:** O resultado da operação do serviço (`serviceResult`) é formatado para uma resposta JSON padronizada usando **`RequestHelper::sendJsonResponse`**, garantindo que o código HTTP, status, mensagem e quaisquer dados de saída sejam comunicados ao cliente.
|
||||
*
|
||||
* @return void Este método não retorna um valor, pois seu objetivo é enviar uma resposta JSON e encerrar a execução do script.
|
||||
*/
|
||||
public function logout(): void {
|
||||
// Chamar o serviço de autenticação para fazer logout
|
||||
$serviceResult = (new AuthService())->logout(token: JWTService::getBearerToken());
|
||||
|
||||
// Enviar a resposta JSON
|
||||
RequestHelper::sendJsonResponse(
|
||||
response_code: $serviceResult['response_code'],
|
||||
status: $serviceResult['status'],
|
||||
message: $serviceResult['message'],
|
||||
output: $serviceResult['output'] ?? []
|
||||
);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function getUserData(): void {
|
||||
// Chamar o serviço de autenticação para obter os dados do usuário
|
||||
$serviceResult = (new AuthService())->getUserData(token: JWTService::getBearerToken());
|
||||
|
||||
// Enviar a resposta JSON
|
||||
RequestHelper::sendJsonResponse(
|
||||
response_code: $serviceResult['response_code'],
|
||||
status: $serviceResult['status'],
|
||||
message: $serviceResult['message'],
|
||||
output: $serviceResult['output'] ?? []
|
||||
);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
237
app/Module/Auth/v0/DTO/UsuarioDTO.php
Executable file
237
app/Module/Auth/v0/DTO/UsuarioDTO.php
Executable file
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
namespace Zampet\Module\Auth\v0\DTO;
|
||||
|
||||
class UsuarioDTO {
|
||||
private ?int $id = null;
|
||||
private ?string $uuid = null;
|
||||
private ?string $nome_completo = null;
|
||||
private ?int $status_id = 1;
|
||||
private ?string $documentcpf = null;
|
||||
private ?string $documentcrmv = null;
|
||||
private ?string $email = null;
|
||||
private ?string $senha = null;
|
||||
private ?string $telefone = null;
|
||||
private ?string $data_nascimento = null;
|
||||
private ?string $endereco_rua = null;
|
||||
private ?string $endereco_numero = null;
|
||||
private ?string $endereco_complemento = null;
|
||||
private ?string $endereco_bairro = null;
|
||||
private ?string $endereco_cidade = null;
|
||||
private ?string $endereco_uf = null;
|
||||
private ?string $endereco_cep = null;
|
||||
private ?string $created_at = null;
|
||||
private ?string $updated_at = null;
|
||||
private ?string $deleted_at = null;
|
||||
|
||||
/**
|
||||
* Construtor do DTO.
|
||||
*
|
||||
* Permite inicializar os atributos do objeto a partir de um array associativo.
|
||||
*
|
||||
* @param array<string, mixed> $data Array associativo opcional para popular os atributos.
|
||||
*/
|
||||
public function __construct(array $data = []) {
|
||||
if (!empty($data)) {
|
||||
$this->fromArray($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Popula o DTO a partir de um array.
|
||||
*
|
||||
* @param array<string, mixed> $data
|
||||
* @return void
|
||||
*/
|
||||
public function fromArray(array $data): void {
|
||||
$this->id = $data['id'] ?? $this->id;
|
||||
$this->uuid = $data['uuid'] ?? $this->uuid;
|
||||
$this->nome_completo = $data['nome_completo'] ?? $this->nome_completo;
|
||||
$this->status_id = $data['status_id'] ?? $this->status_id;
|
||||
$this->documentcpf = $data['documentcpf'] ?? $this->documentcpf;
|
||||
$this->documentcrmv = $data['documentcrmv'] ?? $this->documentcrmv;
|
||||
$this->email = $data['email'] ?? $this->email;
|
||||
$this->senha = $data['senha'] ?? $this->senha;
|
||||
$this->telefone = $data['telefone'] ?? $this->telefone;
|
||||
$this->data_nascimento = $data['data_nascimento'] ?? $this->data_nascimento;
|
||||
$this->endereco_rua = $data['endereco_rua'] ?? $this->endereco_rua;
|
||||
$this->endereco_numero = $data['endereco_numero'] ?? $this->endereco_numero;
|
||||
$this->endereco_complemento = $data['endereco_complemento'] ?? $this->endereco_complemento;
|
||||
$this->endereco_bairro = $data['endereco_bairro'] ?? $this->endereco_bairro;
|
||||
$this->endereco_cidade = $data['endereco_cidade'] ?? $this->endereco_cidade;
|
||||
$this->endereco_uf = $data['endereco_uf'] ?? $this->endereco_uf;
|
||||
$this->endereco_cep = $data['endereco_cep'] ?? $this->endereco_cep;
|
||||
$this->created_at = $data['created_at'] ?? $this->created_at;
|
||||
$this->updated_at = $data['updated_at'] ?? $this->updated_at;
|
||||
$this->deleted_at = $data['deleted_at'] ?? $this->deleted_at;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converte o DTO para um array associativo.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(): array {
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'uuid' => $this->uuid,
|
||||
'nome_completo' => $this->nome_completo,
|
||||
'status_id' => $this->status_id,
|
||||
'documentcpf' => $this->documentcpf,
|
||||
'documentcrmv' => $this->documentcrmv,
|
||||
'email' => $this->email,
|
||||
'senha' => $this->senha,
|
||||
'telefone' => $this->telefone,
|
||||
'data_nascimento' => $this->data_nascimento,
|
||||
'endereco_rua' => $this->endereco_rua,
|
||||
'endereco_numero' => $this->endereco_numero,
|
||||
'endereco_complemento' => $this->endereco_complemento,
|
||||
'endereco_bairro' => $this->endereco_bairro,
|
||||
'endereco_cidade' => $this->endereco_cidade,
|
||||
'endereco_uf' => $this->endereco_uf,
|
||||
'endereco_cep' => $this->endereco_cep,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
'deleted_at' => $this->deleted_at,
|
||||
];
|
||||
}
|
||||
|
||||
public function getId(): ?int {
|
||||
return $this->id;
|
||||
}
|
||||
public function setId(?int $id): void {
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getUuid(): ?string {
|
||||
return $this->uuid;
|
||||
}
|
||||
public function setUuid(?string $uuid): void {
|
||||
$this->uuid = $uuid;
|
||||
}
|
||||
|
||||
public function getNomeCompleto(): ?string {
|
||||
return $this->nome_completo;
|
||||
}
|
||||
public function setNomeCompleto(?string $nome_completo): void {
|
||||
$this->nome_completo = $nome_completo;
|
||||
}
|
||||
|
||||
public function getStatusId(): ?int {
|
||||
return $this->status_id;
|
||||
}
|
||||
public function setStatusId(?int $status_id): void {
|
||||
$this->status_id = $status_id;
|
||||
}
|
||||
|
||||
public function getDocumentCpf(): ?string {
|
||||
return $this->documentcpf;
|
||||
}
|
||||
public function setDocumentCpf(?string $documentcpf): void {
|
||||
$this->documentcpf = $documentcpf;
|
||||
}
|
||||
|
||||
public function getDocumentCrmv(): ?string {
|
||||
return $this->documentcrmv;
|
||||
}
|
||||
public function setDocumentCrmv(?string $documentcrmv): void {
|
||||
$this->documentcrmv = $documentcrmv;
|
||||
}
|
||||
|
||||
public function getEmail(): ?string {
|
||||
return $this->email;
|
||||
}
|
||||
public function setEmail(?string $email): void {
|
||||
$this->email = $email;
|
||||
}
|
||||
|
||||
public function getSenha(): ?string {
|
||||
return $this->senha;
|
||||
}
|
||||
public function setSenha(?string $senha): void {
|
||||
$this->senha = $senha;
|
||||
}
|
||||
|
||||
public function getTelefone(): ?string {
|
||||
return $this->telefone;
|
||||
}
|
||||
public function setTelefone(?string $telefone): void {
|
||||
$this->telefone = $telefone;
|
||||
}
|
||||
|
||||
public function getDataNascimento(): ?string {
|
||||
return $this->data_nascimento;
|
||||
}
|
||||
public function setDataNascimento(?string $data_nascimento): void {
|
||||
$this->data_nascimento = $data_nascimento;
|
||||
}
|
||||
|
||||
public function getEnderecoRua(): ?string {
|
||||
return $this->endereco_rua;
|
||||
}
|
||||
public function setEnderecoRua(?string $endereco_rua): void {
|
||||
$this->endereco_rua = $endereco_rua;
|
||||
}
|
||||
|
||||
public function getEnderecoNumero(): ?string {
|
||||
return $this->endereco_numero;
|
||||
}
|
||||
public function setEnderecoNumero(?string $endereco_numero): void {
|
||||
$this->endereco_numero = $endereco_numero;
|
||||
}
|
||||
|
||||
public function getEnderecoComplemento(): ?string {
|
||||
return $this->endereco_complemento;
|
||||
}
|
||||
public function setEnderecoComplemento(?string $endereco_complemento): void {
|
||||
$this->endereco_complemento = $endereco_complemento;
|
||||
}
|
||||
|
||||
public function getEnderecoBairro(): ?string {
|
||||
return $this->endereco_bairro;
|
||||
}
|
||||
public function setEnderecoBairro(?string $endereco_bairro): void {
|
||||
$this->endereco_bairro = $endereco_bairro;
|
||||
}
|
||||
|
||||
public function getEnderecoCidade(): ?string {
|
||||
return $this->endereco_cidade;
|
||||
}
|
||||
public function setEnderecoCidade(?string $endereco_cidade): void {
|
||||
$this->endereco_cidade = $endereco_cidade;
|
||||
}
|
||||
|
||||
public function getEnderecoUf(): ?string {
|
||||
return $this->endereco_uf;
|
||||
}
|
||||
public function setEnderecoUf(?string $endereco_uf): void {
|
||||
$this->endereco_uf = $endereco_uf;
|
||||
}
|
||||
|
||||
public function getEnderecoCep(): ?string {
|
||||
return $this->endereco_cep;
|
||||
}
|
||||
public function setEnderecoCep(?string $endereco_cep): void {
|
||||
$this->endereco_cep = $endereco_cep;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?string {
|
||||
return $this->created_at;
|
||||
}
|
||||
public function setCreatedAt(?string $created_at): void {
|
||||
$this->created_at = $created_at;
|
||||
}
|
||||
|
||||
public function getUpdatedAt(): ?string {
|
||||
return $this->updated_at;
|
||||
}
|
||||
public function setUpdatedAt(?string $updated_at): void {
|
||||
$this->updated_at = $updated_at;
|
||||
}
|
||||
|
||||
public function getDeletedAt(): ?string {
|
||||
return $this->deleted_at;
|
||||
}
|
||||
public function setDeletedAt(?string $deleted_at): void {
|
||||
$this->deleted_at = $deleted_at;
|
||||
}
|
||||
}
|
||||
52
app/Module/Auth/v0/Middlewares/AuthMiddleware.php
Executable file
52
app/Module/Auth/v0/Middlewares/AuthMiddleware.php
Executable file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
namespace Zampet\Module\Auth\v0\Middlewares;
|
||||
|
||||
use Exception;
|
||||
use Zampet\Services\JWTService;
|
||||
use Zampet\Module\Auth\v0\Services\AuthService;
|
||||
|
||||
class AuthMiddleware {
|
||||
/**
|
||||
* Gerencia o processo de autenticação de uma requisição de API via token Bearer.
|
||||
*
|
||||
* Este método estático atua como um **middleware de autenticação**, verificando a presença e a validade de um token JWT em uma requisição HTTP.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Obtenção do Token:** Tenta extrair o token Bearer do cabeçalho da requisição usando `JWTService::getBearerToken()`.
|
||||
* 2. **Verificação de Presença:** Se o token não for encontrado, uma `Exception` é lançada com o código `401 Unauthorized`.
|
||||
* 3. **Validação do Token:** Chama `AuthService::validateToken()` para verificar a validade do token (assinatura, expiração e status no banco de dados).
|
||||
* 4. **Conclusão:**
|
||||
* * Se a validação for bem-sucedida, o método retorna `true`, permitindo que o processamento da rota continue.
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` lançada durante o processo (token ausente, falha de validação JWT, token expirado) é capturada.
|
||||
* - O método retorna um array de erro formatado, contendo o código HTTP e a mensagem de erro da exceção, o que é útil para a camada de controle enviar uma resposta JSON padronizada ao cliente.
|
||||
*
|
||||
* @return array|bool Retorna `true` se o token for válido; caso contrário, retorna um array associativo com o status da operação, o código de resposta HTTP e uma mensagem de erro.
|
||||
*/
|
||||
public static function handle(): array|bool {
|
||||
// Recebe o token JWT do cabeçalho Authorization
|
||||
$bearerToken = JWTService::getBearerToken();
|
||||
|
||||
try {
|
||||
// Verifica se o token está presente
|
||||
if (!$bearerToken) {
|
||||
throw new Exception(message: "Token not provided", code: 401);
|
||||
}
|
||||
|
||||
// Verifica se o token é válido
|
||||
$validateTokenService = (new AuthService())->validateToken(token: $bearerToken);
|
||||
if ($validateTokenService['status'] !== 'success') {
|
||||
throw new Exception(message: $validateTokenService['message'], code: 401);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch(Exception $e) {
|
||||
return [
|
||||
'response_code' => $e->getCode(),
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
396
app/Module/Auth/v0/Models/UsuarioModel.php
Executable file
396
app/Module/Auth/v0/Models/UsuarioModel.php
Executable file
@@ -0,0 +1,396 @@
|
||||
<?php
|
||||
namespace Zampet\Module\Auth\v0\Models;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use Zampet\Services\DB;
|
||||
use Zampet\Module\Auth\v0\DTO\UsuarioDTO;
|
||||
|
||||
class UsuarioModel {
|
||||
protected string $usuarioTable = 'usuario';
|
||||
protected string $usuarioTokenTable = 'usuario_token';
|
||||
|
||||
/**
|
||||
* Cria e armazena um novo usuário no banco de dados.
|
||||
*
|
||||
* Este método é responsável por inserir os dados completos de um novo usuário, incluindo informações pessoais, documentos, credenciais e endereço, na tabela `{$this->usuarioTable}`. A operação é atômica e utiliza uma transação de banco de dados para garantir a consistência dos dados.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Início da Transação:** Inicia uma transação no banco de dados (`DB::beginTransaction()`).
|
||||
* 2. **Execução da Query:** Prepara e executa a instrução `INSERT` com todos os campos preenchidos a partir do `UsuarioDTO`.
|
||||
* 3. **Confirmação:** Se a inserção for bem-sucedida, a transação é confirmada (`DB::commit()`).
|
||||
* 4. **Retorno de Sucesso:** Retorna um array com `status` 'success', o código `201 Created`, uma mensagem de êxito e os dados do usuário criado (ID e UUID).
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` capturada durante o processo faz com que a transação seja revertida (`DB::rollBack()`).
|
||||
* - Retorna um array com `status` 'error', código `500 Internal Server Error` e uma mensagem genérica de erro.
|
||||
*
|
||||
* @param UsuarioDTO $usuarioDTO O objeto DTO contendo os dados do usuário a ser criado.
|
||||
* @return array Um array associativo com o status da operação, um código de resposta HTTP e uma mensagem.
|
||||
*/
|
||||
public function store(UsuarioDTO $usuarioDTO): array {
|
||||
try {
|
||||
$sql =
|
||||
"INSERT INTO {$this->usuarioTable} (
|
||||
uuid,
|
||||
nome_completo,
|
||||
status_id,
|
||||
documentcpf,
|
||||
documentcrmv,
|
||||
email,
|
||||
senha,
|
||||
telefone,
|
||||
data_nascimento,
|
||||
endereco_rua,
|
||||
endereco_numero,
|
||||
endereco_complemento,
|
||||
endereco_bairro,
|
||||
endereco_cidade,
|
||||
endereco_uf,
|
||||
endereco_cep,
|
||||
created_at,
|
||||
updated_at,
|
||||
deleted_at
|
||||
) VALUES (
|
||||
:uuid,
|
||||
:nome_completo,
|
||||
:status_id,
|
||||
:documentcpf,
|
||||
:documentcrmv,
|
||||
:email,
|
||||
:senha,
|
||||
:telefone,
|
||||
:data_nascimento,
|
||||
:endereco_rua,
|
||||
:endereco_numero,
|
||||
:endereco_complemento,
|
||||
:endereco_bairro,
|
||||
:endereco_cidade,
|
||||
:endereco_uf,
|
||||
:endereco_cep,
|
||||
:created_at,
|
||||
:updated_at,
|
||||
:deleted_at
|
||||
)";
|
||||
|
||||
$params = [
|
||||
":uuid" => $usuarioDTO->getUuid(),
|
||||
":nome_completo" => $usuarioDTO->getNomeCompleto(),
|
||||
":status_id" => $usuarioDTO->getStatusId(),
|
||||
":documentcpf" => $usuarioDTO->getDocumentCpf(),
|
||||
":documentcrmv" => $usuarioDTO->getDocumentCrmv(),
|
||||
":email" => $usuarioDTO->getEmail(),
|
||||
":senha" => $usuarioDTO->getSenha(),
|
||||
":telefone" => $usuarioDTO->getTelefone(),
|
||||
":data_nascimento" => $usuarioDTO->getDataNascimento(),
|
||||
":endereco_rua" => $usuarioDTO->getEnderecoRua(),
|
||||
":endereco_numero" => $usuarioDTO->getEnderecoNumero(),
|
||||
":endereco_complemento" => $usuarioDTO->getEnderecoComplemento(),
|
||||
":endereco_bairro" => $usuarioDTO->getEnderecoBairro(),
|
||||
":endereco_cidade" => $usuarioDTO->getEnderecoCidade(),
|
||||
":endereco_uf" => $usuarioDTO->getEnderecoUf(),
|
||||
":endereco_cep" => $usuarioDTO->getEnderecoCep(),
|
||||
":created_at" => $usuarioDTO->getCreatedAt(),
|
||||
":updated_at" => $usuarioDTO->getUpdatedAt(),
|
||||
":deleted_at" => $usuarioDTO->getDeletedAt()
|
||||
];
|
||||
|
||||
// Executa a query de inserção
|
||||
DB::execute(sql: $sql, params: $params);
|
||||
|
||||
return [
|
||||
'response_code' => 201,
|
||||
'status' => 'success',
|
||||
'message' => 'Usuário registrado com sucesso.',
|
||||
'output' => [
|
||||
'data' => [
|
||||
'user_id' => DB::lastInsertId(),
|
||||
'uuid' => $usuarioDTO->getUuid()
|
||||
]
|
||||
]
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
return [
|
||||
'response_code' => 500,
|
||||
'status' => 'error',
|
||||
'message' => 'Ocorreu um erro ao registrar o usuário. Tente novamente mais tarde.',
|
||||
'output' => ['errors' => $e->getMessage()]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Busca um único usuário ativo no banco de dados por um identificador específico.
|
||||
*
|
||||
* Este método recupera todos os detalhes de um usuário na tabela `{$this->usuarioTable}`. A busca é flexível e permite usar qualquer coluna como identificador (`$identifier`), mas é estritamente restrita a registros que não foram logicamente excluídos (`deleted_at IS NULL`).
|
||||
*
|
||||
* @param string $identifier A coluna da tabela a ser usada como critério de busca (ex: 'uuid', 'email', 'documentcpf').
|
||||
* @param mixed $value O valor correspondente ao identificador que está sendo buscado.
|
||||
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e os dados do usuário, se encontrado.
|
||||
*/
|
||||
public function getUsuarioByIdentifier(string $identifier, mixed $value): array {
|
||||
try {
|
||||
$sql =
|
||||
"SELECT
|
||||
id,
|
||||
uuid,
|
||||
nome_completo,
|
||||
status_id,
|
||||
documentcpf,
|
||||
documentcrmv,
|
||||
email,
|
||||
senha,
|
||||
telefone,
|
||||
data_nascimento,
|
||||
endereco_rua,
|
||||
endereco_numero,
|
||||
endereco_complemento,
|
||||
endereco_bairro,
|
||||
endereco_cidade,
|
||||
endereco_uf,
|
||||
endereco_cep,
|
||||
created_at,
|
||||
updated_at,
|
||||
deleted_at
|
||||
FROM {$this->usuarioTable}
|
||||
WHERE {$identifier} = :value
|
||||
AND deleted_at IS NULL";
|
||||
|
||||
$params = [
|
||||
':value' => $value
|
||||
];
|
||||
|
||||
$result = DB::fetchOne(sql: $sql, params: $params);
|
||||
|
||||
if (!$result) {
|
||||
return [
|
||||
'response_code' => 404,
|
||||
'status' => 'fail',
|
||||
'message' => 'Usuário não encontrado.',
|
||||
'output' => [
|
||||
'data' => []
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'response_code' => 200,
|
||||
'status' => 'success',
|
||||
'output' => ['data' => $result]
|
||||
];
|
||||
} catch(Exception $e) {
|
||||
return [
|
||||
'response_code' => 500,
|
||||
'status' => 'error',
|
||||
'message' => 'Ocorreu um erro ao buscar usuário. Tente novamente mais tarde.',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Armazena um novo token de autenticação no banco de dados para um usuário específico.
|
||||
*
|
||||
* Este método é responsável por registrar um token na tabela `{$this->usuarioTokenTable}`. Ele associa o token a um ID de usuário, define o status inicial como não revogado (`revoked = 0`) e registra o timestamp de criação.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Início da Transação:** O método inicia uma transação no banco de dados (`DB::beginTransaction()`).
|
||||
* 2. **Execução da Query:** Prepara e executa a instrução `INSERT` com o ID do usuário, a string do token, o status inicial de revogação e o timestamp atual.
|
||||
* 3. **Confirmação:** Se a inserção for bem-sucedida, a transação é confirmada (`DB::commit()`).
|
||||
* 4. **Retorno de Sucesso:** Retorna um array com `status` 'success', o código `201 Created` e uma mensagem de êxito.
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` capturada durante o processo faz com que a transação seja revertida (`DB::rollback()`).
|
||||
* - Retorna um array com `status` 'error', o código `500 Internal Server Error` e uma mensagem genérica de erro.
|
||||
*
|
||||
* @param int $user_id O ID do usuário ao qual o token pertence.
|
||||
* @param string $token A string do token de autenticação (JWT ou similar).
|
||||
* @return array Um array associativo contendo o status da operação ("success" ou "error"), um código de resposta HTTP e uma mensagem.
|
||||
*/
|
||||
public function storeToken(int $user_id, string $token): array {
|
||||
try {
|
||||
$sql =
|
||||
"INSERT INTO {$this->usuarioTokenTable} (
|
||||
usuario_id,
|
||||
token,
|
||||
revoked,
|
||||
created_at
|
||||
) VALUES (
|
||||
:usuario_id,
|
||||
:token,
|
||||
:revoked,
|
||||
:created_at
|
||||
)";
|
||||
|
||||
$params = [
|
||||
":usuario_id" => $user_id,
|
||||
":token" => $token,
|
||||
":revoked" => 0,
|
||||
":created_at" => (new DateTime())->format(format: 'Y-m-d H:i:s')
|
||||
];
|
||||
|
||||
DB::execute(sql: $sql, params: $params);
|
||||
|
||||
return [
|
||||
'response_code' => 201,
|
||||
'status' => 'success',
|
||||
'message' => 'Token registrado com sucesso.'
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
return [
|
||||
'response_code' => 500,
|
||||
'status' => 'error',
|
||||
'message' => 'Erro ao registrar token. Tente novamente mais tarde.'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoga todos os tokens de autenticação ativos de um usuário específico.
|
||||
*
|
||||
* Este método atualiza a coluna `revoked` para `1` (revogado) para todos os tokens não revogados (`revoked = 0`) associados a um `user_id` específico na tabela `{$this->usuarioTokenTable}`. Isso invalida todas as sessões ativas do usuário, forçando-o a fazer login novamente.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Execução da Query:** Prepara e executa a instrução `UPDATE` para revogar os tokens.
|
||||
* 2. **Confirmação:** Se a atualização for bem-sucedida, retorna um array com `status` 'success' e código `200 OK`.
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` capturada durante o processo reverte qualquer transação de banco de dados e retorna um array com `status` 'error' e código `500 Internal Server Error`, com uma mensagem de erro genérica.
|
||||
*
|
||||
* @param int $user_id O ID do usuário para o qual os tokens serão revogados.
|
||||
* @return array Um array associativo contendo o status da operação ("success" ou "error"), um código de resposta HTTP e uma mensagem descritiva.
|
||||
*/
|
||||
public function revokeOldTokens(int $user_id): array {
|
||||
try {
|
||||
$sql =
|
||||
"UPDATE {$this->usuarioTokenTable} SET
|
||||
revoked = 1,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE usuario_id = :usuario_id
|
||||
AND revoked = 0";
|
||||
|
||||
$params = [
|
||||
":usuario_id" => $user_id
|
||||
];
|
||||
|
||||
DB::execute(sql: $sql, params: $params);
|
||||
|
||||
return [
|
||||
'response_code' => 200,
|
||||
'status' => 'success',
|
||||
'message' => 'Old tokens revoked successfully.'
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
return [
|
||||
'response_code' => 500,
|
||||
'status' => 'error',
|
||||
'message' => 'Error revoking old tokens. Please try again later.'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Busca um token de usuário no banco de dados por um identificador específico.
|
||||
*
|
||||
* Este método recupera os dados de um único token de autenticação da tabela `{$this->usuarioTokenTable}` de forma flexível, permitindo a busca por qualquer coluna (`$identifier`) e o valor correspondente.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Execução da Query**: Monta e executa uma consulta `SELECT` que usa o `$identifier` de forma dinâmica na cláusula `WHERE` e um parâmetro nomeado (`:value`) para o valor.
|
||||
* 2. **Verificação de Resultados**:
|
||||
* - Se o token for encontrado, retorna um array com **`status` 'success'**, código **200 OK** e os dados do token.
|
||||
* - Se o token **não for encontrado**, retorna um array com **`status` 'fail'** e código **404 Not Found**.
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` capturada durante a consulta retorna um array com **`status` 'error'** e código **500 Internal Server Error**, com uma mensagem genérica de erro.
|
||||
*
|
||||
* @param string $identifier A coluna da tabela a ser usada como critério de busca (ex: 'id', 'token').
|
||||
* @param mixed $value O valor correspondente ao identificador que está sendo buscado.
|
||||
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e os dados do token, se encontrado.
|
||||
*/
|
||||
public function getTokenByidentifier(string $identifier, mixed $value): array {
|
||||
try {
|
||||
$sql =
|
||||
"SELECT
|
||||
id,
|
||||
usuario_id,
|
||||
token,
|
||||
revoked,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM {$this->usuarioTokenTable}
|
||||
WHERE {$identifier} = :value";
|
||||
|
||||
$params = [
|
||||
':value' => $value
|
||||
];
|
||||
|
||||
$result = DB::fetchOne(sql: $sql, params: $params);
|
||||
|
||||
if (!$result) {
|
||||
return [
|
||||
'response_code' => 404,
|
||||
'status' => 'fail',
|
||||
'message' => 'Token não encontrado.',
|
||||
'output' => [
|
||||
'data' => []
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'response_code' => 200,
|
||||
'status' => 'success',
|
||||
'output' => ['data' => $result]
|
||||
];
|
||||
} catch(Exception $e) {
|
||||
return [
|
||||
'response_code' => 500,
|
||||
'status' => 'error',
|
||||
'message' => 'Ocorreu um erro ao buscar token. Tente novamente mais tarde.',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoga um token de autenticação de usuário no banco de dados.
|
||||
*
|
||||
* Este método marca um token específico como revogado, atualizando a coluna `revoked` para `1`. Ele garante que apenas tokens que ainda não foram revogados (`revoked = 0`) sejam atualizados.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Execução da Query**: Prepara e executa a instrução `UPDATE` para definir `revoked = 1` para o token correspondente.
|
||||
* 2. **Confirmação**: Se a atualização for bem-sucedida, retorna um array com **`status` 'success'** e código **200 OK**.
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'** e código **500 Internal Server Error**, com uma mensagem de erro genérica.
|
||||
*
|
||||
* @param string $token O token de autenticação a ser revogado.
|
||||
* @return array Um array associativo contendo o status da operação ("success" ou "error"), um código de resposta HTTP e uma mensagem.
|
||||
*/
|
||||
public function revokeToken(string $token): array {
|
||||
try {
|
||||
$sql =
|
||||
"UPDATE {$this->usuarioTokenTable} SET
|
||||
revoked = 1,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE token = :token
|
||||
AND revoked = 0";
|
||||
|
||||
$params = [
|
||||
":token" => $token
|
||||
];
|
||||
|
||||
DB::execute(sql: $sql, params: $params);
|
||||
|
||||
return [
|
||||
'response_code' => 200,
|
||||
'status' => 'success',
|
||||
'message' => 'Token revoked successfully.'
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
return [
|
||||
'response_code' => 500,
|
||||
'status' => 'error',
|
||||
'message' => 'Error revoking token. Please try again later.'
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
30
app/Module/Auth/v0/Routes/AuthRoutes.php
Executable file
30
app/Module/Auth/v0/Routes/AuthRoutes.php
Executable file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
use AxiumPHP\Core\Router;
|
||||
use Zampet\Module\Auth\v0\Controllers\AuthController;
|
||||
use Zampet\Module\Auth\v0\Middlewares\AuthMiddleware;
|
||||
|
||||
Router::group(
|
||||
prefix: '/auth',
|
||||
callback: function () {
|
||||
// Criar novo usuário
|
||||
Router::POST(
|
||||
uri: '/register',
|
||||
handler: [AuthController::class, 'register']
|
||||
);
|
||||
|
||||
// Login no Sistema
|
||||
Router::POST(
|
||||
uri: '/login',
|
||||
handler: [AuthController::class, 'login']
|
||||
);
|
||||
|
||||
// Logout do Sistema
|
||||
Router::POST(
|
||||
uri: '/logout',
|
||||
handler: [AuthController::class, 'logout'],
|
||||
middlewares: [
|
||||
AuthMiddleware::class.'::handle'
|
||||
]
|
||||
);
|
||||
}
|
||||
);
|
||||
12
app/Module/Auth/v0/Routes/Routes.php
Executable file
12
app/Module/Auth/v0/Routes/Routes.php
Executable file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
use AxiumPHP\Core\Router;
|
||||
|
||||
Router::group(
|
||||
prefix: '/v0',
|
||||
callback: function() {
|
||||
require 'AuthRoutes.php';
|
||||
},
|
||||
middlewares: [
|
||||
|
||||
]
|
||||
);
|
||||
346
app/Module/Auth/v0/Services/AuthService.php
Executable file
346
app/Module/Auth/v0/Services/AuthService.php
Executable file
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
namespace Zampet\Module\Auth\v0\Services;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Zampet\Services\DB;
|
||||
use Zampet\Services\JWTService;
|
||||
use Zampet\Helpers\DataValidator;
|
||||
use Zampet\Module\Auth\v0\DTO\UsuarioDTO;
|
||||
use Zampet\Module\Auth\v0\Models\UsuarioModel;
|
||||
|
||||
class AuthService {
|
||||
/**
|
||||
* Registra um novo usuário no sistema após realizar validações rigorosas.
|
||||
*
|
||||
* Este método orquestra o processo de criação de um novo usuário, incluindo validação de dados, checagem de duplicidade de e-mail e persistência no banco de dados. A operação é atômica e utiliza uma transação de banco de dados (`DB::beginTransaction()`).
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Início da Transação:** Inicia uma transação no banco de dados.
|
||||
* 2. **Validação de Dados:** Um `DataValidator` é usado para validar campos obrigatórios (`nome_completo`, `email`, `senha`) e a complexidade da senha (mínimo de 8 caracteres, maiúsculas, números e símbolos).
|
||||
* 3. **Checagem de Duplicidade:** O método `getUsuarioByIdentifier` é chamado para verificar se o e-mail já está em uso por outro usuário.
|
||||
* 4. **Encerramento em Caso de Falha na Validação/Duplicidade:** Se houver erros de validação (código **400**) ou se o e-mail já estiver em uso (código **409**), a transação é revertida (`DB::rollBack()`) e o erro é retornado.
|
||||
* 5. **Preparação Final dos Dados:** Se a validação passar, o **UUID** do usuário e o timestamp de criação são definidos. A senha é criptografada usando **`PASSWORD_ARGON2ID`** antes de ser persistida.
|
||||
* 6. **Persistência:** O método `store` do `UsuarioModel` é chamado para inserir o novo registro.
|
||||
* 7. **Confirmação:** Se a persistência for bem-sucedida (código **201**), a transação é confirmada (`DB::commit()`) e o resultado de sucesso é retornado.
|
||||
* 8. **Tratamento de Erro de Persistência:** Se o `store` falhar por um motivo inesperado (código **500**), a transação é revertida.
|
||||
*
|
||||
* @param UsuarioDTO $usuarioDTO O objeto DTO contendo os dados do novo usuário, incluindo nome, email e senha.
|
||||
* @return array Um array associativo contendo o código HTTP, status da operação, mensagem e dados de saída.
|
||||
*/
|
||||
public function register(UsuarioDTO $usuarioDTO): array {
|
||||
// Inicia conexão com o banco de dados
|
||||
DB::beginTransaction();
|
||||
|
||||
// Valida os dados do DTO
|
||||
$dataValidador = new DataValidator(inputs: $usuarioDTO->toArray());
|
||||
$dataValidador->validate(field: 'nome_completo', rule: 'notEmpty', message: 'Nome completo é obrigatório');
|
||||
$dataValidador->validate(field: 'email', rule: 'notEmpty', message: 'Email é obrigatório');
|
||||
$dataValidador->validate(field: 'email', rule: 'validateEmail', message: 'Email inválido');
|
||||
$dataValidador->validate(field: 'senha', rule: 'notEmpty', message: 'Senha é obrigatória');
|
||||
$dataValidador->validate(field: 'documentcpf', rule: 'notEmpty', message: 'CPF é obrigatório');
|
||||
$dataValidador->validate(field: 'documentcpf', rule: 'validateCpf', message: 'CPF informado é inválido.');
|
||||
$dataValidador->validate(field: 'senha', rule: 'validatePassword', message: 'Senha inválida', additionalParams: [
|
||||
[
|
||||
'minLength' => 8,
|
||||
'upper' => true,
|
||||
'digit' => true,
|
||||
'symbol' => true
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
// Se houver erros de validação, retornar resposta de erro
|
||||
if(!$dataValidador->passes()) {
|
||||
DB::rollBack();
|
||||
return [
|
||||
'response_code' => 400,
|
||||
'status' => 'error',
|
||||
'message' => 'Validation errors',
|
||||
'output' => [
|
||||
'errors' => $dataValidador->getErrors()
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
// Verifica se o email já está em uso
|
||||
$existingEmailUser = (new UsuarioModel())->getUsuarioByIdentifier(identifier: 'email', value: $usuarioDTO->getEmail());
|
||||
if($existingEmailUser['status'] === 'success' && !empty($existingEmailUser['output']['data'])) {
|
||||
DB::rollBack();
|
||||
return [
|
||||
'response_code' => 409,
|
||||
'status' => 'error',
|
||||
'message' => 'E-mail já está em uso por outro usuário.',
|
||||
'output' => []
|
||||
];
|
||||
}
|
||||
|
||||
// Verifica se o CPF já está em uso
|
||||
$existingCpfUser = (new UsuarioModel())->getUsuarioByIdentifier(identifier: 'documentcpf', value: $usuarioDTO->getDocumentCpf());
|
||||
if($existingCpfUser['status'] === 'success' && !empty($existingCpfUser['output']['data'])) {
|
||||
DB::rollBack();
|
||||
return [
|
||||
'response_code' => 409,
|
||||
'status' => 'error',
|
||||
'message' => 'CPF já está em uso por outro usuário.',
|
||||
'output' => []
|
||||
];
|
||||
}
|
||||
|
||||
// Define dados adicionais
|
||||
$usuarioDTO->setUuid(uuid: Uuid::uuid7()->toString());
|
||||
$usuarioDTO->setCreatedAt(created_at: (new DateTime())->format(format: 'Y-m-d H:i:s'));
|
||||
$usuarioDTO->setSenha(senha: password_hash(password: $usuarioDTO->getSenha(), algo: PASSWORD_ARGON2ID));
|
||||
|
||||
// Persiste o novo usuário no banco de dados
|
||||
$storeUsuario = (new UsuarioModel())->store(usuarioDTO: $usuarioDTO);
|
||||
if($storeUsuario['status'] !== 'success') {
|
||||
DB::rollBack();
|
||||
return [
|
||||
'response_code' => 500,
|
||||
'status' => 'error',
|
||||
'message' => 'Erro ao registrar usuário. Tente novamente mais tarde.',
|
||||
'output' => $storeUsuario['output'] ?? []
|
||||
];
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
return [
|
||||
'response_code' => $storeUsuario['response_code'],
|
||||
'status' => $storeUsuario['status'],
|
||||
'message' => $storeUsuario['message'],
|
||||
'output' => $storeUsuario['output']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Realiza a autenticação de um usuário por e-mail e senha, gerando e registrando um token JWT.
|
||||
*
|
||||
* Este método gerencia todo o fluxo de login, incluindo a verificação de credenciais, revogação de tokens antigos, geração de um novo JWT e persistência desse novo token no banco de dados. A operação é atômica e utiliza uma transação de banco de dados para garantir a consistência dos dados.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Início da Transação:** Inicia uma transação no banco de dados (`DB::beginTransaction()`).
|
||||
* 2. **Consulta do Usuário:** Busca o usuário pelo e-mail. Se o usuário não for encontrado, uma `Exception` é lançada com o código `401 Unauthorized`.
|
||||
* 3. **Validação da Senha:** Verifica a senha fornecida contra o hash armazenado. Se a senha for inválida, uma `Exception` é lançada com o código `401 Unauthorized`.
|
||||
* 4. **Revogação de Tokens Antigos:** Chama o método `revokeOldTokens` do `UsuarioModel` para invalidar todas as sessões anteriores do usuário.
|
||||
* 5. **Geração do JWT:** Monta o payload do JWT com os dados essenciais do usuário e os timestamps `iat` (emitido em) e `exp` (expira em), gerando o novo token.
|
||||
* 6. **Registro do Token:** Armazena o novo token no banco de dados usando `storeToken`. Se a operação falhar, uma `Exception` é lançada com o código `500 Internal Server Error`.
|
||||
* 7. **Confirmação e Retorno:** Se todas as etapas forem bem-sucedidas, a transação é confirmada (`DB::commit()`), e o token gerado é retornado com `status` 'success' e código `200 OK`.
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` capturada durante o processo faz com que a transação seja revertida (`DB::rollBack()`).
|
||||
* - Retorna um array com `status` 'error', o código de erro da exceção (ou 500 como fallback) e uma mensagem descritiva.
|
||||
*
|
||||
* @param string $email O e-mail do usuário para autenticação.
|
||||
* @param string $password A senha em texto plano fornecida pelo usuário.
|
||||
* @return array Um array associativo contendo o status da operação, o código de resposta HTTP e o token JWT em caso de sucesso.
|
||||
*/
|
||||
public function login(string $email, string $password): array {
|
||||
try {
|
||||
// Inicia Transação no Banco de dados
|
||||
DB::beginTransaction();
|
||||
|
||||
// Consulta as informações do usuário pelo email
|
||||
$userResult = (new UsuarioModel())->getUsuarioByIdentifier(identifier: 'email', value: $email);
|
||||
if($userResult['status'] !== 'success' || empty($userResult['output']['data'])) {
|
||||
throw new Exception(message: 'E-mail ou Senha incorretos.', code: 401);
|
||||
}
|
||||
|
||||
// Valida a senha fornecida com a armazenada
|
||||
if(!password_verify(password: $password, hash: $userResult['output']['data']['senha'])) {
|
||||
throw new Exception(message: 'E-mail ou Senha incorretos.', code: 401);
|
||||
}
|
||||
|
||||
// Revoga os tokens antigos do usuário
|
||||
$revokeOldTokens = (new UsuarioModel())->revokeOldTokens(user_id: $userResult['output']['data']['id']);
|
||||
if($revokeOldTokens['status'] !== 'success') {
|
||||
throw new Exception(message: 'Erro ao revogar tokens antigos.', code: 500);
|
||||
}
|
||||
|
||||
// Prepara o Payload do JWT
|
||||
$jwtPayload = [
|
||||
'user_uuid' => $userResult['output']['data']['uuid'],
|
||||
'email' => $userResult['output']['data']['email'],
|
||||
'nome_completo' => $userResult['output']['data']['nome_completo'],
|
||||
'iat' => (new DateTime())->getTimestamp(),
|
||||
'exp' => (new DateTime())->modify(modifier: '+8 hour')->getTimestamp()
|
||||
];
|
||||
|
||||
$token = JWTService::generateToken(payload: $jwtPayload);
|
||||
|
||||
// Armazena o token no banco de dados
|
||||
$storeToken = (new UsuarioModel())->storeToken(user_id: $userResult['output']['data']['id'], token: $token);
|
||||
if($storeToken['status'] !== 'success') {
|
||||
throw new Exception(message: 'Erro ao armazenar token de autenticação.', code: 500);
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
return [
|
||||
'response_code' => 200,
|
||||
'status' => 'success',
|
||||
'message' => 'Login successful.',
|
||||
'output' => [
|
||||
'data' => [
|
||||
'token' => $token
|
||||
]
|
||||
]
|
||||
];
|
||||
} catch(Exception $e) {
|
||||
DB::rollBack();
|
||||
return [
|
||||
'response_code' => $e->getCode() ?: 500,
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage() ?: 'An unexpected error occurred during login.'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finaliza a sessão do usuário revogando um token JWT específico.
|
||||
*
|
||||
* Este método é o responsável por orquestrar o processo de logout seguro, garantindo que o token de autenticação seja validado e, em seguida, invalidado no banco de dados.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Validação do Token:** O método chama `validateToken` para verificar se o token existe e ainda é válido (não expirado ou revogado no DB). Se a validação falhar, uma `Exception` é lançada com o código de erro apropriado.
|
||||
* 2. **Revogação no Banco de Dados:** Tenta revogar o token usando `UsuarioModel::revokeToken()`. Se a revogação falhar (ex: erro de banco de dados), uma `Exception` é lançada.
|
||||
* 3. **Retorno de Sucesso:** Se ambas as operações (validação e revogação) forem bem-sucedidas, retorna um array com **`status` 'success'**, código **200 OK** e uma mensagem de êxito.
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` lançada durante o processo é capturada, e o método retorna um array de erro formatado, contendo o código HTTP e a mensagem da exceção.
|
||||
*
|
||||
* @param string $token O token JWT a ser revogado.
|
||||
* @return array Um array associativo contendo o status da operação ("success" ou "error"), o código de resposta HTTP e uma mensagem.
|
||||
*/
|
||||
public function logout(string $token): array {
|
||||
try {
|
||||
// Validar se o token existe e não está revogado
|
||||
$validateToken = $this->validateToken(token: $token);
|
||||
if($validateToken['status'] !== 'success') {
|
||||
throw new Exception(message: $validateToken['message'], code: $validateToken['response_code']);
|
||||
}
|
||||
|
||||
// Revoga o token fornecido
|
||||
$revokeToken = (new UsuarioModel())->revokeToken(token: $token);
|
||||
if($revokeToken['status'] !== 'success') {
|
||||
throw new Exception(message: 'Erro ao revogar token.', code: $revokeToken['response_code']);
|
||||
}
|
||||
|
||||
return [
|
||||
'response_code' => 200,
|
||||
'status' => 'success',
|
||||
'message' => 'Logout successful.',
|
||||
'output' => []
|
||||
];
|
||||
} catch(Exception $e) {
|
||||
return [
|
||||
'response_code' => $e->getCode() ?: 500,
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage() ?: 'An unexpected error occurred during logout.'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Valida um token de autenticação verificando sua presença no banco de dados e seu status de expiração.
|
||||
*
|
||||
* Este método realiza uma validação de duas etapas: 1) Checa se o token existe e não está revogado no banco de dados, e 2) verifica se o timestamp de expiração (exp) no payload do token já passou. Se o token estiver expirado, ele é revogado no banco de dados.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Início da Transação:** Inicia uma transação no banco de dados (`DB::beginTransaction()`) para garantir que a revogação, se necessária, seja atômica.
|
||||
* 2. **Busca no DB:** Tenta encontrar o token usando o `UsuarioModel`. Se a busca falhar (token não encontrado ou revogado), uma `Exception` é lançada com o código `401 Unauthorized`.
|
||||
* 3. **Decodificação:** O token é decodificado usando `JWTService::decode()` para acessar o payload e o timestamp de expiração (`exp`).
|
||||
* 4. **Verificação de Expiração:** Compara o timestamp atual com o `exp` do payload.
|
||||
* * **Se Expirado:** O método tenta revogar o token no DB. Se a revogação for bem-sucedida, a transação é confirmada e o método retorna um erro com código `401`. Se a revogação falhar, a transação é revertida e um erro `500` é retornado.
|
||||
* * **Se Válido:** A transação é confirmada e o método retorna `status` 'success' com o código `200 OK` e os dados do payload.
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` lançada durante o processo (falha de busca, falha de decodificação JWT, falha de revogação) é capturada. Se a falha ocorrer após o início da transação, um `DB::rollBack()` é implícito ou explicitamente chamado antes do retorno.
|
||||
* - Retorna um array com `status` 'error', o código de erro (ou `500` como fallback) e uma mensagem descritiva.
|
||||
*
|
||||
* @param string $token O token JWT a ser validado.
|
||||
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e os dados do token, se válido.
|
||||
*/
|
||||
public function validateToken(string $token): array {
|
||||
// Inicia transação no banco de dados
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$dbTokenData = (new UsuarioModel())->getTokenByidentifier(identifier: 'token', value: $token);
|
||||
if($dbTokenData['status'] !== 'success' || empty($dbTokenData['output']['data'])) {
|
||||
throw new Exception(message: 'Token inválido ou expirado.', code: 401);
|
||||
}
|
||||
|
||||
// Decodifica o token para verificar sua validade
|
||||
$decodedToken = JWTService::decode(token: $dbTokenData['output']['data']['token']);
|
||||
|
||||
// Verifica se o token expirou
|
||||
$currentTimestamp = (new DateTime())->getTimestamp();
|
||||
switch (true) {
|
||||
case $currentTimestamp > $decodedToken['exp']:
|
||||
// Revoga o token expirado
|
||||
$revokeToken = (new UsuarioModel())->revokeToken(token: $dbTokenData['output']['data']['token']);
|
||||
if($revokeToken['status'] !== 'success') {
|
||||
DB::rollBack();
|
||||
throw new Exception(message: 'Erro ao revogar token expirado.', code: 500);
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
return [
|
||||
'response_code' => 401,
|
||||
'status' => 'error',
|
||||
'message' => 'Token expirado.',
|
||||
'output' => []
|
||||
];
|
||||
|
||||
case $dbTokenData['output']['data']['revoked'] === 1:
|
||||
DB::commit();
|
||||
return [
|
||||
'response_code' => 401,
|
||||
'status' => 'error',
|
||||
'message' => 'Token inválido ou expirado.',
|
||||
'output' => []
|
||||
];
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
return [
|
||||
'response_code' => 200,
|
||||
'status' => 'success',
|
||||
'message' => 'Token is valid.',
|
||||
'output' => ['token_data' => $decodedToken]
|
||||
];
|
||||
} catch(Exception $e) {
|
||||
return [
|
||||
'response_code' => $e->getCode() ?: 500,
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage() ?: 'An unexpected error occurred during token validation.'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public function getUserData(string $token): array {
|
||||
try {
|
||||
// Validar se o token existe e não está revogado
|
||||
$validateToken = $this->validateToken(token: $token);
|
||||
if($validateToken['status'] !== 'success') {
|
||||
throw new Exception(message: $validateToken['message'], code: $validateToken['response_code']);
|
||||
}
|
||||
|
||||
return [
|
||||
'response_code' => 200,
|
||||
'status' => 'success',
|
||||
'message' => 'User data retrieved successfully.',
|
||||
'output' => [
|
||||
'data' => $validateToken['output']['token_data']
|
||||
]
|
||||
];
|
||||
} catch(Exception $e) {
|
||||
return [
|
||||
'response_code' => $e->getCode() ?: 500,
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage() ?: 'An unexpected error occurred while retrieving user data.'
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
66
app/Module/Auth/v0/bootstrap.php
Executable file
66
app/Module/Auth/v0/bootstrap.php
Executable file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
namespace Zampet\Module\Auth;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
|
||||
// Caminho do manifest.json
|
||||
$manifestPath = realpath(path: __DIR__ . "/manifest.json");
|
||||
|
||||
if (!$manifestPath || !file_exists(filename: $manifestPath)) {
|
||||
throw new Exception(message: "Arquivo 'manifest.json' não encontrado.");
|
||||
}
|
||||
|
||||
// Lê e decodifica
|
||||
$manifest = json_decode(json: file_get_contents(filename: $manifestPath), associative: true);
|
||||
|
||||
// Verifica estrutura
|
||||
if (!isset($manifest['apis']) || !is_array(value: $manifest['apis'])) {
|
||||
throw new Exception(message: "Estrutura de 'apis' inválida no manifest.");
|
||||
}
|
||||
|
||||
$now = (new DateTime())->format(format: 'Y-m-d');
|
||||
$newApis = [];
|
||||
$updated = false;
|
||||
|
||||
// Checa se tem versão esperando que começa hoje
|
||||
foreach ($manifest['apis'] as $api) {
|
||||
if ($api['status'] === 'planned' && $api['start_date'] === $now) {
|
||||
// Promote a nova
|
||||
$api['status'] = 'active';
|
||||
$newApis[] = $api;
|
||||
$updated = true;
|
||||
} elseif ($api['status'] === 'active' && $api['start_date'] !== $now) {
|
||||
// Ignora versão antiga ativa
|
||||
continue;
|
||||
} else {
|
||||
$newApis[] = $api;
|
||||
}
|
||||
}
|
||||
|
||||
// Atualiza manifest se tiver mudança
|
||||
if ($updated) {
|
||||
$manifest['apis'] = $newApis;
|
||||
file_put_contents(filename: $manifestPath, data: json_encode(value: $manifest, flags: JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
// Carrega a versão ativa
|
||||
$loaded = false;
|
||||
foreach ($manifest['apis'] as $api) {
|
||||
$startDate = new DateTime(datetime: $api['start_date']);
|
||||
if ($api['status'] === 'active' && $startDate <= new DateTime()) {
|
||||
$routeFile = MODULE_PATH . "/{$manifest['dirName']}/{$api['version']}/Routes/Routes.php";
|
||||
if (file_exists(filename: $routeFile)) {
|
||||
require_once realpath(path: $routeFile);
|
||||
$loaded = true;
|
||||
break;
|
||||
} else {
|
||||
throw new Exception(message: "Arquivo de rotas não encontrado para a versão '{$api['version']}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verifica se carregou
|
||||
if (!$loaded) {
|
||||
throw new Exception(message: "Nenhuma versão ativa da API foi carregada.");
|
||||
}
|
||||
16
app/Module/Auth/v0/manifest.json
Executable file
16
app/Module/Auth/v0/manifest.json
Executable file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "Autenticação",
|
||||
"dirName": "Auth",
|
||||
"slug": "auth",
|
||||
"description": "Gerencia login, logout, sessão e permissões de acesso.",
|
||||
"uuid": "0199abc8-026a-75b0-9b25-0f9456a44c70",
|
||||
"version": "1.0",
|
||||
"dependencies": [],
|
||||
"apis": [
|
||||
{
|
||||
"version": "v0",
|
||||
"status": "active",
|
||||
"start_date": "2025-08-01"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user