Problem/Motivation

This issue is a follow-up of #2881572: User login flood lock doesn't clear when reset password as admin.
Currently it is not possible to clear the flood based on the flood identifier prefix.

Proposed resolution

Implement a method clearByPrefix to the Flood API so it will be possible to clear a flood register by the identifier prefix.

Issue fork drupal-3225354

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

paulocs created an issue. See original summary.

paulocs’s picture

Assigned: Unassigned » paulocs

On it.

paulocs’s picture

Assigned: paulocs » Unassigned
Status: Active » Needs review

A patch for it.
I'm not sure about MemoryBackend::clearByPrefix.
Please review.

paulocs’s picture

StatusFileSize
new2.2 KB
berdir’s picture

FWIW, this might not be easy to implement for alternative backends. Or at least not efficient. Redis for example.

daffie’s picture

Category: Task » Feature request
Status: Needs review » Needs work
Issue tags: +Needs tests
  1. +++ b/core/lib/Drupal/Core/Flood/FloodInterface.php
    @@ -36,6 +36,16 @@ public function register($name, $window = 3600, $identifier = NULL);
    +  public function clearByPrefix($name, $prefix);
    

    Can we add typehints: Something like: clearByPrefix(string $name, string $prefix): void;

  2. The new method cannot be added to the FloodInterface, because when somebody it overriding the service and they did not add the new method, they will have a BC break.
  3. This new method will need testing.
eddie_c’s picture

StatusFileSize
new3.82 KB

A new patch which includes tests and suggestions from @daffie.

I also made a change to MemoryBackend::clearByPrefix: The second parameter passed to explode() was an array, not a string. It's the key of the array we are interested in exploding I think.

eddie_c’s picture

Status: Needs work » Needs review
daffie’s picture

Issue tags: -Needs tests
StatusFileSize
new1.94 KB

Lets see if a patch with only the added tests fails.

Status: Needs review » Needs work

The last submitted patch, 9: 3225354-7-tests-only-should-fail.patch, failed testing. View results

daffie’s picture

Status: Needs work » Needs review

Back to needs review.
I will do a review later.

@eddie_c: Thanks for writing the tests.

paulocs’s picture

Should we provide a data provider for the test functions created to avoid duplicated code?

I'm talking about:

    $threshold = 1;
    $window_expired = 3600;
    $identifier = 'prefix-127.0.0.1';
    $name = 'flood_test_cleanup'; 
catch’s picture

FWIW, this might not be easy to implement for alternative backends. Or at least not efficient. Redis for example.

This is the same problem we had with cache backends, which eventually led to cache tags.

The use case is #2881572: User login flood lock doesn't clear when reset password as admin where we want to remove flood entries for a user, but the flood entries include their IP address which we don't know.

berdir’s picture

Yeah, and I assume currently we'd be forced to implement similar workarounds like we had to in the memcache module in D7, store prefix deletions and then check them on read.

daffie’s picture

Status: Needs review » Needs work

The patch look good to me. Just a couple of minor remarks:

  1. +++ b/core/lib/Drupal/Core/Flood/DatabaseBackend.php
    @@ -107,6 +107,26 @@ public function clear($name, $identifier = NULL) {
    +        ->condition('identifier', $prefix . '-%', 'LIKE')
    

    Can we change '-%' to '%' or is there a very good reason why the dash is added?

  2. +++ b/core/lib/Drupal/Core/Flood/MemoryBackend.php
    @@ -54,6 +54,23 @@ public function clear($name, $identifier = NULL) {
    +      $identifier_prefix = explode("-", key($identifier));
    

    This looks wrong to me. Should we not do the explode() first and then get the first element with reset()?

  3. +++ b/core/modules/system/tests/src/Kernel/System/FloodTest.php
    @@ -103,4 +103,46 @@ public function testDatabaseBackend() {
    +  public function testMemoryBackendClearByPrefix() {
    ...
    +    $flood = new MemoryBackend($request_stack);
    ...
    +  public function testDatabaseBackendClearByPrefix() {
    ...
    +    $flood = new DatabaseBackend($connection, $request_stack);
    

    The 2 new test method are almost the same. The only difference I can see is MemoryBackend vs. DatabaseBackend. Can we change it to a single test method with a dataProvider method. See: https://phpunit.de/manual/3.7/en/writing-tests-for-phpunit.html#writing-... for more info or do a code base search for "dataProvider".

berdir’s picture

Checking the implementations again. the thing with - is coming from this:

```
MariaDB [d8_import]> select * from flood;
+-----+------------------------+-------------+------------+------------+
| fid | event | identifier | timestamp | expiration |
+-----+------------------------+-------------+------------+------------+
| 3 | user.failed_login_ip | 127.0.0.1 | 1628326866 | 1628330466 |
| 4 | user.failed_login_ip | 127.0.0.1 | 1628326873 | 1628330473 |
| 5 | user.failed_login_user | 1-127.0.0.1 | 1628326873 | 1628348473 |
+-----+------------------------+-------------+------------+------------+
```

The user/ip combination uses - as separator. However, right now, that is an implementation detail, the flood API does, so far, not care at all what's in the identifier.

I like limiting this API somewhat. It makes more more feasible to implement in alternative backends that have no wildcard support. A bit like the memory backend, although we can expect that the number of entries there is very limited and it should IMHO be a lot easier to implement by just checking each array key.

However, in that case, this needs to be more clearly documented. We should document on the existing methods, or somewhere central that the identifier may consist of two sections split by a -. And that flood backends can optionally (at least for BC) support clearing by the first section. We should also put the new method on an optional interface I guess so that implementations can check whether that is supported.

catch’s picture

#16 should help alternative backends a lot, also agreed we should add a new optional interface for this.

eddie_c’s picture

StatusFileSize
new3.69 KB

This patch reduces the duplication in tests. Still need to look at #15 point 2 and @Berdir's suggestion of adding an optional interface.

daffie’s picture

@eddie_c: Could you also post an interdiff file. It makes review your changes a lot easier.

eddie_c’s picture

StatusFileSize
new4.61 KB
new6.03 KB

@daffie Here's an updated patch with interdiff to the patch in #7.

The patch includes:
- The optional interface for the clearByPrefix method
- A correction which addresses your point in #15.2

Still to add: documentation

paulocs’s picture

Unused statement use Drupal\Core\Flood\FloodInterface; in FloodTest.php.

eddie_c’s picture

StatusFileSize
new4.39 KB
new3.49 KB

Removing unused use statement

eddie_c’s picture

StatusFileSize
new3.48 KB
new4.38 KB

...And correcting misspelling in comment

paulocs’s picture

I was working in this issue and I'm not sure about the solution proposed.
I think it makes sense to add that interface because its a block for #2881572: User login flood lock doesn't clear when reset password as admin. The user module set a flood control register that its not possible to be cleared later and its causing the issue.
But maybe the right class to implement that interface its not the MemoryBackend or the DatabaseBackend because we can not ensure that all identifiers will be separated by a hyphen. We are now considering that approach because the user module set the identifier like: $identifier = $account->id() . '-' . $this->getRequest()->getClientIP();

IMHO the right thing to do in this issue is:
1) Create the interface PrefixFloodInterface
2) Let the MemoryBackend and the DatabaseBackend as it is because we can not ensure that the prefix will be always separated with a hyphen because that is not their responsibility.
3) Make the UserFloodControlInterface implement the PrefixFloodInterface
4) Write the method to clear the flood by prefix.

Please let me know if you agree...

paulocs’s picture

Status: Needs work » Needs review
StatusFileSize
new637 bytes
new5.12 KB

I added some documentation in FloodInterface::register().

eddie_c’s picture

Thanks for the docs @paulocs. I wonder if they could be made a little bit clearer. Something like this would expand on what you wrote:

   * @param string $identifier
   *   (optional) Unique identifier of the current user. Defaults to the current
   *   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.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Bhanu951 made their first commit to this issue’s fork.

bhanu951’s picture

Created a MR against 10.x branch and updated the comment description as per #26.

Spokje made their first commit to this issue’s fork.

spokje’s picture

Status: Needs review » Reviewed & tested by the community

Rebased but didn't touch anything else, which (probably?) leaves me eligible to RTBC.

- Code changes make sense to me.
- All remarks made seem to be addressed.
- Green testbot

RTBC for me.

catch’s picture

Issue tags: +Needs change record

This needs a change record for the new interface.

spokje’s picture

Issue tags: -Needs change record

Draft CR added.

spokje’s picture

  • catch committed be7d65ec on 10.1.x
    Issue #3225354 by eddie_c, paulocs, Bhanu951, daffie, Spokje, Berdir:...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Looks good now, hopefully the '-' prefix limitation will help Redis et al implement support for this without too much pain.

Committed be7d65e and pushed to 10.1.x. Thanks!

bhanu951’s picture

Seems we forgot to publish CR. Published it.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.