aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Security/RateLimiting/Limiter.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/Security/RateLimiting/Limiter.php')
-rw-r--r--lib/private/Security/RateLimiting/Limiter.php81
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);
+ }
+}