Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Reschke <lukas@statuscode.ch>2021-09-06 17:31:01 +0300
committerLukas Reschke <lukas@statuscode.ch>2021-09-06 17:31:01 +0300
commitd4f97affc1a0ecfaacfbdc26aab820cad1650a06 (patch)
treeaa77abd5adf02edd735159cc3c0c07cf7fd4c847
parent33a0b75c83a1c56fa84b98d3a07a26b5c4932b65 (diff)
Add database ratelimiting backend
In case no distributed memory cache is specified this adds a database backend for ratelimit purposes. Signed-off-by: Lukas Reschke <lukas@statuscode.ch>
-rw-r--r--core/Migrations/Version23000Date20210906132259.php47
-rw-r--r--lib/composer/composer/autoload_classmap.php6
-rw-r--r--lib/composer/composer/autoload_psr4.php1
-rw-r--r--lib/composer/composer/autoload_static.php14
-rw-r--r--lib/composer/composer/installed.json62
-rw-r--r--lib/composer/composer/installed.php15
-rw-r--r--lib/private/Security/RateLimiting/Backend/DatabaseBackend.php136
-rw-r--r--lib/private/Server.php18
-rw-r--r--version.php2
9 files changed, 290 insertions, 11 deletions
diff --git a/core/Migrations/Version23000Date20210906132259.php b/core/Migrations/Version23000Date20210906132259.php
new file mode 100644
index 00000000000..b0eb7bc4dcc
--- /dev/null
+++ b/core/Migrations/Version23000Date20210906132259.php
@@ -0,0 +1,47 @@
+<?php
+
+declare(strict_types=1);
+
+namespace OC\Core\Migrations;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+/**
+ * Auto-generated migration step: Please modify to your needs!
+ */
+class Version23000Date20210906132259 extends SimpleMigrationStep {
+ private const TABLE_NAME = 'ratelimit_entries';
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ $hasTable = $schema->hasTable(self::TABLE_NAME);
+
+ if(!$hasTable)
+ {
+ $table = $schema->createTable(self::TABLE_NAME);
+ $table->addColumn('hash', Types::STRING, [
+ 'notnull' => true,
+ 'length' => 128,
+ ]);
+ $table->addColumn('timestamp', 'datetime', [
+ 'notnull' => true,
+ ]);
+ $table->addIndex(['hash'], 'ratelimit_hash_idx');
+ $table->addIndex(['timestamp'], 'ratelimit_timestamp_idx');
+ }
+
+ return $schema;
+ }
+}
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index ea1473f27dc..ca0607a5d35 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -6,6 +6,10 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname(dirname($vendorDir));
return array(
+ 'Bamarni\\Composer\\Bin\\BinCommand' => $vendorDir . '/bamarni/composer-bin-plugin/src/BinCommand.php',
+ 'Bamarni\\Composer\\Bin\\CommandProvider' => $vendorDir . '/bamarni/composer-bin-plugin/src/CommandProvider.php',
+ 'Bamarni\\Composer\\Bin\\Config' => $vendorDir . '/bamarni/composer-bin-plugin/src/Config.php',
+ 'Bamarni\\Composer\\Bin\\Plugin' => $vendorDir . '/bamarni/composer-bin-plugin/src/Plugin.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'OCP\\Accounts\\IAccount' => $baseDir . '/lib/public/Accounts/IAccount.php',
'OCP\\Accounts\\IAccountManager' => $baseDir . '/lib/public/Accounts/IAccountManager.php',
@@ -969,6 +973,7 @@ return array(
'OC\\Core\\Migrations\\Version21000Date20210309185126' => $baseDir . '/core/Migrations/Version21000Date20210309185126.php',
'OC\\Core\\Migrations\\Version21000Date20210309185127' => $baseDir . '/core/Migrations/Version21000Date20210309185127.php',
'OC\\Core\\Migrations\\Version22000Date20210216080825' => $baseDir . '/core/Migrations/Version22000Date20210216080825.php',
+ 'OC\\Core\\Migrations\\Version23000Date20210906132259' => $baseDir . '/core/Migrations/Version23000Date20210906132259.php',
'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php',
'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php',
@@ -1365,6 +1370,7 @@ return array(
'OC\\Security\\IdentityProof\\Manager' => $baseDir . '/lib/private/Security/IdentityProof/Manager.php',
'OC\\Security\\IdentityProof\\Signer' => $baseDir . '/lib/private/Security/IdentityProof/Signer.php',
'OC\\Security\\Normalizer\\IpAddress' => $baseDir . '/lib/private/Security/Normalizer/IpAddress.php',
+ 'OC\\Security\\RateLimiting\\Backend\\DatabaseBackend' => $baseDir . '/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php',
'OC\\Security\\RateLimiting\\Backend\\IBackend' => $baseDir . '/lib/private/Security/RateLimiting/Backend/IBackend.php',
'OC\\Security\\RateLimiting\\Backend\\MemoryCache' => $baseDir . '/lib/private/Security/RateLimiting/Backend/MemoryCache.php',
'OC\\Security\\RateLimiting\\Exception\\RateLimitExceededException' => $baseDir . '/lib/private/Security/RateLimiting/Exception/RateLimitExceededException.php',
diff --git a/lib/composer/composer/autoload_psr4.php b/lib/composer/composer/autoload_psr4.php
index b641d9c6a03..fe0f6a5fed2 100644
--- a/lib/composer/composer/autoload_psr4.php
+++ b/lib/composer/composer/autoload_psr4.php
@@ -9,5 +9,6 @@ return array(
'OC\\Core\\' => array($baseDir . '/core'),
'OC\\' => array($baseDir . '/lib/private'),
'OCP\\' => array($baseDir . '/lib/public'),
+ 'Bamarni\\Composer\\Bin\\' => array($vendorDir . '/bamarni/composer-bin-plugin/src'),
'' => array($baseDir . '/lib/private/legacy'),
);
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 93208f6ff15..98633252c40 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -13,6 +13,10 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\' => 3,
'OCP\\' => 4,
),
+ 'B' =>
+ array (
+ 'Bamarni\\Composer\\Bin\\' => 21,
+ ),
);
public static $prefixDirsPsr4 = array (
@@ -28,6 +32,10 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
array (
0 => __DIR__ . '/../../..' . '/lib/public',
),
+ 'Bamarni\\Composer\\Bin\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src',
+ ),
);
public static $fallbackDirsPsr4 = array (
@@ -35,6 +43,10 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
);
public static $classMap = array (
+ 'Bamarni\\Composer\\Bin\\BinCommand' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/BinCommand.php',
+ 'Bamarni\\Composer\\Bin\\CommandProvider' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/CommandProvider.php',
+ 'Bamarni\\Composer\\Bin\\Config' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/Config.php',
+ 'Bamarni\\Composer\\Bin\\Plugin' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/Plugin.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'OCP\\Accounts\\IAccount' => __DIR__ . '/../../..' . '/lib/public/Accounts/IAccount.php',
'OCP\\Accounts\\IAccountManager' => __DIR__ . '/../../..' . '/lib/public/Accounts/IAccountManager.php',
@@ -998,6 +1010,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Core\\Migrations\\Version21000Date20210309185126' => __DIR__ . '/../../..' . '/core/Migrations/Version21000Date20210309185126.php',
'OC\\Core\\Migrations\\Version21000Date20210309185127' => __DIR__ . '/../../..' . '/core/Migrations/Version21000Date20210309185127.php',
'OC\\Core\\Migrations\\Version22000Date20210216080825' => __DIR__ . '/../../..' . '/core/Migrations/Version22000Date20210216080825.php',
+ 'OC\\Core\\Migrations\\Version23000Date20210906132259' => __DIR__ . '/../../..' . '/core/Migrations/Version23000Date20210906132259.php',
'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php',
'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php',
@@ -1394,6 +1407,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Security\\IdentityProof\\Manager' => __DIR__ . '/../../..' . '/lib/private/Security/IdentityProof/Manager.php',
'OC\\Security\\IdentityProof\\Signer' => __DIR__ . '/../../..' . '/lib/private/Security/IdentityProof/Signer.php',
'OC\\Security\\Normalizer\\IpAddress' => __DIR__ . '/../../..' . '/lib/private/Security/Normalizer/IpAddress.php',
+ 'OC\\Security\\RateLimiting\\Backend\\DatabaseBackend' => __DIR__ . '/../../..' . '/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php',
'OC\\Security\\RateLimiting\\Backend\\IBackend' => __DIR__ . '/../../..' . '/lib/private/Security/RateLimiting/Backend/IBackend.php',
'OC\\Security\\RateLimiting\\Backend\\MemoryCache' => __DIR__ . '/../../..' . '/lib/private/Security/RateLimiting/Backend/MemoryCache.php',
'OC\\Security\\RateLimiting\\Exception\\RateLimitExceededException' => __DIR__ . '/../../..' . '/lib/private/Security/RateLimiting/Exception/RateLimitExceededException.php',
diff --git a/lib/composer/composer/installed.json b/lib/composer/composer/installed.json
index f20a6c47c6d..9c4acdfa483 100644
--- a/lib/composer/composer/installed.json
+++ b/lib/composer/composer/installed.json
@@ -1,5 +1,61 @@
{
- "packages": [],
- "dev": false,
- "dev-package-names": []
+ "packages": [
+ {
+ "name": "bamarni/composer-bin-plugin",
+ "version": "1.4.1",
+ "version_normalized": "1.4.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/bamarni/composer-bin-plugin.git",
+ "reference": "9329fb0fbe29e0e1b2db8f4639a193e4f5406225"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/9329fb0fbe29e0e1b2db8f4639a193e4f5406225",
+ "reference": "9329fb0fbe29e0e1b2db8f4639a193e4f5406225",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^1.0 || ^2.0",
+ "php": "^5.5.9 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "composer/composer": "^1.0 || ^2.0",
+ "symfony/console": "^2.5 || ^3.0 || ^4.0"
+ },
+ "time": "2020-05-03T08:27:20+00:00",
+ "type": "composer-plugin",
+ "extra": {
+ "class": "Bamarni\\Composer\\Bin\\Plugin"
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Bamarni\\Composer\\Bin\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "No conflicts for your bin dependencies",
+ "keywords": [
+ "composer",
+ "conflict",
+ "dependency",
+ "executable",
+ "isolation",
+ "tool"
+ ],
+ "support": {
+ "issues": "https://github.com/bamarni/composer-bin-plugin/issues",
+ "source": "https://github.com/bamarni/composer-bin-plugin/tree/master"
+ },
+ "install-path": "../bamarni/composer-bin-plugin"
+ }
+ ],
+ "dev": true,
+ "dev-package-names": [
+ "bamarni/composer-bin-plugin"
+ ]
}
diff --git a/lib/composer/composer/installed.php b/lib/composer/composer/installed.php
index b811738fbd0..583c0c32593 100644
--- a/lib/composer/composer/installed.php
+++ b/lib/composer/composer/installed.php
@@ -5,9 +5,9 @@
'type' => 'library',
'install_path' => __DIR__ . '/../../../',
'aliases' => array(),
- 'reference' => '66144c300395458ff38b86e50cd92174443cd85e',
+ 'reference' => '33a0b75c83a1c56fa84b98d3a07a26b5c4932b65',
'name' => '__root__',
- 'dev' => false,
+ 'dev' => true,
),
'versions' => array(
'__root__' => array(
@@ -16,8 +16,17 @@
'type' => 'library',
'install_path' => __DIR__ . '/../../../',
'aliases' => array(),
- 'reference' => '66144c300395458ff38b86e50cd92174443cd85e',
+ 'reference' => '33a0b75c83a1c56fa84b98d3a07a26b5c4932b65',
'dev_requirement' => false,
),
+ 'bamarni/composer-bin-plugin' => array(
+ 'pretty_version' => '1.4.1',
+ 'version' => '1.4.1.0',
+ 'type' => 'composer-plugin',
+ 'install_path' => __DIR__ . '/../bamarni/composer-bin-plugin',
+ 'aliases' => array(),
+ 'reference' => '9329fb0fbe29e0e1b2db8f4639a193e4f5406225',
+ 'dev_requirement' => true,
+ ),
),
);
diff --git a/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php b/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php
new file mode 100644
index 00000000000..a7f2a7b4b62
--- /dev/null
+++ b/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php
@@ -0,0 +1,136 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2021 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @author Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+namespace OC\Security\RateLimiting\Backend;
+
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+
+/**
+ * Class DatabaseBackend uses the database for storing rate limiting data.
+ *
+ * @package OC\Security\RateLimiting\Backend
+ */
+class DatabaseBackend implements IBackend {
+ private const TABLE_NAME = 'ratelimit_entries';
+
+ /** @var IDBConnection */
+ private $dbConnection;
+ /** @var ITimeFactory */
+ private $timeFactory;
+
+ /**
+ * @param IDBConnection $dbConnection
+ * @param ITimeFactory $timeFactory
+ */
+ public function __construct(
+ IDBConnection $dbConnection,
+ ITimeFactory $timeFactory
+ ) {
+ $this->dbConnection = $dbConnection;
+ $this->timeFactory = $timeFactory;
+ }
+
+ /**
+ * @param string $methodIdentifier
+ * @param string $userIdentifier
+ * @return string
+ */
+ private function hash(string $methodIdentifier,
+ string $userIdentifier): string {
+ return hash('sha512', $methodIdentifier . $userIdentifier);
+ }
+
+ /**
+ * @param string $identifier
+ * @param int $seconds
+ * @return int
+ * @throws \OCP\DB\Exception
+ */
+ private function getExistingAttemptCount(
+ string $identifier,
+ int $seconds
+ ): int {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $notOlderThan = $this->timeFactory->getDateTime()->sub(new \DateInterval("PT{$seconds}S"));
+
+ $qb->selectAlias($qb->createFunction('COUNT(*)'), 'count')
+ ->from(self::TABLE_NAME)
+ ->where(
+ $qb->expr()->eq('hash', $qb->createNamedParameter($identifier, IQueryBuilder::PARAM_STR))
+ )
+ ->andWhere(
+ $qb->expr()->gte('timestamp', $qb->createParameter('notOlderThan'))
+ )
+ ->setParameter('notOlderThan', $notOlderThan, 'datetime');
+
+ $cursor = $qb->executeQuery();
+ $row = $cursor->fetch();
+ $cursor->closeCursor();
+
+ return (int)$row['count'];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getAttempts(string $methodIdentifier,
+ string $userIdentifier,
+ int $seconds): int {
+ $identifier = $this->hash($methodIdentifier, $userIdentifier);
+ return $this->getExistingAttemptCount($identifier, $seconds);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function registerAttempt(string $methodIdentifier,
+ string $userIdentifier,
+ int $period) {
+ $identifier = $this->hash($methodIdentifier, $userIdentifier);
+ $currentTime = $this->timeFactory->getDateTime();
+ $notOlderThan = $this->timeFactory->getDateTime('@' . $period);
+
+ $qb = $this->dbConnection->getQueryBuilder();
+
+ $qb->delete(self::TABLE_NAME)
+ ->where(
+ $qb->expr()->eq('hash', $qb->createNamedParameter($identifier, IQueryBuilder::PARAM_STR))
+ )
+ ->andWhere(
+ $qb->expr()->lt('timestamp', $qb->createParameter('notOlderThan'))
+ )
+ ->setParameter('notOlderThan', $notOlderThan, 'datetime')
+ ->executeStatement();
+
+ $qb->insert(self::TABLE_NAME)
+ ->values([
+ 'hash' => $qb->createNamedParameter($identifier, IQueryBuilder::PARAM_STR),
+ 'timestamp' => $qb->createNamedParameter($currentTime, IQueryBuilder::PARAM_DATE),
+ ])
+ ->executeStatement();
+ }
+}
diff --git a/lib/private/Server.php b/lib/private/Server.php
index 0320eda2b91..83a3c905f94 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -785,10 +785,20 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerDeprecatedAlias('Search', ISearch::class);
$this->registerService(\OC\Security\RateLimiting\Backend\IBackend::class, function ($c) {
- return new \OC\Security\RateLimiting\Backend\MemoryCache(
- $this->get(ICacheFactory::class),
- new \OC\AppFramework\Utility\TimeFactory()
- );
+ $cacheFactory = $c->get(ICacheFactory::class);
+ if ($cacheFactory->isAvailable()) {
+ $backend = new \OC\Security\RateLimiting\Backend\MemoryCache(
+ $this->get(ICacheFactory::class),
+ new \OC\AppFramework\Utility\TimeFactory()
+ );
+ } else {
+ $backend = new \OC\Security\RateLimiting\Backend\DatabaseBackend(
+ $c->get(IDBConnection::class),
+ new \OC\AppFramework\Utility\TimeFactory()
+ );
+ }
+
+ return $backend;
});
$this->registerAlias(\OCP\Security\ISecureRandom::class, SecureRandom::class);
diff --git a/version.php b/version.php
index 0f7f176f04c..045fa2866b6 100644
--- a/version.php
+++ b/version.php
@@ -30,7 +30,7 @@
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
// when updating major/minor version number.
-$OC_Version = [23, 0, 0, 0];
+$OC_Version = [23, 0, 0, 1];
// The human readable string
$OC_VersionString = '23.0.0 alpha';