init commit
All checks were successful
build-api-image / build (push) Successful in 51s

This commit is contained in:
Antonio Lopes dos Santos
2026-08-04 16:52:02 -03:00
commit afec302c3e
11594 changed files with 1636728 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
<?php
namespace App\Services\Mobile\ConectaCidadao\Autenticacao;
use App\Models\Cadastro\SujeitoAtencao\Cidadao;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Hash;
class AutenticacaoService
{
public function login(array $dados): array
{
$cidadao = Cidadao::where('cpf', $dados['cpf'])
->whereHas('paciente', fn ($q) => $q->whereNotNull('senha'))
->first();
if (is_null($cidadao) || !Hash::check($dados['senha'], $cidadao->paciente->senha)) {
throw new \InvalidArgumentException('CPF e/ou senha incorreto(s).', Response::HTTP_UNAUTHORIZED);
}
$usuario = $cidadao->paciente;
$token = auth('mobile')->setTTL(15)->login($usuario);
$refreshToken = auth('mobile')
->claims(['type' => 'refresh'])
->setTTL(10080)
->tokenById($usuario->id);
return [
'expiration' => time() + 15 * 60,
'token' => $token,
'refresh_token' => $refreshToken,
'usuario' => [
'cpf' => $dados['cpf'],
'nome' => $cidadao->nome,
'paciente_id' => $cidadao->paciente->id,
],
];
}
public function refresh()
{
$payload = auth('mobile')->payload();
if ($payload->get('type') !== 'refresh') {
return response()->json(['error' => 'Token inválido'], 401);
}
$user = auth('mobile')->user();
auth('mobile')->invalidate();
$accessToken = auth('mobile')
->claims(['type' => 'access'])
->setTTL(15)
->tokenById($user->id);
$refreshToken = auth('mobile')
->claims(['type' => 'refresh'])
->setTTL(10080)
->tokenById($user->id);
return [
'token' => $accessToken,
'refresh_token' => $refreshToken,
'expiration' => time() + 15 * 60,
];
}
}

View File

@@ -0,0 +1,145 @@
<?php
namespace App\Services\Mobile\ConectaCidadao\Autenticacao;
use App\Enums\Mobile\ConectaCidadao\PrimeiroAcesso;
use App\Models\Cadastro\SujeitoAtencao\Cidadao;
use App\Models\Mobile\ConectaCidadao\ConectaCidadaoSmsCode;
use App\Services\Sms\SmsService;
use Carbon\Carbon;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Hash;
class PrimeiroAcessoService
{
public function iniciar(array $dados): array
{
$cidadao = Cidadao::where('cpf', $dados['cpf'])->first();
$telefonePrincipal = $cidadao ? $cidadao->telefone_principal : null;
$paciente = $cidadao ? $cidadao->paciente : null;
if (!is_null($paciente) && !is_null($paciente->senha)) {
throw new \InvalidArgumentException('Primeiro acesso já realizado. Utilize a tela de login.', Response::HTTP_UNPROCESSABLE_ENTITY);
}
if (
is_null($cidadao) ||
is_null($paciente) ||
is_null($telefonePrincipal) ||
$cidadao->data_nascimento->format('Y-m-d') !== $dados['data_nascimento'] ||
$telefonePrincipal->numero !== $dados['telefone']
) {
throw new \InvalidArgumentException('Dados não conferem.', Response::HTTP_UNPROCESSABLE_ENTITY);
}
$codigo = (string) random_int(100000, 999999);
$agora = Carbon::now();
$registro = ConectaCidadaoSmsCode::updateOrCreate(
[
'cidadao_id' => $cidadao->id,
'telefone' => $telefonePrincipal->numero,
'usado' => false,
],
[
'codigo' => $codigo,
'expires_at' => $agora->copy()->addMinutes(PrimeiroAcesso::SMS_CODE_TTL_MINUTOS),
'last_sent_at' => $agora,
'tentativas_envio' => 1,
'tentativas_validacao' => 0,
]
);
$mensagem = "Seu código de segurança ConectaSus Cidadão é: {$codigo}";
if (app()->environment('production')) {
(new SmsService())->send($registro->telefone, $mensagem);
}
$retorno = [
'token' => $registro->id,
];
if (!app()->environment('production')) {
$retorno['codigo'] = $codigo;
}
return $retorno;
}
public function reenviarCodigo(int $token): void
{
$registro = ConectaCidadaoSmsCode::findOrFail($token);
if ($registro->usado || $registro->expires_at->isPast()) {
throw new \InvalidArgumentException('Código expirado. Inicie novamente o processo.', Response::HTTP_UNPROCESSABLE_ENTITY);
}
$agora = Carbon::now();
if (!is_null($registro->last_sent_at) && $agora->diffInSeconds($registro->last_sent_at) < PrimeiroAcesso::SMS_REENVIO_INTERVALO_SEGUNDOS) {
throw new \InvalidArgumentException('Aguarde antes de solicitar um novo código.', Response::HTTP_TOO_MANY_REQUESTS);
}
if ($registro->tentativas_envio >= PrimeiroAcesso::SMS_REENVIO_LIMITE) {
throw new \InvalidArgumentException('Limite de reenvios atingido.', Response::HTTP_TOO_MANY_REQUESTS);
}
$registro->tentativas_envio++;
$registro->last_sent_at = $agora;
$registro->save();
$mensagem = "Seu código de segurança ConectaSus Cidadão é: {$registro->codigo}";
if (app()->environment('production')) {
(new SmsService())->send($registro->telefone, $mensagem);
}
}
public function validarCodigo(array $dados): void
{
$registro = ConectaCidadaoSmsCode::findOrFail($dados['token']);
if ($registro->usado || $registro->expires_at->isPast()) {
throw new \InvalidArgumentException('Código expirado. Inicie novamente o processo.', Response::HTTP_UNPROCESSABLE_ENTITY);
}
if ($registro->codigo !== $dados['codigo']) {
$registro->tentativas_validacao++;
$registro->save();
throw new \InvalidArgumentException('Código inválido.', Response::HTTP_UNPROCESSABLE_ENTITY);
}
$registro->usado = true;
$registro->save();
}
public function definirSenha(array $dados): void
{
$registro = ConectaCidadaoSmsCode::findOrFail($dados['token']);
if (!$registro->usado || $registro->expires_at->isPast()) {
throw new \InvalidArgumentException('Fluxo inválido. Valide o código de segurança novamente.', Response::HTTP_UNPROCESSABLE_ENTITY);
}
$cidadao = Cidadao::findOrFail($registro->cidadao_id);
if (is_null($cidadao->paciente)) {
throw new \InvalidArgumentException('Cidadão não encontrado.', Response::HTTP_UNPROCESSABLE_ENTITY);
}
$cidadao->paciente->senha = Hash::make($dados['senha']);
$cidadao->paciente->save();
}
}

View File

@@ -0,0 +1,158 @@
<?php
namespace App\Services\Mobile\ConectaCidadao\Autenticacao;
use App\Enums\Mobile\ConectaCidadao\PrimeiroAcesso;
use App\Models\Cadastro\SujeitoAtencao\Cidadao;
use App\Models\Mobile\ConectaCidadao\ConectaCidadaoSmsCode;
use App\Services\Sms\SmsService;
use Carbon\Carbon;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Hash;
class RecuperacaoSenhaService
{
public function iniciar(string $cpf): array
{
$cidadao = Cidadao::where('cpf', $cpf)->first();
if ($cidadao && empty($cidadao->paciente->senha)) {
throw new \InvalidArgumentException('Cidadão não possui senha cadastrada.', Response::HTTP_UNPROCESSABLE_ENTITY);
}
$telefonePrincipal = $cidadao ? $cidadao->telefone_principal : null;
$paciente = $cidadao ? $cidadao->paciente : null;
if (is_null($cidadao) || is_null($paciente) || is_null($telefonePrincipal)) {
throw new \InvalidArgumentException('Cidadão não encontrado ou sem telefone principal cadastrado.', Response::HTTP_UNPROCESSABLE_ENTITY);
}
$codigo = (string) random_int(100000, 999999);
$agora = Carbon::now();
$registro = ConectaCidadaoSmsCode::updateOrCreate(
[
'cidadao_id' => $cidadao->id,
'telefone' => $telefonePrincipal->numero,
'usado' => false,
],
[
'codigo' => $codigo,
'expires_at' => $agora->copy()->addMinutes(PrimeiroAcesso::SMS_CODE_TTL_MINUTOS),
'last_sent_at' => $agora,
'tentativas_envio' => 1,
'tentativas_validacao' => 0,
]
);
$mensagem = "Seu código de recuperação de senha ConectaSus Cidadão é: {$codigo}";
if (app()->environment('production')) {
(new SmsService())->send($registro->telefone, $mensagem);
}
$retorno = [
'token' => $registro->id,
'telefone' => $this->mascararTelefone($telefonePrincipal->numero),
];
if (!app()->environment('production')) {
$retorno['codigo'] = $codigo;
}
return $retorno;
}
public function reenviarCodigo(int $token): void
{
$registro = ConectaCidadaoSmsCode::findOrFail($token);
if ($registro->usado || $registro->expires_at->isPast()) {
throw new \InvalidArgumentException('Código expirado. Inicie novamente o processo.', Response::HTTP_UNPROCESSABLE_ENTITY);
}
$agora = Carbon::now();
if (!is_null($registro->last_sent_at) && $agora->diffInSeconds($registro->last_sent_at) < PrimeiroAcesso::SMS_REENVIO_INTERVALO_SEGUNDOS) {
throw new \InvalidArgumentException('Aguarde antes de solicitar um novo código.', Response::HTTP_TOO_MANY_REQUESTS);
}
if ($registro->tentativas_envio >= PrimeiroAcesso::SMS_REENVIO_LIMITE) {
throw new \InvalidArgumentException('Limite de reenvios atingido.', Response::HTTP_TOO_MANY_REQUESTS);
}
$registro->tentativas_envio++;
$registro->last_sent_at = $agora;
$registro->save();
$mensagem = "Seu código de recuperação de senha ConectaSus Cidadão é: {$registro->codigo}";
if (app()->environment('production')) {
(new SmsService())->send($registro->telefone, $mensagem);
}
}
public function validarCodigo(array $dados): void
{
$registro = ConectaCidadaoSmsCode::findOrFail($dados['token']);
if ($registro->usado || $registro->expires_at->isPast()) {
throw new \InvalidArgumentException('Código expirado. Inicie novamente o processo.', Response::HTTP_UNPROCESSABLE_ENTITY);
}
if ($registro->codigo !== $dados['codigo']) {
$registro->tentativas_validacao++;
$registro->save();
throw new \InvalidArgumentException('Código inválido.', Response::HTTP_UNPROCESSABLE_ENTITY);
}
$registro->usado = true;
$registro->save();
}
public function redefinirSenha(array $dados): void
{
$registro = ConectaCidadaoSmsCode::findOrFail($dados['token']);
if (!$registro->usado || $registro->expires_at->isPast()) {
throw new \InvalidArgumentException('Fluxo inválido. Valide o código de segurança novamente.', Response::HTTP_UNPROCESSABLE_ENTITY);
}
$cidadao = Cidadao::findOrFail($registro->cidadao_id);
if (is_null($cidadao->paciente)) {
throw new \InvalidArgumentException('Cidadão não encontrado.', Response::HTTP_UNPROCESSABLE_ENTITY);
}
$cidadao->paciente->senha = Hash::make($dados['senha']);
$cidadao->paciente->save();
}
private function mascararTelefone(string $telefone): string
{
$apenasNumeros = preg_replace('/[^0-9]/', '', $telefone);
$tamanho = strlen($apenasNumeros);
if ($tamanho < 8) {
return $telefone;
}
$fimVisivel = substr($apenasNumeros, -4);
$ddd = substr($apenasNumeros, 0, 2);
return "({$ddd}) *****-{$fimVisivel}";
}
}