133 lines
3.4 KiB
PHP
133 lines
3.4 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Auth;
|
|
|
|
use Illuminate\Auth\Events\Lockout;
|
|
use Illuminate\Cache\RateLimiter;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Str;
|
|
|
|
/**
|
|
* Login throttling, keyed on the submitted username and the client IP.
|
|
*
|
|
* This was laravel/ui's ThrottlesLogins trait plus Vanguard's overrides of it,
|
|
* inlined here so the package is no longer a dependency. The limits come from
|
|
* application settings rather than from properties, which is why maxAttempts()
|
|
* and decayMinutes() are methods.
|
|
*/
|
|
trait ThrottlesLogins
|
|
{
|
|
/**
|
|
* Get the login username to be used by the controller.
|
|
*/
|
|
public function username(): string
|
|
{
|
|
return 'username';
|
|
}
|
|
|
|
/**
|
|
* Determine if the user has too many failed login attempts.
|
|
*/
|
|
protected function hasTooManyLoginAttempts(Request $request): bool
|
|
{
|
|
return $this->limiter()->tooManyAttempts(
|
|
$this->throttleKey($request),
|
|
$this->maxAttempts()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Increment the login attempts for the user.
|
|
*/
|
|
protected function incrementLoginAttempts(Request $request): void
|
|
{
|
|
$this->limiter()->hit(
|
|
$this->throttleKey($request),
|
|
$this->decayMinutes() * 60
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Clear the login locks for the given user credentials.
|
|
*/
|
|
protected function clearLoginAttempts(Request $request): void
|
|
{
|
|
$this->limiter()->clear($this->throttleKey($request));
|
|
}
|
|
|
|
/**
|
|
* Fire an event when a lockout occurs.
|
|
*/
|
|
protected function fireLockoutEvent(Request $request): void
|
|
{
|
|
event(new Lockout($request));
|
|
}
|
|
|
|
/**
|
|
* Determine how many retries are left for the user.
|
|
*/
|
|
protected function retriesLeft(Request $request): int
|
|
{
|
|
$attempts = $this->limiter()->attempts(
|
|
$this->throttleKey($request)
|
|
);
|
|
|
|
return $this->maxAttempts() - $attempts + 1;
|
|
}
|
|
|
|
/**
|
|
* Redirect the user after determining they are locked out.
|
|
*
|
|
* Redirects rather than throwing a 429 ValidationException, so the lockout
|
|
* message lands on the login form like any other error.
|
|
*/
|
|
protected function sendLockoutResponse(Request $request): RedirectResponse
|
|
{
|
|
$seconds = $this->limiter()->availableIn(
|
|
$this->throttleKey($request)
|
|
);
|
|
|
|
return redirect('login')
|
|
->withInput($request->only($this->username(), 'remember'))
|
|
->withErrors([
|
|
$this->username() => $this->getLockoutErrorMessage($seconds),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get the login lockout error message.
|
|
*/
|
|
protected function getLockoutErrorMessage(int $seconds): string
|
|
{
|
|
return trans('auth.throttle', ['seconds' => $seconds]);
|
|
}
|
|
|
|
/**
|
|
* Get the throttle key for the given request.
|
|
*/
|
|
protected function throttleKey(Request $request): string
|
|
{
|
|
return Str::transliterate(
|
|
Str::lower($request->input($this->username())).'|'.$request->ip()
|
|
);
|
|
}
|
|
|
|
protected function limiter(): RateLimiter
|
|
{
|
|
return app(RateLimiter::class);
|
|
}
|
|
|
|
protected function maxAttempts(): int
|
|
{
|
|
return (int) setting('throttle_attempts', 5);
|
|
}
|
|
|
|
protected function decayMinutes(): int
|
|
{
|
|
$lockout = (int) setting('throttle_lockout_time');
|
|
|
|
return max($lockout, 1);
|
|
}
|
|
}
|