Unhandled exception when trying to register a duplicate username with different case

Created on 18 January 2024, 5 months ago
Updated 7 April 2024, 3 months ago

Problem/Motivation

When registering a duplicate user name, the registration should be gracefully blocked when user name is the same case insensitively
This is similar to https://www.drupal.org/project/drupal/issues/2502021 β†’

Steps to reproduce

  • Go to user/register and register an account named 'example'. (note the lowercase e as the first character)
  • Go back to user/register and register an account named 'Example' (note the uppercase E as the first character).

This results in a WSD with the message "The website encountered an unexpected error. Try again later."

The following 'user' type error is logged:
Drupal\Core\Database\IntegrityConstraintViolationException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'Example-en' for key 'user__name': INSERT INTO "users_field_data" ("uid", "langcode", "preferred_langcode", "preferred_admin_langcode", "name", "pass", "mail", "timezone", "status", "created", "changed", "access", "login", "init", "default_langcode") VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9, :db_insert_placeholder_10, :db_insert_placeholder_11, :db_insert_placeholder_12, :db_insert_placeholder_13, :db_insert_placeholder_14); Array ( [:db_insert_placeholder_0] => 3319 [:db_insert_placeholder_1] => en [:db_insert_placeholder_2] => en [:db_insert_placeholder_3] => en [:db_insert_placeholder_4] => Example [:db_insert_placeholder_5] => [REMOVED] [:db_insert_placeholder_6] => example2@example.com [:db_insert_placeholder_7] => Europe/London [:db_insert_placeholder_8] => 1 [:db_insert_placeholder_9] => 1705591756 [:db_insert_placeholder_10] => 1705591756 [:db_insert_placeholder_11] => 0 [:db_insert_placeholder_12] => 0 [:db_insert_placeholder_13] => example2@example.com [:db_insert_placeholder_14] => 1 ) in Drupal\mysql\Driver\Database\mysql\ExceptionHandler->handleExecutionException() (line 45 of /code/web/core/modules/mysql/src/Driver/Database/mysql/ExceptionHandler.php).

And a similar 'php' type error is also logged:
Drupal\Core\Entity\EntityStorageException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'Example-en' for key 'user__name': INSERT INTO .........

Proposed resolution

Drupal displays an error message on the registration form, which should be "The username Example is already taken."

Drupal v10.2.1

πŸ› Bug report
Status

Fixed

Version

10.2 ✨

Component
User moduleΒ  β†’

Last updated less than a minute ago

Created by

πŸ‡¬πŸ‡§United Kingdom Matt B

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • Issue created by @Matt B
  • πŸ‡ΊπŸ‡ΈUnited States cilefen

    There is no evident data corruption or other signifiers of critical priority β†’ so this is probably major. I am adjusting that.

  • πŸ‡¬πŸ‡§United Kingdom Matt B

    Fair enough, but for me a WSD when someone tries to register renders a site unusable

    I wanted to rule out any contrib module on my site causing this issue, so I stood up a fresh v10.2.2 standard install on Pantheon and am able to replicated the issue.

  • πŸ‡ΊπŸ‡ΈUnited States cilefen

    The critical priority guideline means most or all of the site would be unusable (not the case) and have no workaround (another username is a workaround). Also, #2502021: Unhandled exception when trying to register a duplicate user β†’ was major priority and is essentially the same.

  • πŸ‡ΊπŸ‡ΈUnited States cilefen

    LOL, I just noticed I was the reporter of #2502021: Unhandled exception when trying to register a duplicate user β†’ and raised it from normal to major.

  • πŸ‡¬πŸ‡§United Kingdom Matt B

    Thanks, and understood on the priorities. 9 years and it comes back to haunt you!

    I have also checked I can log in as the original user as 'Example' or 'example' (didn't check any other variations) so the username is tried in a case insensitive manner on login, but not on registration.

  • πŸ‡ΊπŸ‡ΈUnited States cilefen

    Database string type fields are usually case-insensitive for lookups, which is why.

  • I believe this is heavily related to the changes introduced by https://www.drupal.org/node/3372085 β†’ . It seems like the UniqueFieldValueValidator is no longer case-insensitive.

    Furthermore, this also introduces another problem: You can now create multiple user accounts with the same e-mail address if you use a different case since the validator does no longer detect that there is an account with the same e-mail address, for example you can create one account with user@example.com and one with User@example.com.

  • What may need to happen is that this condition get updated to build out a 'LIKE' condition for each item rather than simply an 'IN' condition.

    Building a 'LIKE' condition in Drupal

    Note that you can't combine 'LIKE' and 'IN', but a loop should make short work of it.

    The tests added in πŸ“Œ UniqueFieldValueValidator works only with single value fields Fixed should be easy to expand to check for case insensitivity.

  • @caesius I did some debugging on the code and, actually, it seems like the condition you mentioned leads to correct results. Only this statement seems to get rid of duplicates. For example, when already having an account "example" and then trying to create an account "Example", $other_entity_values contains "example", which is what we want, right? Only #L85 then gets rid of "example" and thus, the code ends up in the conclusion that there are no duplicate values.

    A really straightforward fix that worked for me was converting all values to lowercase before making the intersection

    $duplicate_values = array_intersect(array_map('strtolower', $item_values), array_map('strtolower', $other_entity_values));

    Not sure if this might have any unintended side effects, though.

  • Pipeline finished with Failed
    5 months ago
    Total: 579s
    #88894
  • Pipeline finished with Canceled
    5 months ago
    Total: 454s
    #88904
  • I added a test case that outlines the issue. This is my first ever test contribution (or Core contribution in general), so I hope everything is fine.

  • Pipeline finished with Failed
    5 months ago
    Total: 1310s
    #88911
  • Pipeline finished with Failed
    5 months ago
    Total: 172s
    #89467
  • Pipeline finished with Failed
    5 months ago
    Total: 183s
    #89479
  • Pipeline finished with Success
    5 months ago
    Total: 580s
    #89491
  • Status changed to Needs review 5 months ago
  • I've also added a potential fix. As stated before, not sure about potential side effects though.

  • Status changed to Needs work 5 months ago
  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    Left a comment on the MR, we can't assume all unique field constraints are case-insensitive.

    πŸ› Able to create user accounts with the same email address Active has a different approach.

    I think we should combine the two issues.

  • Are there other instances where two values may be considered the same by a database query but not in a PHP function like array_intersect?

  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    #2068655: Entity fields do not support case sensitive queries β†’ leads me to believe that entity queries/values are case sensitive at the DB layer but I'm happy to be wrong about that

  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10
  • I was actually asking about any theoretical "similarities" that can occur in the data comparisons besides case sensitivity. For example, 0 == FALSE (a stretch, I know) or encoding issues.

    It's probably fine to only check for case sensitivity for emails and usernames, and it's also probably unlikely that there's anything else to consider here, but still something that should be pondered for a minute before moving on IMO.

  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    Yes, agree @caesius

  • I don't think we can assume all instances of this constraint want a case insensitive comparison.

    In general, I agree with this statement.

    However, there are still some things not clear to me: If I understand the code of the UniqueFieldValueValidator and the changes made β†’ correctly, wasn't the behavior exactly the same in the past since the case-sensitivity was solely depend on the DB layer (since there were no additional PHP checks)? Do the changes I suggested really change this behavior since if the DB layer is case-sensitive, the code I suggested doesn't change that because the PHP code doesn't even see the potential case-insensitive duplicate because it's not even returned by the DB layer as a potential candidate (=> the results are still case-sensitive)? On the other hand, if the DB layer is case-insensitive, we now preserve this behavior and don't do an additional check on the PHP-side to filter out case-insensitive duplicates.

    In contrast, in the code changes you suggested in https://www.drupal.org/project/drupal/issues/3419816 πŸ› Able to create user accounts with the same email address Active , I believe the behavior is now quite confusing:
    - If shouldIgnoreCase == FALSE, case-sensitivity is enforced, so this is fine.
    - However, if shouldIgnoreCase == TRUE, case-insensitivity is not enforced if the DB layer performs a case-sensitive comparison, right? Therefore, even if shouldIgnoreCase == TRUE, it can happen that the uniqueness is still checked in a case-sensitive manner.

    This behavior doesn't align with the expectations I would have towards a shouldIgnoreCase toggle. There might be an oversight on my end, so obviously the above only applies if my understandings are correct.

  • πŸ‡¦πŸ‡·Argentina gerzenstl Resistencia

    I able to reproduce this issue on Drupal v10.2.1.

  • Status changed to Needs review 4 months ago
  • Status changed to Needs work 3 months ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Appears to have open thread, also MR target branch needs to be updated

  • Pipeline finished with Failed
    3 months ago
    Total: 633s
    #122024
  • πŸ‡ΊπŸ‡ΈUnited States aasarava

    I have to agree with @nico.b. The MR gets the UniqueFieldValueValidator back to where it was before the breaking change.

    Given that the breaking change was inadvertent (as it was not documented and it broke major core functionality), there's no reason to assume that any core and contrib modules that use the UniqueField constraint now expect there to be case sensitivity when comparing values. So there's no strong reason to separate out a properly working constraint for username and email while leaving any other uses potentially broken.

    If there's a need for case sensitivity in UniqueField, that should be handled as its own constraint & validator (CaseSensitiveUniqueField?) that contributors can use if needed.

    For context, I have a client site that relies on dozens of user self-registrations per day. With 50K users in the system, we were seeing a lot of username clashes showing up as SQL integrity violations in the logs. This means lots of potential users were getting WSODs without explanation. We've deployed the MR to production due to urgently needing a fix, and it has been working without problems so far.

  • Thanks for the confirmation @aasarava. We've also already applied the patch to our production system due to the same reasons. For large pages with a high number of user registrations per day, this issue is really critical.

    @smustgrave I've updated the MR target branch. For the open thread, there's not much I can do except for waiting for a response. I've also added a response directly in the MR again (not only here).

  • Pipeline finished with Failed
    3 months ago
    Total: 248s
    #124077
  • Pipeline finished with Failed
    3 months ago
    Total: 2173s
    #124078
  • Pipeline finished with Failed
    3 months ago
    Total: 212s
    #124123
  • Pipeline finished with Success
    3 months ago
    #124132
  • πŸ‡ΊπŸ‡ΈUnited States aasarava

    Does changing the target branch to 11.x mean there's no plan to try to get this included as a bug fix in a 10.2.5 or 6 release?

  • Status changed to Needs review 3 months ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Will let @larowlan respond to the thread

  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    Yes, we'd want this in a 10.2 bugfix.
    Replied to the comment, sorry this one dropped off my radar. Left a couple of minor nitpicks on the MR.

  • Status changed to Needs work 3 months ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Thanks @larowlan!

  • Pipeline finished with Canceled
    3 months ago
    Total: 474s
    #124867
  • Pipeline finished with Success
    3 months ago
    Total: 796s
    #124877
  • Status changed to Needs review 3 months ago
  • Thanks a lot, @smustgrave and @larowlan! The suggestions in the MR seem perfectly valid and I've applied them.

    Pipeline runs are still successful (and test-only changes still fail) and all threads are resolved, so I hope the MR is good to go now :)

    @aasarava: Based on what I've also only learned just a few days ago (from here β†’ ), patches usually go to the main dev branch (currently 11.x). The patch is then cherry-picked back to stable branches, so e.g. 10.2. So as @larowlan also said, the change of the target is rather a "technical detail"/"process adherence"-thing instead of a decision to not bring it to 10.2.

  • Status changed to RTBC 3 months ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Feedback appears to be addressed

    Thanks!

    • larowlan β†’ committed 78133af8 on 10.2.x
      Issue #3415582 by nico.b, larowlan, Matt B: Unhandled exception when...
    • larowlan β†’ committed 7f98626a on 10.3.x
      Issue #3415582 by nico.b, larowlan, Matt B: Unhandled exception when...
    • larowlan β†’ committed ce9ec037 on 11.x
      Issue #3415582 by nico.b, larowlan, Matt B: Unhandled exception when...
  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    Committed to 11.x and backported to 10.2.x and 10.3.x

    Thanks folks

    I'll repurpose the other issue to add a case-insenstive option (which as you rightly point out, we never supported before)

  • Status changed to Fixed 3 months ago
  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10
  • πŸ‡¬πŸ‡§United Kingdom Matt B

    Thank you for this - I'm very time poor at the moment, so haven't had an opportunity to test!

  • Just found this. Thanks for the fix!
    How should accounts that are created programatically handle this sort when there is an existing username? Like is there a way to put an _1 etc after the username?

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

Production build 0.69.0 2024