TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\RateLimiter\LimiterInterface;
|
||||
use TYPO3\CMS\Core\Authentication\Event\AfterUserLoggedInEvent;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use TYPO3\CMS\Core\RateLimiter\RateLimiterFactoryInterface;
|
||||
use TYPO3\CMS\Core\RateLimiter\RequestRateLimitedException;
|
||||
use TYPO3\CMS\Core\Session\UserSessionManager;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\HttpUtility;
|
||||
use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
|
||||
|
||||
/**
|
||||
* This middleware authenticates a Frontend User (fe_users).
|
||||
*/
|
||||
readonly class FrontendUserAuthenticator implements MiddlewareInterface
|
||||
{
|
||||
public function __construct(
|
||||
protected Context $context,
|
||||
protected RateLimiterFactoryInterface $rateLimiterFactory,
|
||||
protected EventDispatcherInterface $eventDispatcher,
|
||||
protected LoggerInterface $logger,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Creates a frontend user authentication object, tries to authenticate a user and stores
|
||||
* it in the current request as attribute.
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$frontendUser = GeneralUtility::makeInstance(FrontendUserAuthentication::class);
|
||||
|
||||
// Rate Limiting
|
||||
$rateLimiter = $this->ensureLoginRateLimit($frontendUser, $request);
|
||||
|
||||
// Authenticate now
|
||||
$frontendUser->start($request);
|
||||
// no matter if we have an active user we try to fetch matching groups which can
|
||||
// be set without an user (simulation for instance!)
|
||||
$frontendUser->fetchGroupData($request);
|
||||
|
||||
// Register the frontend user as aspect and within the request
|
||||
$this->context->setAspect('frontend.user', $frontendUser->createUserAspect());
|
||||
$request = $request->withAttribute('frontend.user', $frontendUser);
|
||||
|
||||
if ($this->context->getAspect('frontend.user')->isLoggedIn() && $rateLimiter) {
|
||||
$rateLimiter->reset();
|
||||
$this->eventDispatcher->dispatch(new AfterUserLoggedInEvent($frontendUser, $request));
|
||||
}
|
||||
|
||||
$response = $handler->handle($request);
|
||||
|
||||
// Store session data for fe_users
|
||||
$frontendUser->storeSessionData();
|
||||
$response = $frontendUser->appendCookieToResponse($response, $request->getAttribute('normalizedParams'));
|
||||
// Collect garbage in Frontend requests, which aren't fully cacheable (e.g. with cookies)
|
||||
if ($response->hasHeader('Set-Cookie')) {
|
||||
$this->sessionGarbageCollection();
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Garbage collection for fe_sessions (with a probability)
|
||||
*/
|
||||
protected function sessionGarbageCollection(): void
|
||||
{
|
||||
UserSessionManager::create('FE')->collectGarbage();
|
||||
}
|
||||
|
||||
protected function ensureLoginRateLimit(FrontendUserAuthentication $user, ServerRequestInterface $request): ?LimiterInterface
|
||||
{
|
||||
if (!$user->isActiveLogin($request)) {
|
||||
return null;
|
||||
}
|
||||
$loginRateLimiter = $this->rateLimiterFactory->createLoginRateLimiter($request, $user->loginType);
|
||||
$limit = $loginRateLimiter->consume();
|
||||
if (!$limit->isAccepted()) {
|
||||
$this->logger->debug('Login request has been rate limited for IP address {ipAddress}', ['ipAddress' => $request->getAttribute('normalizedParams')->getRemoteAddress()]);
|
||||
$dateformat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
|
||||
$lockedUntil = $limit->getRetryAfter()->getTimestamp() > 0
|
||||
? ' until ' . date($dateformat, $limit->getRetryAfter()->getTimestamp()) : '';
|
||||
throw new RequestRateLimitedException(
|
||||
HttpUtility::HTTP_STATUS_403,
|
||||
'The login is locked' . $lockedUntil . ' due to too many failed login attempts from your IP address.',
|
||||
'Login Request Rate Limited',
|
||||
1616175847
|
||||
);
|
||||
}
|
||||
return $loginRateLimiter;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user