update api and add more features
This commit is contained in:
@@ -27,6 +27,9 @@ class AuthorizedUsersManagementService
|
||||
|
||||
/**
|
||||
* CI create() first step — lookup by token after invite email (pending row, created within TTL).
|
||||
*
|
||||
* Tokens are stored as SHA-256 hashes; we only ever look up by hash so that
|
||||
* a leaked database row cannot be replayed as a plaintext token.
|
||||
*/
|
||||
public function findPendingByIncomingToken(string $plainToken): ?AuthorizedUser
|
||||
{
|
||||
@@ -34,12 +37,8 @@ class AuthorizedUsersManagementService
|
||||
return null;
|
||||
}
|
||||
|
||||
$hash = $this->hashToken($plainToken);
|
||||
|
||||
return AuthorizedUser::query()
|
||||
->where(function ($q) use ($hash, $plainToken) {
|
||||
$q->where('token', $hash)->orWhere('token', $plainToken);
|
||||
})
|
||||
->where('token', $this->hashToken($plainToken))
|
||||
->where('created_at', '>=', now()->subHours(self::TOKEN_TTL_HOURS))
|
||||
->first();
|
||||
}
|
||||
@@ -49,16 +48,12 @@ class AuthorizedUsersManagementService
|
||||
*/
|
||||
public function findActiveSetupRow(int $authorizedUserId, string $plainToken): ?AuthorizedUser
|
||||
{
|
||||
if ($plainToken === '') {
|
||||
if ($plainToken === '' || $authorizedUserId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$hash = $this->hashToken($plainToken);
|
||||
|
||||
return AuthorizedUser::query()
|
||||
->where(function ($q) use ($hash, $plainToken) {
|
||||
$q->where('token', $hash)->orWhere('token', $plainToken);
|
||||
})
|
||||
->where('token', $this->hashToken($plainToken))
|
||||
->where('authorized_user_id', $authorizedUserId)
|
||||
->where('status', 'Active')
|
||||
->where('updated_at', '>=', now()->subHours(self::TOKEN_TTL_HOURS))
|
||||
@@ -90,6 +85,9 @@ class AuthorizedUsersManagementService
|
||||
/**
|
||||
* CI `create` — when email is not a registered user, respond success without leaking (privacy).
|
||||
*
|
||||
* Refuses self-invites and silently re-uses an existing invite row to avoid
|
||||
* letting a parent spam another user with confirmation emails.
|
||||
*
|
||||
* @return array{created: bool, record: AuthorizedUser|null}
|
||||
*/
|
||||
public function inviteByEmail(int $primaryUserId, string $email): array
|
||||
@@ -104,22 +102,49 @@ class AuthorizedUsersManagementService
|
||||
return ['created' => false, 'record' => null];
|
||||
}
|
||||
|
||||
// A parent cannot invite themselves as their own authorized user.
|
||||
if ((int) $user->id === $primaryUserId) {
|
||||
return ['created' => false, 'record' => null];
|
||||
}
|
||||
|
||||
// If the same parent already invited this user, rotate the token instead
|
||||
// of creating a duplicate row (prevents spam + race-driven duplicates).
|
||||
$existing = AuthorizedUser::query()
|
||||
->where('user_id', $primaryUserId)
|
||||
->where('authorized_user_id', (int) $user->id)
|
||||
->first();
|
||||
|
||||
$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',
|
||||
]);
|
||||
$gender = trim((string) ($user->gender ?? ''));
|
||||
|
||||
if ($existing) {
|
||||
$existing->update([
|
||||
'firstname' => (string) ($user->firstname ?? ''),
|
||||
'lastname' => (string) ($user->lastname ?? ''),
|
||||
'phone_number' => $phone !== '' ? $phone : '-',
|
||||
'gender' => $gender !== '' ? $gender : '-',
|
||||
'email' => $email,
|
||||
'token' => $tokenHash,
|
||||
'status' => 'Pending',
|
||||
]);
|
||||
$record = $existing;
|
||||
} else {
|
||||
$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' => $gender !== '' ? $gender : '-',
|
||||
'email' => $email,
|
||||
'relation_to_user' => null,
|
||||
'token' => $tokenHash,
|
||||
'status' => 'Pending',
|
||||
]);
|
||||
}
|
||||
|
||||
$this->sendConfirmationEmail($email, $plain);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user