Commit be7d65ec authored by Nathaniel Catchpole's avatar Nathaniel Catchpole
Browse files

Issue #3225354 by eddie_c, paulocs, Bhanu951, daffie, Spokje, Berdir: Add a...

Issue #3225354 by eddie_c, paulocs, Bhanu951, daffie, Spokje, Berdir: Add a clearByPrefix() method to the flood API
parent 9dc5c66e
......@@ -9,7 +9,7 @@
/**
* Defines the database flood backend. This is the default Drupal backend.
*/
class DatabaseBackend implements FloodInterface {
class DatabaseBackend implements FloodInterface, PrefixFloodInterface {
/**
* The database table name.
......@@ -107,6 +107,21 @@ public function clear($name, $identifier = NULL) {
}
}
/**
* {@inheritdoc}
*/
public function clearByPrefix(string $name, string $prefix): void {
try {
$this->connection->delete(static::TABLE_NAME)
->condition('event', $name)
->condition('identifier', $prefix . '-%', 'LIKE')
->execute();
}
catch (\Exception $e) {
$this->catchException($e);
}
}
/**
* {@inheritdoc}
*/
......
......@@ -21,7 +21,10 @@ interface FloodInterface {
* table from growing indefinitely.
* @param string $identifier
* (optional) Unique identifier of the current user. Defaults to the current
* user's IP address).
* user's IP address. The identifier can be given an additional prefix
* separated by "-". Flood backends may then optionally implement the
* PrefixFloodInterface which allows all flood events that share the same
* prefix to be cleared simultaneously.
*/
public function register($name, $window = 3600, $identifier = NULL);
......
......@@ -7,7 +7,7 @@
/**
* Defines the memory flood backend. This is used for testing.
*/
class MemoryBackend implements FloodInterface {
class MemoryBackend implements FloodInterface, PrefixFloodInterface {
/**
* The request stack.
......@@ -54,6 +54,20 @@ public function clear($name, $identifier = NULL) {
unset($this->events[$name][$identifier]);
}
/**
* {@inheritdoc}
*/
public function clearByPrefix(string $name, string $prefix): void {
foreach ($this->events as $event_name => $identifier) {
$identifier_key = key($identifier);
$identifier_parts = explode("-", $identifier_key);
$identifier_prefix = reset($identifier_parts);
if ($prefix == $identifier_prefix && $name == $event_name) {
unset($this->events[$event_name][$identifier_key]);
}
}
}
/**
* {@inheritdoc}
*/
......
<?php
namespace Drupal\Core\Flood;
/**
* Defines an interface for flood controllers that clear by identifier prefix.
*/
interface PrefixFloodInterface {
/**
* Makes the flood control mechanism forget an event by identifier prefix.
*
* @param string $name
* The name of an event.
* @param string $prefix
* The prefix of the identifier to be cleared.
*/
public function clearByPrefix(string $name, string $prefix): void;
}
......@@ -115,4 +115,41 @@ public function testDatabaseBackend() {
$this->assertFalse($flood->isAllowed($name, $threshold));
}
/**
* Provides an array of backends for testClearByPrefix.
*/
public function floodBackendProvider() :array {
$request_stack = \Drupal::service('request_stack');
$connection = \Drupal::service('database');
return [
new MemoryBackend($request_stack),
new DatabaseBackend($connection, $request_stack),
];
}
/**
* Tests clearByPrefix method on flood backends.
*/
public function testClearByPrefix() {
$threshold = 1;
$window_expired = 3600;
$identifier = 'prefix-127.0.0.1';
$name = 'flood_test_cleanup';
// We can't use an PHPUnit data provider because we need access to the
// container.
$backends = $this->floodBackendProvider();
foreach ($backends as $backend) {
// Register unexpired event.
$backend->register($name, $window_expired, $identifier);
// Verify event is not allowed.
$this->assertFalse($backend->isAllowed($name, $threshold, $window_expired, $identifier));
// Clear by prefix and verify event is now allowed.
$backend->clearByPrefix($name, 'prefix');
$this->assertTrue($backend->isAllowed($name, $threshold));
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment