first commit
This commit is contained in:
73
app/Common/Component/Auth/bootstrap.php
Normal file
73
app/Common/Component/Auth/bootstrap.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
namespace Workbloom\Component\Auth;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use AxiumPHP\Core\Router;
|
||||
|
||||
// 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 = COMPONENT_PATH . "/{$manifest['dirName']}/{$api['version']}/Routes/Routes.php";
|
||||
if (file_exists(filename: $routeFile)) {
|
||||
Router::group(
|
||||
prefix: "{$api['version']}/{$manifest['slug']}",
|
||||
callback: function() use ($routeFile) {
|
||||
require_once realpath(path: $routeFile);
|
||||
},
|
||||
middlewares: []
|
||||
);
|
||||
$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/Common/Component/Auth/manifest.json
Normal file
16
app/Common/Component/Auth/manifest.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
51
app/Common/Component/Auth/v0/Controllers/AuthController.php
Normal file
51
app/Common/Component/Auth/v0/Controllers/AuthController.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
namespace Workbloom\Component\Auth\v0\Controllers;
|
||||
|
||||
use Workbloom\Helpers\DataSanitizer;
|
||||
use AxiumPHP\Helpers\RequestHelper;
|
||||
use Workbloom\Component\Auth\v0\Services\AuthService;
|
||||
|
||||
class AuthController {
|
||||
public function login() {
|
||||
// Recebe formulário de login
|
||||
$form = RequestHelper::getFilteredInput(form_type: INPUT_POST);
|
||||
|
||||
// Define campos obrigatórios
|
||||
$required_fields = [
|
||||
'login',
|
||||
'password'
|
||||
];
|
||||
|
||||
// Validação dos campos obrigatórios
|
||||
foreach ($required_fields as $field) {
|
||||
if (empty($form[$field])) {
|
||||
// Resposta JSON de erro
|
||||
RequestHelper::sendJsonResponse(
|
||||
response_code: 400,
|
||||
status: 'error',
|
||||
message: "O campo '{$field}' é obrigatório",
|
||||
output: []
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Chama o serviço de autenticação
|
||||
$loginService = (new AuthService())->login(
|
||||
credentials: [
|
||||
'login' => DataSanitizer::string(value: $form['login']),
|
||||
'password' => DataSanitizer::string(value: $form['password'])
|
||||
]
|
||||
);
|
||||
|
||||
// Resposta JSON
|
||||
RequestHelper::sendJsonResponse(
|
||||
response_code: 200,
|
||||
status: 'success',
|
||||
message: 'Login successful',
|
||||
output: [
|
||||
'data' => $form
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
177
app/Common/Component/Auth/v0/DTO/UsuarioDTO.php
Normal file
177
app/Common/Component/Auth/v0/DTO/UsuarioDTO.php
Normal file
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
namespace Workbloom\Component\Auth\v0\DTO;
|
||||
|
||||
class UsuarioDTO {
|
||||
private ?int $id = null;
|
||||
private ?string $uuid = null;
|
||||
private ?int $status_id = null;
|
||||
private ?int $is_root = 0;
|
||||
private ?string $nome_completo = null;
|
||||
private ?string $nome_usuario = null;
|
||||
private ?string $email = null;
|
||||
private ?string $senha_hash = null;
|
||||
private ?int $need_update_password = 1;
|
||||
private ?int $days_to_expire_password = 60;
|
||||
private ?string $last_password_update = 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: $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->status_id = $data['status_id'] ?? $this->status_id;
|
||||
$this->is_root = $data['is_root'] ?? $this->is_root;
|
||||
$this->nome_completo = $data['nome_completo'] ?? $this->nome_completo;
|
||||
$this->nome_usuario = $data['nome_usuario'] ?? $this->nome_usuario;
|
||||
$this->email = $data['email'] ?? $this->email;
|
||||
$this->senha_hash = $data['senha_hash'] ?? $this->senha_hash;
|
||||
$this->need_update_password = $data['need_update_password'] ?? $this->need_update_password;
|
||||
$this->days_to_expire_password = $data['days_to_expire_password'] ?? $this->days_to_expire_password;
|
||||
$this->last_password_update = $data['last_password_update'] ?? $this->last_password_update;
|
||||
$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,
|
||||
'status_id' => $this->status_id,
|
||||
'is_root' => $this->is_root,
|
||||
'nome_completo' => $this->nome_completo,
|
||||
'nome_usuario' => $this->nome_usuario,
|
||||
'email' => $this->email,
|
||||
'senha_hash' => $this->senha_hash,
|
||||
'need_update_password' => $this->need_update_password,
|
||||
'days_to_expire_password' => $this->days_to_expire_password,
|
||||
'last_password_update' => $this->last_password_update,
|
||||
'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 getStatusId(): ?int {
|
||||
return $this->status_id;
|
||||
}
|
||||
public function setStatusId(?int $status_id): void {
|
||||
$this->status_id = $status_id;
|
||||
}
|
||||
|
||||
public function getIsRoot(): ?int {
|
||||
return $this->is_root;
|
||||
}
|
||||
public function setIsRoot(?int $is_root): void {
|
||||
$this->is_root = $is_root;
|
||||
}
|
||||
|
||||
public function getNomeCompleto(): ?string {
|
||||
return $this->nome_completo;
|
||||
}
|
||||
public function setNomeCompleto(?string $nome_completo): void {
|
||||
$this->nome_completo = $nome_completo;
|
||||
}
|
||||
|
||||
public function getNomeUsuario(): ?string {
|
||||
return $this->nome_usuario;
|
||||
}
|
||||
public function setNomeUsuario(?string $nome_usuario): void {
|
||||
$this->nome_usuario = $nome_usuario;
|
||||
}
|
||||
|
||||
public function getEmail(): ?string {
|
||||
return $this->email;
|
||||
}
|
||||
public function setEmail(?string $email): void {
|
||||
$this->email = $email;
|
||||
}
|
||||
|
||||
public function getSenhaHash(): ?string {
|
||||
return $this->senha_hash;
|
||||
}
|
||||
public function setSenhaHash(?string $senha_hash): void {
|
||||
$this->senha_hash = $senha_hash;
|
||||
}
|
||||
|
||||
public function getNeedUpdatePassword(): ?int {
|
||||
return $this->need_update_password;
|
||||
}
|
||||
public function setNeedUpdatePassword(?int $need_update_password): void {
|
||||
$this->need_update_password = $need_update_password;
|
||||
}
|
||||
|
||||
public function getDaysToExpirePassword(): ?int {
|
||||
return $this->days_to_expire_password;
|
||||
}
|
||||
public function setDaysToExpirePassword(?int $days_to_expire_password): void {
|
||||
$this->days_to_expire_password = $days_to_expire_password;
|
||||
}
|
||||
|
||||
public function getLastPasswordUpdate(): ?string {
|
||||
return $this->last_password_update;
|
||||
}
|
||||
public function setLastPasswordUpdate(?string $last_password_update): void {
|
||||
$this->last_password_update = $last_password_update;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
127
app/Common/Component/Auth/v0/DTO/UsuarioTokenDTO.php
Normal file
127
app/Common/Component/Auth/v0/DTO/UsuarioTokenDTO.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
namespace Workbloom\Component\Auth\v0\DTO;
|
||||
|
||||
class UsuarioTokenDTO {
|
||||
private ?int $id = null;
|
||||
private ?int $usuario_id = null;
|
||||
private ?string $usuario_ip = null;
|
||||
private ?string $usuario_user_agent = null;
|
||||
private ?string $token = null;
|
||||
private ?int $is_revoked = 0;
|
||||
private ?string $created_at = null;
|
||||
private ?string $revoked_at = null;
|
||||
private ?string $expires_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: $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->usuario_id = $data['usuario_id'] ?? $this->usuario_id;
|
||||
$this->usuario_ip = $data['usuario_ip'] ?? $this->usuario_ip;
|
||||
$this->usuario_user_agent = $data['usuario_user_agent'] ?? $this->usuario_user_agent;
|
||||
$this->token = $data['token'] ?? $this->token;
|
||||
$this->is_revoked = $data['is_revoked'] ?? $this->is_revoked;
|
||||
$this->created_at = $data['created_at'] ?? $this->created_at;
|
||||
$this->revoked_at = $data['revoked_at'] ?? $this->revoked_at;
|
||||
$this->expires_at = $data['expires_at'] ?? $this->expires_at;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converte o DTO para um array associativo.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(): array {
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'usuario_id' => $this->usuario_id,
|
||||
'usuario_ip' => $this->usuario_ip,
|
||||
'usuario_user_agent' => $this->usuario_user_agent,
|
||||
'token' => $this->token,
|
||||
'is_revoked' => $this->is_revoked,
|
||||
'created_at' => $this->created_at,
|
||||
'revoked_at' => $this->revoked_at,
|
||||
'expires_at' => $this->expires_at,
|
||||
];
|
||||
}
|
||||
|
||||
public function getId(): ?int {
|
||||
return $this->id;
|
||||
}
|
||||
public function setId(?int $id): void {
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getUsuarioId(): ?int {
|
||||
return $this->usuario_id;
|
||||
}
|
||||
public function setUsuarioId(?int $usuario_id): void {
|
||||
$this->usuario_id = $usuario_id;
|
||||
}
|
||||
|
||||
public function getUsuarioIp(): ?string {
|
||||
return $this->usuario_ip;
|
||||
}
|
||||
public function setUsuarioIp(?string $usuario_ip): void {
|
||||
$this->usuario_ip = $usuario_ip;
|
||||
}
|
||||
|
||||
public function getUsuarioUserAgent(): ?string {
|
||||
return $this->usuario_user_agent;
|
||||
}
|
||||
public function setUsuarioUserAgent(?string $usuario_user_agent): void {
|
||||
$this->usuario_user_agent = $usuario_user_agent;
|
||||
}
|
||||
|
||||
public function getToken(): ?string {
|
||||
return $this->token;
|
||||
}
|
||||
public function setToken(?string $token): void {
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
public function getIsRevoked(): ?int {
|
||||
return $this->is_revoked;
|
||||
}
|
||||
public function setIsRevoked(?int $is_revoked): void {
|
||||
$this->is_revoked = $is_revoked;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?string {
|
||||
return $this->created_at;
|
||||
}
|
||||
public function setCreatedAt(?string $created_at): void {
|
||||
$this->created_at = $created_at;
|
||||
}
|
||||
|
||||
public function getRevokedAt(): ?string {
|
||||
return $this->revoked_at;
|
||||
}
|
||||
public function setRevokedAt(?string $revoked_at): void {
|
||||
$this->revoked_at = $revoked_at;
|
||||
}
|
||||
|
||||
public function getExpiresAt(): ?string {
|
||||
return $this->expires_at;
|
||||
}
|
||||
public function setExpiresAt(?string $expires_at): void {
|
||||
$this->expires_at = $expires_at;
|
||||
}
|
||||
}
|
||||
510
app/Common/Component/Auth/v0/Models/UsuarioModel.php
Normal file
510
app/Common/Component/Auth/v0/Models/UsuarioModel.php
Normal file
@@ -0,0 +1,510 @@
|
||||
<?php
|
||||
namespace Workbloom\Component\Auth\v0\Models;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use Workbloom\Services\DB;
|
||||
use Workbloom\Component\Auth\v0\DTO\UsuarioDTO;
|
||||
use Workbloom\Component\Auth\v0\DTO\UsuarioTokenDTO;
|
||||
|
||||
class UsuarioModel {
|
||||
protected string $usuarioTable = 'usuario';
|
||||
protected string $usuarioTokenTable = 'usuario_token';
|
||||
|
||||
/**
|
||||
* Armazena um novo usuário no banco de dados.
|
||||
*
|
||||
* Este método insere os dados de um novo usuário na tabela `{$this->usuarioTable}`, incluindo informações de segurança como hash de senha, status e configurações de expiração de senha. A operação é protegida por um bloco `try-catch` para garantir o tratamento de erros.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Execução da Query:** Prepara e executa a instrução `INSERT` com todos os campos preenchidos a partir do `UsuarioDTO`.
|
||||
* 2. **Confirmação e Retorno de Sucesso:** Se a inserção for bem-sucedida, retorna um array com **`status` 'success'**, código **201 Created** e os dados do usuário criado (ID e UUID).
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'**, código **500 Internal Server Error** (ou o código da exceção) e a mensagem detalhada de erro.
|
||||
*
|
||||
* @param UsuarioDTO $usuarioDTO O objeto DTO contendo os dados do usuário a ser criado.
|
||||
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e os dados do usuário criado.
|
||||
*/
|
||||
public function storeUsuario(UsuarioDTO $usuarioDTO): array {
|
||||
try {
|
||||
$sql =
|
||||
"INSERT INTO {$this->usuarioTable} (
|
||||
uuid,
|
||||
status_id,
|
||||
is_root,
|
||||
nome_completo,
|
||||
nome_usuario,
|
||||
email,
|
||||
senha_hash,
|
||||
need_update_password,
|
||||
days_to_expire_password,
|
||||
last_password_update,
|
||||
created_at,
|
||||
updated_at,
|
||||
deleted_at
|
||||
) VALUES (
|
||||
:uuid,
|
||||
:status_id,
|
||||
:is_root,
|
||||
:nome_completo,
|
||||
:nome_usuario,
|
||||
:email,
|
||||
:senha_hash,
|
||||
:need_update_password,
|
||||
:days_to_expire_password,
|
||||
:last_password_update,
|
||||
:created_at,
|
||||
:updated_at,
|
||||
:deleted_at
|
||||
)";
|
||||
|
||||
$params = [
|
||||
":uuid"=> $usuarioDTO->getUuid(),
|
||||
":status_id"=> $usuarioDTO->getStatusId(),
|
||||
":is_root"=> $usuarioDTO->getIsRoot(),
|
||||
":nome_completo"=> $usuarioDTO->getNomeCompleto(),
|
||||
":nome_usuario"=> $usuarioDTO->getNomeUsuario(),
|
||||
":email"=> $usuarioDTO->getEmail(),
|
||||
":senha_hash"=> $usuarioDTO->getSenhaHash(),
|
||||
":need_update_password"=> $usuarioDTO->getNeedUpdatePassword(),
|
||||
":days_to_expire_password"=> $usuarioDTO->getDaysToExpirePassword(),
|
||||
":last_password_update"=> $usuarioDTO->getLastPasswordUpdate(),
|
||||
":created_at"=> $usuarioDTO->getCreatedAt(),
|
||||
":updated_at"=> $usuarioDTO->getUpdatedAt(),
|
||||
":deleted_at"=> $usuarioDTO->getDeletedAt(),
|
||||
];
|
||||
|
||||
// Executa a query
|
||||
DB::execute(sql: $sql, params: $params);
|
||||
|
||||
return [
|
||||
'response_code' => 201,
|
||||
'status' => 'success',
|
||||
'message' => 'Usuário criado com sucesso',
|
||||
'output' => [
|
||||
'data' => [
|
||||
'id' => DB::lastInsertId(),
|
||||
'uuid' => $usuarioDTO->getUuid()
|
||||
]
|
||||
]
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
return [
|
||||
'response_code' => $e->getCode() ?? 500,
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage() ?? 'Internal Server Error'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atualiza um usuário existente no banco de dados.
|
||||
*
|
||||
* Este método modifica os dados de um usuário na tabela `{$this->usuarioTable}` com base no **UUID** fornecido no objeto `UsuarioDTO`. Ele atualiza informações de segurança e perfil, como hash de senha, status, permissão de root, nome e configurações de expiração de senha. A operação utiliza um bloco `try-catch` para garantir o tratamento de erros.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Execução da Query:** Prepara e executa a instrução `UPDATE` com todos os campos preenchidos a partir do `UsuarioDTO`, modificando o registro correspondente ao UUID.
|
||||
* 2. **Confirmação e Retorno de Sucesso:** Se a atualização for bem-sucedida, retorna um array com **`status` 'success'**, código **200 OK** e uma mensagem de êxito.
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'**, o código de erro da exceção (ou `500` como fallback) e a mensagem detalhada de erro.
|
||||
*
|
||||
* @param UsuarioDTO $usuarioDTO O objeto DTO contendo os dados atualizados do usuário, incluindo o UUID para identificação.
|
||||
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e uma mensagem.
|
||||
*/
|
||||
public function updateUsuario(UsuarioDTO $usuarioDTO): array {
|
||||
try {
|
||||
$sql =
|
||||
"UPDATE {$this->usuarioTable} SET
|
||||
status_id = :status_id,
|
||||
is_root = :is_root,
|
||||
nome_completo = :nome_completo,
|
||||
nome_usuario = :nome_usuario,
|
||||
email = :email,
|
||||
senha_hash = :senha_hash,
|
||||
need_update_password = :need_update_password,
|
||||
days_to_expire_password = :days_to_expire_password,
|
||||
last_password_update = :last_password_update,
|
||||
updated_at = :updated_at
|
||||
WHERE uuid = :uuid";
|
||||
|
||||
$params = [
|
||||
":status_id" => $usuarioDTO->getStatusId(),
|
||||
":is_root" => $usuarioDTO->getIsRoot(),
|
||||
":nome_completo" => $usuarioDTO->getNomeCompleto(),
|
||||
":nome_usuario" => $usuarioDTO->getNomeUsuario(),
|
||||
":email" => $usuarioDTO->getEmail(),
|
||||
":senha_hash" => $usuarioDTO->getSenhaHash(),
|
||||
":need_update_password" => $usuarioDTO->getNeedUpdatePassword(),
|
||||
":days_to_expire_password" => $usuarioDTO->getDaysToExpirePassword(),
|
||||
":last_password_update" => $usuarioDTO->getLastPasswordUpdate(),
|
||||
":updated_at" => $usuarioDTO->getUpdatedAt(),
|
||||
":uuid" => $usuarioDTO->getUuid(),
|
||||
];
|
||||
|
||||
// Executa a query
|
||||
DB::execute(sql: $sql, params: $params);
|
||||
|
||||
return [
|
||||
'response_code' => 200,
|
||||
'status' => 'success',
|
||||
'message' => 'Usuário atualizado com sucesso',
|
||||
];
|
||||
} catch(Exception $e) {
|
||||
return [
|
||||
'response_code' => $e->getCode() ?? 500,
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage() ?? 'Internal Server Error'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Realiza a exclusão lógica de um usuário no banco de dados.
|
||||
*
|
||||
* Este método atualiza a coluna `deleted_at` do registro do usuário na tabela `{$this->usuarioTable}` com o timestamp fornecido no objeto `UsuarioDTO`. Isso garante uma **exclusão lógica**, mantendo o registro para fins de auditoria, mas marcando-o como inativo.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Execução da Query:** Prepara e executa a instrução `UPDATE` que define a data de exclusão para o registro correspondente ao UUID do usuário.
|
||||
* 2. **Retorno de Sucesso:** Se a atualização for bem-sucedida, retorna um array com **`status` 'success'**, código **200 OK** e uma mensagem de êxito.
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'**, o código de erro da exceção (ou `500` como fallback) e a mensagem detalhada da exceção.
|
||||
*
|
||||
* @param UsuarioDTO $usuarioDTO O objeto DTO contendo o UUID do usuário a ser excluído e o timestamp a ser definido em `deleted_at`.
|
||||
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e uma mensagem.
|
||||
*/
|
||||
public function deleteUsuario(UsuarioDTO $usuarioDTO): array {
|
||||
try {
|
||||
$sql =
|
||||
"UPDATE {$this->usuarioTable} SET
|
||||
deleted_at = :deleted_at
|
||||
WHERE uuid = :uuid";
|
||||
|
||||
$params = [
|
||||
":deleted_at" => $usuarioDTO->getDeletedAt(),
|
||||
":uuid" => $usuarioDTO->getUuid(),
|
||||
];
|
||||
|
||||
// Executa a query
|
||||
DB::execute(sql: $sql, params: $params);
|
||||
|
||||
return [
|
||||
'response_code' => 200,
|
||||
'status' => 'success',
|
||||
'message' => 'Usuário excluído com sucesso',
|
||||
];
|
||||
} catch(Exception $e) {
|
||||
return [
|
||||
'response_code' => $e->getCode() ?? 500,
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage() ?? 'Internal Server Error'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Busca um único usuário no banco de dados por um identificador específico.
|
||||
*
|
||||
* Este método recupera todos os detalhes de um usuário da tabela `{$this->usuarioTable}`. A busca é flexível e permite usar qualquer coluna como identificador (`$identifier`) e o valor correspondente.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Construção da Query Dinâmica:** O método monta e executa uma consulta `SELECT` que usa o `$identifier` de forma dinâmica na cláusula `WHERE` e um parâmetro nomeado seguro (`:value`) para o valor.
|
||||
* 2. **Execução da Consulta:** A consulta usa `DB::fetchOne`, indicando que apenas um registro é esperado.
|
||||
* 3. **Verificação de Resultados:**
|
||||
* * Se o usuário for encontrado, retorna um array com **`status` 'success'**, código **200 OK** e os dados do usuário na chave `output`.
|
||||
* * Se o usuário **não for encontrado** (`empty($result)`), retorna um array com **`status` 'error'** e código **404 Not Found**, com uma mensagem indicando que o usuário não foi encontrado.
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'**, o código de erro da exceção (ou `500` como fallback) e uma mensagem detalhada de erro.
|
||||
*
|
||||
* @param string $identifier A coluna da tabela a ser usada como critério de busca (ex: 'uuid', 'email', 'nome_usuario').
|
||||
* @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 findUsuarioByIdentifier(string $identifier, mixed $value): array {
|
||||
try {
|
||||
$sql =
|
||||
"SELECT
|
||||
id,
|
||||
uuid,
|
||||
status_id,
|
||||
is_root,
|
||||
nome_completo,
|
||||
nome_usuario,
|
||||
email,
|
||||
senha_hash,
|
||||
need_update_password,
|
||||
days_to_expire_password,
|
||||
last_password_update,
|
||||
created_at,
|
||||
updated_at,
|
||||
deleted_at
|
||||
FROM {$this->usuarioTable}
|
||||
WHERE {$identifier} = :value";
|
||||
|
||||
$params = [
|
||||
":value" => $value,
|
||||
];
|
||||
|
||||
$result = DB::fetchOne(sql: $sql, params: $params);
|
||||
if(empty($result)) {
|
||||
return [
|
||||
'response_code' => 404,
|
||||
'status' => 'error',
|
||||
'message' => 'Usuário não encontrado',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'response_code' => 200,
|
||||
'status' => 'success',
|
||||
'message' => 'Usuário encontrado com sucesso',
|
||||
'output' => [
|
||||
'data' => $result
|
||||
]
|
||||
];
|
||||
} catch(Exception $e) {
|
||||
return [
|
||||
'response_code' => $e->getCode() ?? 500,
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage() ?? 'Internal Server Error'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Busca um único usuário no banco de dados usando seu e-mail ou nome de usuário como critério de login.
|
||||
*
|
||||
* Este método é utilizado para o processo de autenticação, tentando encontrar um usuário na tabela `{$this->usuarioTable}` por meio de uma correspondência na coluna `nome_usuario` ou `email`.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Execução da Query:** Monta e executa uma consulta `SELECT` que usa o valor `$login` para tentar encontrar um registro no campo `email` ou `nome_usuario`.
|
||||
* 2. **Verificação de Resultados:**
|
||||
* * Se o usuário for encontrado, retorna um array com **`status` 'success'**, código **200 OK** e os dados do usuário na chave `output`.
|
||||
* * Se o usuário **não for encontrado** (`empty($result)`), retorna um array com **`status` 'error'** e código **404 Not Found**, com uma mensagem indicando que o usuário não foi encontrado.
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'**, o código de erro da exceção (ou `500` como fallback) e uma mensagem detalhada de erro.
|
||||
*
|
||||
* @param string $login O valor do e-mail ou nome de usuário a ser pesquisado.
|
||||
* @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 findUsuarioByLogin(string $login): array {
|
||||
try {
|
||||
$sql =
|
||||
"SELECT
|
||||
id,
|
||||
uuid,
|
||||
status_id,
|
||||
is_root,
|
||||
nome_completo,
|
||||
nome_usuario,
|
||||
email,
|
||||
senha_hash,
|
||||
need_update_password,
|
||||
days_to_expire_password,
|
||||
last_password_update,
|
||||
created_at,
|
||||
updated_at,
|
||||
deleted_at
|
||||
FROM {$this->usuarioTable}
|
||||
WHERE (nome_usuario = :value OR email = :value)";
|
||||
|
||||
$params = [
|
||||
":value" => $login,
|
||||
];
|
||||
|
||||
$result = DB::fetchOne(sql: $sql, params: $params);
|
||||
if(empty($result)) {
|
||||
return [
|
||||
'response_code' => 404,
|
||||
'status' => 'error',
|
||||
'message' => 'Usuário não encontrado',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'response_code' => 200,
|
||||
'status' => 'success',
|
||||
'message' => 'Usuário encontrado com sucesso',
|
||||
'output' => [
|
||||
'data' => $result
|
||||
]
|
||||
];
|
||||
} catch(Exception $e) {
|
||||
return [
|
||||
'response_code' => $e->getCode() ?? 500,
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage() ?? 'Internal Server Error'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Armazena um novo token de autenticação de usuário no banco de dados.
|
||||
*
|
||||
* Este método é responsável por registrar o token na tabela `{$this->usuarioTokenTable}`, associando-o ao usuário, IP, User Agent e definindo seu status de revogação e tempo de expiração.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Execução da Query:** Prepara e executa a instrução `INSERT` para inserir o registro do token com todos os detalhes fornecidos pelo `UsuarioTokenDTO`.
|
||||
* 2. **Confirmação e Retorno de Sucesso:** Se a inserção for bem-sucedida, retorna um array com **`status` 'success'**, código **201 Created** e os dados do token (ID gerado e o token).
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'**, o código de erro da exceção (ou `500` como fallback) e a mensagem detalhada de erro.
|
||||
*
|
||||
* @param UsuarioTokenDTO $usuarioTokenDTO O objeto DTO contendo os dados do token a ser armazenado.
|
||||
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e os dados do token.
|
||||
*/
|
||||
public function storeUsuarioToken(UsuarioTokenDTO $usuarioTokenDTO): array {
|
||||
try {
|
||||
$sql =
|
||||
"INSERT INTO {$this->usuarioTokenTable} (
|
||||
usuario_id,
|
||||
usuario_ip,
|
||||
usuario_user_agent,
|
||||
token,
|
||||
is_revoked,
|
||||
created_at,
|
||||
expires_at
|
||||
) VALUES (
|
||||
:usuario_id,
|
||||
:usuario_ip,
|
||||
:usuario_user_agent,
|
||||
:token,
|
||||
:is_revoked,
|
||||
:created_at,
|
||||
:expires_at
|
||||
)";
|
||||
|
||||
$params = [
|
||||
":usuario_id"=> $usuarioTokenDTO->getUsuarioId(),
|
||||
":usuario_ip"=> $usuarioTokenDTO->getUsuarioIp(),
|
||||
":usuario_user_agent"=> $usuarioTokenDTO->getUsuarioUserAgent(),
|
||||
":token"=> $usuarioTokenDTO->getToken(),
|
||||
":is_revoked"=> $usuarioTokenDTO->getIsRevoked(),
|
||||
":created_at"=> $usuarioTokenDTO->getCreatedAt(),
|
||||
":expires_at"=> $usuarioTokenDTO->getExpiresAt(),
|
||||
];
|
||||
|
||||
// Executa a query
|
||||
DB::execute(sql: $sql, params: $params);
|
||||
|
||||
return [
|
||||
'response_code' => 201,
|
||||
'status' => 'success',
|
||||
'message' => 'Token de usuário criado com sucesso',
|
||||
'output' => [
|
||||
'data' => [
|
||||
'id' => DB::lastInsertId(),
|
||||
'token' => $usuarioTokenDTO->getToken()
|
||||
]
|
||||
]
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
return [
|
||||
'response_code' => $e->getCode() ?? 500,
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage() ?? 'Internal Server Error'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atualiza o status de revogação de um token de usuário no banco de dados.
|
||||
*
|
||||
* Este método modifica o registro de um token na tabela `{$this->usuarioTokenTable}`, alterando o status de revogação (`is_revoked`) e o timestamp de revogação (`revoked_at`), com base na string do token fornecida no DTO.
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Execução da Query:** Prepara e executa a instrução `UPDATE` para modificar o status de revogação do token.
|
||||
* 2. **Retorno de Sucesso:** Se a atualização for bem-sucedida, retorna um array com **`status` 'success'**, código **200 OK** e uma mensagem de êxito.
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'**, o código de erro da exceção (ou `500` como fallback) e a mensagem detalhada de erro.
|
||||
*
|
||||
* @param UsuarioTokenDTO $usuarioTokenDTO O objeto DTO contendo o token a ser atualizado, o novo status de revogação (`is_revoked`) e a data de revogação (`revoked_at`).
|
||||
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e uma mensagem.
|
||||
*/
|
||||
public function updateUsuarioToken(UsuarioTokenDTO $usuarioTokenDTO): array {
|
||||
try {
|
||||
$sql =
|
||||
"UPDATE {$this->usuarioTokenTable} SET
|
||||
is_revoked = :is_revoked,
|
||||
revoked_at = :revoked_at
|
||||
WHERE token = :token";
|
||||
|
||||
$params = [
|
||||
":is_revoked"=> $usuarioTokenDTO->getIsRevoked(),
|
||||
":revoked_at"=> $usuarioTokenDTO->getRevokedAt(),
|
||||
":token"=> $usuarioTokenDTO->getToken(),
|
||||
];
|
||||
|
||||
// Executa a query
|
||||
DB::execute(sql: $sql, params: $params);
|
||||
|
||||
return [
|
||||
'response_code' => 200,
|
||||
'status' => 'success',
|
||||
'message' => 'Token de usuário atualizado com sucesso',
|
||||
];
|
||||
} catch(Exception $e) {
|
||||
return [
|
||||
'response_code' => $e->getCode() ?? 500,
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage() ?? 'Internal Server Error'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoga (invalida) todos os tokens de autenticação ativos de um usuário no banco de dados, buscando o usuário pelo UUID.
|
||||
*
|
||||
* Este método executa uma exclusão lógica em todos os tokens não revogados (`is_revoked = 0`) associados ao ID interno de um usuário, que é determinado pelo `usuario_uuid` fornecido.
|
||||
*
|
||||
* #### Atenção à Sintaxe SQL (PostgreSQL/SQL Server):
|
||||
* A sintaxe `UPDATE ... FROM` é comum em PostgreSQL ou SQL Server e está sendo usada para realizar um JOIN implícito na instrução UPDATE, o que não é sintaxe padrão do MySQL. Assumindo que o ambiente de banco de dados suporte essa sintaxe (ou que o código seja adaptado para o SGBD correto).
|
||||
*
|
||||
* #### Fluxo de Operação:
|
||||
* 1. **Execução da Query:** Prepara e executa a instrução `UPDATE` para definir `is_revoked = 1` e `revoked_at` para o timestamp atual para todos os tokens ativos do usuário.
|
||||
* 2. **Confirmação e Retorno de Sucesso:** Se a atualização for bem-sucedida, retorna um array com **`status` 'success'**, código **200 OK** e uma mensagem de êxito.
|
||||
*
|
||||
* #### Tratamento de Erros:
|
||||
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'**, o código de erro da exceção (ou `500` como fallback) e uma mensagem detalhada de erro.
|
||||
*
|
||||
* @param string $usuario_uuid O UUID 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.
|
||||
*/
|
||||
public function revolkOldUsuarioTokens(string $usuario_uuid): array {
|
||||
try {
|
||||
$sql =
|
||||
"UPDATE {$this->usuarioTokenTable} AS ut SET
|
||||
is_revoked = 1,
|
||||
revoked_at = :revoked_at
|
||||
FROM {$this->usuarioTable} AS u
|
||||
WHERE ut.usuario_id = u.id
|
||||
AND u.uuid = :usuario_uuid
|
||||
AND ut.is_revoked = 0";
|
||||
|
||||
$params = [
|
||||
":revoked_at"=> (new DateTime())->format(format: 'Y-m-d H:i:s'),
|
||||
":usuario_uuid"=> $usuario_uuid
|
||||
];
|
||||
|
||||
// Executa a query
|
||||
DB::execute(sql: $sql, params: $params);
|
||||
|
||||
return [
|
||||
'response_code' => 200,
|
||||
'status' => 'success',
|
||||
'message' => 'Tokens antigos revogados com sucesso',
|
||||
];
|
||||
} catch(Exception $e) {
|
||||
return [
|
||||
'response_code' => $e->getCode() ?? 500,
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage() ?? 'Internal Server Error'
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
9
app/Common/Component/Auth/v0/Routes/AuthRoutes.php
Normal file
9
app/Common/Component/Auth/v0/Routes/AuthRoutes.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
use AxiumPHP\Core\Router;
|
||||
use Workbloom\Component\Auth\v0\Controllers\AuthController;
|
||||
|
||||
// Rota de login
|
||||
Router::POST(
|
||||
uri: '/login',
|
||||
handler: [AuthController::class, 'login']
|
||||
);
|
||||
3
app/Common/Component/Auth/v0/Routes/Routes.php
Normal file
3
app/Common/Component/Auth/v0/Routes/Routes.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
// Importa rotas de autenticação
|
||||
require_once __DIR__ . '/AuthRoutes.php';
|
||||
8
app/Common/Component/Auth/v0/Services/AuthService.php
Normal file
8
app/Common/Component/Auth/v0/Services/AuthService.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace Workbloom\Component\Auth\v0\Services;
|
||||
|
||||
class AuthService {
|
||||
public function login(array $credentials) {
|
||||
return ['data' => $credentials];
|
||||
}
|
||||
}
|
||||
42
app/Common/Config/Config.php
Normal file
42
app/Common/Config/Config.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
namespace Workbloom\Config;
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
class Config {
|
||||
private $config = [];
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Método construtor
|
||||
*/
|
||||
public function __construct() {
|
||||
// Carrega apenas para $_ENV, não para $_SERVER
|
||||
$dotEnv = Dotenv::createUnsafeImmutable(
|
||||
paths: realpath(path: __DIR__ . "/../../../"),
|
||||
names: '.env'
|
||||
);
|
||||
$dotEnv->safeLoad(); // não dá erro se .env não existir
|
||||
|
||||
// Carrega as configurações
|
||||
$this->config = require_once realpath(path: __DIR__ . "/Consts.php");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna a instância única da classe
|
||||
*/
|
||||
public static function getInstance() {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new Config();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna uma configuração
|
||||
*/
|
||||
public function get(string $key) {
|
||||
return $this->config[$key] ?? null;
|
||||
}
|
||||
}
|
||||
21
app/Common/Config/Consts.php
Normal file
21
app/Common/Config/Consts.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
return [
|
||||
'SYSTEM_URL' => (string) getenv(name: 'SYSTEM_URL'),
|
||||
'SYSTEM_VERSION' => (string) getenv(name: 'SYSTEM_VERSION'),
|
||||
'SYSTEM_TIMEZONE' => (string) getenv(name: 'SYSTEM_TIMEZONE'),
|
||||
'SYSTEM_ENVIRONMENT_ID' => (int) getenv(name: 'SYSTEM_ENVIRONMENT_ID'),
|
||||
'SYSTEM_FRONTEND_URL' => (string) getenv(name: 'SYSTEM_FRONTEND_URL'),
|
||||
|
||||
'JWT_ALGO' => getenv(name: 'JWT_ALGO'),
|
||||
'JWT_ISSUER' => getenv(name: 'JWT_ISSUER'),
|
||||
'JWT_SECRET' => getenv(name: 'JWT_SECRET'),
|
||||
'JWT_EXPIRATION' => (int) getenv(name: 'JWT_EXPIRATION'),
|
||||
|
||||
'DEFAULT_DATABASE_HOST' => getenv(name: 'DEFAULT_DATABASE_HOST'),
|
||||
'DEFAULT_DATABASE_PORT' => getenv(name: 'DEFAULT_DATABASE_PORT'),
|
||||
'DEFAULT_DATABASE_DRIVER' => getenv(name: 'DEFAULT_DATABASE_DRIVER'),
|
||||
'DEFAULT_DATABASE_SCHEMA' => getenv(name: 'DEFAULT_DATABASE_SCHEMA'),
|
||||
'DEFAULT_DATABASE_CHARSET' => getenv(name: 'DEFAULT_DATABASE_CHARSET'),
|
||||
'DEFAULT_DATABASE_USERNAME' => getenv(name: 'DEFAULT_DATABASE_USERNAME'),
|
||||
'DEFAULT_DATABASE_PASSWORD' => getenv(name: 'DEFAULT_DATABASE_PASSWORD'),
|
||||
];
|
||||
69
app/Common/Helpers/DataSanitizer.php
Normal file
69
app/Common/Helpers/DataSanitizer.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
namespace Workbloom\Helpers;
|
||||
|
||||
use DateTime;
|
||||
|
||||
class DataSanitizer {
|
||||
public static function string(mixed $value): string|null {
|
||||
if($value === null) {
|
||||
return null;
|
||||
}
|
||||
return trim(string: strip_tags(string: $value));
|
||||
}
|
||||
|
||||
public static function int(mixed $value): int {
|
||||
return filter_var(value: $value, filter: FILTER_VALIDATE_INT) ?? 0;
|
||||
}
|
||||
|
||||
public static function float(mixed $value): float {
|
||||
return filter_var(value: $value, filter: FILTER_VALIDATE_FLOAT) ?? 0.0;
|
||||
}
|
||||
|
||||
public static function email(string $value): string {
|
||||
$value = trim(string: strtolower(string: $value));
|
||||
return filter_var(value: $value, filter: FILTER_VALIDATE_EMAIL) ?: '';
|
||||
}
|
||||
|
||||
public static function phone(string $value): string {
|
||||
// remove tudo que não for número
|
||||
return preg_replace(pattern: '/\D+/', replacement: '', subject: $value) ?? '';
|
||||
}
|
||||
|
||||
public static function document(string $value): string {
|
||||
// CPF, CNPJ, RG etc. só números
|
||||
return preg_replace(pattern: '/\D+/', replacement: '', subject: $value) ?? '';
|
||||
}
|
||||
|
||||
public static function date(string $value, string $format = 'Y-m-d'): string {
|
||||
$value = trim(string: $value);
|
||||
|
||||
if ($value === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$date = DateTime::createFromFormat(format: "!{$format}", datetime: $value);
|
||||
|
||||
if (!$date) {
|
||||
// tenta converter qualquer formato válido
|
||||
$date = date_create(datetime: $value);
|
||||
}
|
||||
|
||||
return $date ? $date->format(format: $format) : '';
|
||||
}
|
||||
|
||||
public static function datetime(string $value, string $format = 'Y-m-d H:i:s'): string {
|
||||
$value = trim(string: $value);
|
||||
|
||||
if ($value === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$date = DateTime::createFromFormat(format: "!{$format}", datetime: $value);
|
||||
|
||||
if (!$date) {
|
||||
$date = date_create(datetime: $value);
|
||||
}
|
||||
|
||||
return $date ? $date->format(format: $format) : '';
|
||||
}
|
||||
}
|
||||
222
app/Common/Helpers/DataValidator.php
Normal file
222
app/Common/Helpers/DataValidator.php
Normal file
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
namespace Workbloom\Helpers;
|
||||
|
||||
class DataValidator {
|
||||
private array $inputs;
|
||||
private array $errors = [];
|
||||
|
||||
public function __construct(array $inputs) {
|
||||
$this->inputs = $inputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Valida um campo usando uma regra específica.
|
||||
*/
|
||||
public function validate(string $field, callable|string $rule, string $message = '', array $additionalParams = []): static {
|
||||
$value = $this->inputs[$field] ?? null;
|
||||
|
||||
// Se for string, monta callable usando a própria classe
|
||||
if (is_string(value: $rule) && method_exists(object_or_class: self::class, method: $rule)) {
|
||||
$rule = [self::class, $rule];
|
||||
}
|
||||
|
||||
$params = array_merge([$value], $additionalParams);
|
||||
|
||||
$result = call_user_func_array(callback: $rule, args: $params);
|
||||
|
||||
// Caso seja validação de senha (retorna array)
|
||||
if (is_array(value: $result) && isset($result['valid'])) {
|
||||
if (!$result['valid']) {
|
||||
$this->errors[$field] = [
|
||||
'status' => 'is_invalid',
|
||||
'message' => $result['errors']
|
||||
];
|
||||
}
|
||||
}
|
||||
// Para outras validações booleanas
|
||||
elseif ($result === false) {
|
||||
$this->errors[$field] = [
|
||||
'status' => 'is_invalid',
|
||||
'message' => $message
|
||||
];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getErrors(): array {
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
public function passes(): bool {
|
||||
return empty($this->errors);
|
||||
}
|
||||
|
||||
// ---------- Regras comuns ----------
|
||||
|
||||
public static function validateEmail(string $value): bool {
|
||||
return (bool) filter_var(value: $value, filter: FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
|
||||
public static function validatePhone(string $value, int $minLength = 10, int $maxLength = 13): bool {
|
||||
$digits = preg_replace(pattern: '/\D+/', replacement: '', subject: $value);
|
||||
$len = strlen(string: $digits);
|
||||
return $len >= $minLength && $len <= $maxLength;
|
||||
}
|
||||
|
||||
public static function validateCpf(string $value): bool {
|
||||
$cpf = preg_replace(pattern: '/\D+/', replacement: '', subject: $value);
|
||||
|
||||
if (strlen(string: $cpf) !== 11 || preg_match(pattern: '/(\d)\1{10}/', subject: $cpf)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for ($t = 9; $t < 11; $t++) {
|
||||
$d = 0;
|
||||
for ($c = 0; $c < $t; $c++) {
|
||||
$d += $cpf[$c] * (($t + 1) - $c);
|
||||
}
|
||||
$d = ((10 * $d) % 11) % 10;
|
||||
if ($cpf[$c] != $d) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function validateCnpj(string $value): bool {
|
||||
// Remove qualquer máscara (pontos, barras, hífens)
|
||||
$cleanedValue = preg_replace(pattern: '/[.\-\/]/', replacement: '', subject: $value);
|
||||
|
||||
// O CNPJ sem máscara deve ter 14 caracteres (12 alfanuméricos + 2 dígitos)
|
||||
if (strlen(string: $cleanedValue) !== 14) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cnpjBase = strtoupper(string: substr(string: $cleanedValue, offset: 0, length: 12)); // Os 12 caracteres alfanuméricos
|
||||
$dvRecebidos = substr(string: $cleanedValue, offset: 12, length: 2); // Os 2 dígitos verificadores
|
||||
|
||||
// Verifica se os dois últimos caracteres são realmente dígitos
|
||||
if (!ctype_digit(text: $dvRecebidos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mapeamento de Valor para cálculo do DV (do documento) [cite: 19]
|
||||
$mapaValores = [
|
||||
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
|
||||
'A' => 17, 'B' => 18, 'C' => 19, 'D' => 20, 'E' => 21, 'F' => 22, 'G' => 23, 'H' => 24, 'I' => 25,
|
||||
'J' => 26, 'K' => 27, 'L' => 28, 'M' => 29, 'N' => 30, 'O' => 31, 'P' => 32, 'Q' => 33, 'R' => 34,
|
||||
'S' => 35, 'T' => 36, 'U' => 37, 'V' => 38, 'W' => 39, 'X' => 40, 'Y' => 41, 'Z' => 42
|
||||
];
|
||||
|
||||
// Array de pesos de 2 a 9 (repetidos da direita para a esquerda, recomeçando depois do 8º caracter) [cite: 26, 27]
|
||||
// O documento usa [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] para o 1º DV no exemplo de 12 caracteres [cite: 27]
|
||||
$pesos1Dv = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
|
||||
|
||||
// Para o 2º DV, são 13 caracteres (os 12 de base + o 1º DV), e a contagem de pesos recomeça (2 a 9)
|
||||
// O documento usa [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] para 13 caracteres no exemplo [cite: 40]
|
||||
$pesos2Dv = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
|
||||
|
||||
$cnpjParaCalculo = $cnpjBase;
|
||||
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$soma = 0;
|
||||
$pesos = ($i === 0) ? $pesos1Dv : $pesos2Dv;
|
||||
$tamanho = strlen(string: $cnpjParaCalculo);
|
||||
|
||||
// O documento usa os pesos na ordem da esquerda para a direita no cálculo [cite: 27, 40]
|
||||
for ($j = 0; $j < $tamanho; $j++) {
|
||||
$caractere = $cnpjParaCalculo[$j];
|
||||
$valor = $mapaValores[$caractere] ?? null;
|
||||
|
||||
// Se o caractere não for um dos mapeados (ex: um caractere especial ou minúsculo que não foi maiusculizado)
|
||||
if ($valor === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$peso = $pesos[$j];
|
||||
$soma += $valor * $peso;
|
||||
}
|
||||
|
||||
// Obter o resto da divisão do somatório por 11 [cite: 31]
|
||||
$resto = $soma % 11;
|
||||
|
||||
// Se o resto da divisão for igual a 1 ou 0, o primeiro dígito será igual a 0 (zero)[cite: 32].
|
||||
// Senão, o primeiro dígito será igual ao resultado de 11 - resto[cite: 33].
|
||||
$digitoCalculado = ($resto < 2) ? 0 : 11 - $resto;
|
||||
|
||||
$dvRecebido = (int) $dvRecebidos[$i];
|
||||
|
||||
if ($dvRecebido !== $digitoCalculado) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Para o cálculo do segundo DV, acrescentar o primeiro DV (calculado) ao final do CNPJ base [cite: 38]
|
||||
if ($i === 0) {
|
||||
$cnpjParaCalculo .= $digitoCalculado;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function notEmpty(mixed $value): bool {
|
||||
if (is_array(value: $value)) {
|
||||
return !empty(array_filter(array: $value, callback: fn($v) => $v !== null && $v !== ''));
|
||||
}
|
||||
return !empty(trim(string: (string) $value));
|
||||
}
|
||||
|
||||
public static function validatePassword(string $password, array $rules = []): array {
|
||||
$errors = [];
|
||||
|
||||
$minLength = $rules['minLength'] ?? 8;
|
||||
$maxLength = $rules['maxLength'] ?? 64;
|
||||
$requireUpper = $rules['upper'] ?? true;
|
||||
$requireLower = $rules['lower'] ?? true;
|
||||
$requireDigit = $rules['digit'] ?? true;
|
||||
$requireSymbol = $rules['symbol'] ?? true;
|
||||
|
||||
if (strlen(string: $password) < $minLength) {
|
||||
$errors[] = "Senha deve ter no mínimo {$minLength} caracteres.";
|
||||
}
|
||||
|
||||
if (strlen(string: $password) > $maxLength) {
|
||||
$errors[] = "Senha deve ter no máximo {$maxLength} caracteres.";
|
||||
}
|
||||
|
||||
if ($requireUpper && !preg_match(pattern: '/[A-Z]/', subject: $password)) {
|
||||
$errors[] = "Senha deve conter pelo menos uma letra maiúscula.";
|
||||
}
|
||||
|
||||
if ($requireLower && !preg_match(pattern: '/[a-z]/', subject: $password)) {
|
||||
$errors[] = "Senha deve conter pelo menos uma letra minúscula.";
|
||||
}
|
||||
|
||||
if ($requireDigit && !preg_match(pattern: '/\d/', subject: $password)) {
|
||||
$errors[] = "Senha deve conter pelo menos um número.";
|
||||
}
|
||||
|
||||
if ($requireSymbol && !preg_match(pattern: '/[\W_]/', subject: $password)) {
|
||||
$errors[] = "Senha deve conter pelo menos um símbolo.";
|
||||
}
|
||||
|
||||
return [
|
||||
'valid' => empty($errors),
|
||||
'errors' => $errors
|
||||
];
|
||||
}
|
||||
|
||||
public static function validateList(mixed $value, array $validOptions): bool {
|
||||
if (is_array(value: $value)) {
|
||||
foreach ($value as $item) {
|
||||
if (!in_array(needle: $item, haystack: $validOptions, strict: true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_array(needle: $value, haystack: $validOptions, strict: true);
|
||||
}
|
||||
}
|
||||
8
app/Common/Services/DB.php
Normal file
8
app/Common/Services/DB.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace Workbloom\Services;
|
||||
|
||||
use AxiumPHP\Core\Database;
|
||||
|
||||
class DB extends Database {
|
||||
|
||||
}
|
||||
0
app/Storage/blank_file
Normal file
0
app/Storage/blank_file
Normal file
Reference in New Issue
Block a user