This commit is contained in:
@@ -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}";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user