update controllers logic

This commit is contained in:
root
2026-04-23 00:04:35 -04:00
parent 1977a513df
commit ca4ba272fc
353 changed files with 13402 additions and 1301 deletions
@@ -0,0 +1,166 @@
<?php
namespace App\Services\Parents;
use App\Models\AuthorizedUser;
use App\Models\User;
use App\Services\ApplicationUrlService;
use App\Services\Email\EmailDispatchService;
use Illuminate\Support\Facades\DB;
/**
* CodeIgniter {@see \App\Controllers\View\AuthorizedUsersController} parity.
*/
class AuthorizedUsersManagementService
{
private const TOKEN_TTL_HOURS = 24;
public function __construct(
private EmailDispatchService $mail,
private ApplicationUrlService $urls,
) {}
public function hashToken(string $plain): string
{
return hash('sha256', $plain);
}
/**
* CI create() first step — lookup by token after invite email (pending row, created within TTL).
*/
public function findPendingByIncomingToken(string $plainToken): ?AuthorizedUser
{
if ($plainToken === '') {
return null;
}
$hash = $this->hashToken($plainToken);
return AuthorizedUser::query()
->where(function ($q) use ($hash, $plainToken) {
$q->where('token', $hash)->orWhere('token', $plainToken);
})
->where('created_at', '>=', now()->subHours(self::TOKEN_TTL_HOURS))
->first();
}
/**
* CI setPassword / savePassword — active row with rotating token.
*/
public function findActiveSetupRow(int $authorizedUserId, string $plainToken): ?AuthorizedUser
{
if ($plainToken === '') {
return null;
}
$hash = $this->hashToken($plainToken);
return AuthorizedUser::query()
->where(function ($q) use ($hash, $plainToken) {
$q->where('token', $hash)->orWhere('token', $plainToken);
})
->where('authorized_user_id', $authorizedUserId)
->where('status', 'Active')
->where('updated_at', '>=', now()->subHours(self::TOKEN_TTL_HOURS))
->first();
}
/**
* @return array{redirect_url: string}
*/
public function confirmEmailClick(string $plainToken): array
{
$row = $this->findPendingByIncomingToken($plainToken);
if (! $row) {
throw new \InvalidArgumentException('Invalid or expired confirmation link.');
}
$nextPlain = bin2hex(random_bytes(48));
$nextHash = $this->hashToken($nextPlain);
$row->status = 'Active';
$row->token = $nextHash;
$row->save();
$url = $this->urls->inviteSetPasswordFormUrl((int) $row->authorized_user_id, $nextPlain);
return ['redirect_url' => $url];
}
/**
* CI `create` — when email is not a registered user, respond success without leaking (privacy).
*
* @return array{created: bool, record: AuthorizedUser|null}
*/
public function inviteByEmail(int $primaryUserId, string $email): array
{
$email = strtolower(trim($email));
$user = User::query()
->whereRaw('LOWER(email) = ?', [$email])
->first();
if (! $user) {
return ['created' => false, 'record' => null];
}
$plain = bin2hex(random_bytes(48));
$tokenHash = $this->hashToken($plain);
$phone = trim((string) ($user->cellphone ?? ''));
$record = AuthorizedUser::query()->create([
'user_id' => $primaryUserId,
'authorized_user_id' => (int) $user->id,
'firstname' => (string) ($user->firstname ?? ''),
'lastname' => (string) ($user->lastname ?? ''),
'phone_number' => $phone !== '' ? $phone : '-',
'gender' => trim((string) ($user->gender ?? '')) !== '' ? (string) $user->gender : '-',
'email' => $email,
'relation_to_user' => null,
'token' => $tokenHash,
'status' => 'Pending',
]);
$this->sendConfirmationEmail($email, $plain);
return ['created' => true, 'record' => $record];
}
public function sendConfirmationEmail(string $email, string $plainToken): void
{
$confirmLink = $this->urls->inviteConfirmUrl($plainToken);
$body = '<p>You have been added as an authorized user for another account. Click the link below to confirm your access:</p>'
.'<p><a href="'.e($confirmLink).'">Confirm Access</a></p>'
.'<p>If you did not request this, please ignore this email.</p>';
$this->mail->send($email, 'Authorized User Confirmation', $body, 'notifications');
}
/**
* @throws \InvalidArgumentException
*/
public function setAuthorizedUserPassword(int $authorizedUserId, int $postedUserId, string $plainToken, string $password): void
{
if ($postedUserId !== $authorizedUserId || $authorizedUserId <= 0) {
throw new \InvalidArgumentException('Invalid request.');
}
$row = $this->findActiveSetupRow($authorizedUserId, $plainToken);
if (! $row) {
throw new \InvalidArgumentException('Invalid or expired confirmation link.');
}
$user = User::query()->find($authorizedUserId);
if (! $user) {
throw new \InvalidArgumentException('User not found.');
}
DB::transaction(function () use ($user, $row, $password) {
$user->password = $password;
$user->save();
$row->token = null;
$row->save();
});
}
}