forked from nextcloud/server
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
In case no distributed memory cache is specified this adds a database backend for ratelimit purposes. Signed-off-by: Lukas Reschke <lukas@statuscode.ch>
- Loading branch information
1 parent
33a0b75
commit d4f97af
Showing
9 changed files
with
290 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
136 changes: 136 additions & 0 deletions
136
lib/private/Security/RateLimiting/Backend/DatabaseBackend.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters