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,130 @@
<?php
namespace App\Services\Auth;
use App\Models\IpAttempt;
use App\Models\LoginActivity;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
/**
* Mirrors CodeIgniter `AuthController::apiLogin` security behavior:
* IP lockout (ip_attempts), failed_attempts / suspension after 3 failures, login_activity logging.
*/
class ApiLoginSecurityService
{
public function isIpBlocked(string $ip): bool
{
$row = IpAttempt::query()->where('ip_address', $ip)->first();
if (!$row || !$row->blocked_until) {
return false;
}
return $row->blocked_until->isFuture();
}
public function logIpAttempt(string $ip): void
{
$now = utc_now();
$attempt = IpAttempt::query()->where('ip_address', $ip)->first();
if ($attempt) {
$attempts = ((int) $attempt->attempts) + 1;
$blockedUntil = null;
if ($attempts >= 10) {
$blockedUntil = date('Y-m-d H:i:s', strtotime('+24 hours'));
}
$attempt->update([
'attempts' => $attempts,
'last_attempt_at' => $now,
'blocked_until' => $blockedUntil,
]);
return;
}
IpAttempt::query()->create([
'ip_address' => $ip,
'attempts' => 1,
'last_attempt_at' => $now,
]);
}
public function handleFailedLogin(User $user, string $email, string $ip): void
{
$user->refresh();
$failedAttempts = ((int) ($user->failed_attempts ?? 0)) + 1;
$data = [
'failed_attempts' => $failedAttempts,
'last_failed_at' => utc_now(),
];
if ($failedAttempts >= 3) {
$data['is_suspended'] = true;
// CI sends reset email via View\UserController::sendResetEmail — wire Laravel mail here when parity is needed.
Log::notice('api_login: account suspended after failed attempts', ['email' => $email, 'user_id' => $user->id]);
}
$user->fill($data);
$user->save();
$this->logIpAttempt($ip);
}
public function resetFailedAttempts(User $user): void
{
$user->failed_attempts = 0;
$user->last_failed_at = null;
$user->save();
}
public function logSuccessfulLogin(User $user, Request $request): void
{
LoginActivity::query()->create([
'user_id' => $user->id,
'email' => $user->email,
'login_time' => utc_now(),
'ip_address' => $request->ip(),
'user_agent' => (string) ($request->userAgent() ?? ''),
]);
}
/**
* CI returns roles as an object map {"RoleName": true}.
*
* @param list<string> $roleNames
*/
public function rolesToObjectMap(array $roleNames): object
{
$rolesMap = [];
foreach ($roleNames as $r) {
$rolesMap[$r] = true;
}
return (object) $rolesMap;
}
/**
* Top-level JSON body aligned with the CodeIgniter 4 `AuthController::apiLogin` success payload.
*
* @return array{status: true, token: string, user: array{id: int, name: string, roles: object}}
*/
public function buildLoginResponse(User $user, string $jwtToken): array
{
$roleNames = $user->roleNamesLikeCodeIgniter();
$fullName = trim(($user->firstname ?? '').' '.($user->lastname ?? ''));
return [
'status' => true,
'token' => $jwtToken,
'user' => [
'id' => (int) $user->id,
'name' => $fullName,
'roles' => $this->rolesToObjectMap($roleNames),
],
];
}
}