diff options
Diffstat (limited to 'lib/private/Security/RateLimiting/Limiter.php')
-rw-r--r-- | lib/private/Security/RateLimiting/Limiter.php | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/lib/private/Security/RateLimiting/Limiter.php b/lib/private/Security/RateLimiting/Limiter.php new file mode 100644 index 00000000000..316becfa009 --- /dev/null +++ b/lib/private/Security/RateLimiting/Limiter.php @@ -0,0 +1,81 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Security\RateLimiting; + +use OC\Security\Normalizer\IpAddress; +use OC\Security\RateLimiting\Backend\IBackend; +use OC\Security\RateLimiting\Exception\RateLimitExceededException; +use OCP\IUser; +use OCP\Security\RateLimiting\ILimiter; +use Psr\Log\LoggerInterface; + +class Limiter implements ILimiter { + public function __construct( + private IBackend $backend, + private LoggerInterface $logger, + ) { + } + + /** + * @param int $period in seconds + * @throws RateLimitExceededException + */ + private function register( + string $methodIdentifier, + string $userIdentifier, + int $period, + int $limit, + ): void { + $existingAttempts = $this->backend->getAttempts($methodIdentifier, $userIdentifier); + if ($existingAttempts >= $limit) { + $this->logger->info('Request blocked because it exceeds the rate limit [method: {method}, limit: {limit}, period: {period}]', [ + 'method' => $methodIdentifier, + 'limit' => $limit, + 'period' => $period, + ]); + throw new RateLimitExceededException(); + } + + $this->backend->registerAttempt($methodIdentifier, $userIdentifier, $period); + } + + /** + * Registers attempt for an anonymous request + * + * @param int $anonPeriod in seconds + * @throws RateLimitExceededException + */ + public function registerAnonRequest( + string $identifier, + int $anonLimit, + int $anonPeriod, + string $ip, + ): void { + $ipSubnet = (new IpAddress($ip))->getSubnet(); + + $anonHashIdentifier = hash('sha512', 'anon::' . $identifier . $ipSubnet); + $this->register($identifier, $anonHashIdentifier, $anonPeriod, $anonLimit); + } + + /** + * Registers attempt for an authenticated request + * + * @param int $userPeriod in seconds + * @throws RateLimitExceededException + */ + public function registerUserRequest( + string $identifier, + int $userLimit, + int $userPeriod, + IUser $user, + ): void { + $userHashIdentifier = hash('sha512', 'user::' . $identifier . $user->getUID()); + $this->register($identifier, $userHashIdentifier, $userPeriod, $userLimit); + } +} |