Problem/Motivation

The Anonymous user, as created by the user_install() function (@see user.module's .install file),
has its status property set to 0 and is therefore blocked from authenticating.
That reasonable default should not be modified since we don't want to authenticate anonymous users.

To safeguard against this, the UnblockUser plugin User::activate() method should ascertain that the Anonymous user does not become unblocked.
In order to achieve this, the User::isAnonymous() method also needs to enforce a stricter check on the uid property.

Proposed resolution

Modify the UnblockUser::execute() User::activate() method and implement a Logical Exception being thrown that disallows the Anonymous user from becoming unblocked.
Additionally, make User::isAnonymous() more strict and accept only uid === 0 as the condition that reveals when a user is truly anonymous.

Remaining tasks

Provide a patch.
Provide a Change Record for changes that may affect developers.

Issue fork drupal-3311563

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

stefanos.petrakis created an issue. See original summary.

stefanos.petrakis’s picture

stefanos.petrakis’s picture

Status: Active » Needs review
StatusFileSize
new642 bytes
cilefen’s picture

wim leers’s picture

Interesting!

But shouldn't we then make this much more explicit by updating \Drupal\user\Entity\User::activate() to throw an exception if one even tries to do this for the anonymous user?

stefanos.petrakis’s picture

StatusFileSize
new1.14 KB
new526 bytes

Thanks @WimLeers, that sounds even better. Something along these lines then?

stefanos.petrakis’s picture

StatusFileSize
new1.91 KB
new1.39 KB

Test added.

Status: Needs review » Needs work
stefanos.petrakis’s picture

Priority: Normal » Major
Status: Needs work » Needs review
Related issues: +#2854252: User forms broken for admin without 'administer users'

Fixing different things, the main attempt is at hopefully improving the following points:

  1. Making isAnonymous() stricter in that a NULL uid (e.g. for newly created unsaved User entities) will not return TRUE
  2. Switch from using isAnonymous() as a condition for detecting the 'register' condition for the AccountForm to isNew(). That was discussed here [2854252#95] and is mostly related to this quote from alexpott:

    So the code we're changing is super weird because why does a user you're in the process of creating return TRUE for $items->getEntity()->isAnonymous() - but this is not the fault of this patch. I think isNew() would be a better check here. Let's open a follow-up for that.

    which I agree with.

All in all, this seems to be more important than not, I have the feeling there are a couple of important things that need attention here, maybe even breaking this down in smaller issues. Gonna bump this to major precautionary.

stefanos.petrakis’s picture

And the patches of course.

stefanos.petrakis’s picture

StatusFileSize
new8.53 KB

PHPCS fixes

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.

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs Review Queue Initiative

This issue is being reviewed by the kind folks in Slack, #needs-review-queue-initiative. We are working to keep the size of Needs Review queue [2700+ issues] to around 400 (1 month or less), following Review a patch or merge request as a guide.

At this time we would need a D10.1.x patch or MR for this issue.

_utsavsharma’s picture

StatusFileSize
new7.16 KB
new8.55 KB

Patch for 10.1.x.

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

smustgrave’s picture

Status: Needs work » Reviewed & tested by the community

Hiding files to avoid confusion.

Ran the tests locally without the fix and got

Failed asserting that exception of type "LogicException" is thrown.

And clearly it passes with the fix.

Changes look good. Not sure if a security review should happen first (question for committer)

longwave’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs change record

Added a question to the MR. This also needs a change record to note that we are making User::isAnonymous() more strict, as other code may have copied the pattern in core and used isAnonymous() to detect new users, which will now stop working.

stefanos.petrakis’s picture

Issue summary: View changes
stefanos.petrakis’s picture

Assigned: Unassigned » stefanos.petrakis

Added a Change Record as per #19.
Will have a look at the failing tests now.

stefanos.petrakis’s picture

Status: Needs work » Needs review

This one is up for a review again, the change record is in place, although a draft that could use some eyes on.
The points in the MR have all been addressed.

smustgrave’s picture

See \Drupal\Core\Session\AccountInterface is being replaced now. Is this change going to be disruptive to anyone using that interface now?

stefanos.petrakis’s picture

The reason I replaced \Drupal\Core\Session\AccountInterface with \Drupal\user\UserInterface is because it was causing a breakage in that Unit test class due to the following change:

diff --git a/core/modules/user/src/UserAccessControlHandler.php b/core/modules/user/src/UserAccessControlHandler.php
index a231d5214d..46b97cfa2e 100644
--- a/core/modules/user/src/UserAccessControlHandler.php
+++ b/core/modules/user/src/UserAccessControlHandler.php
@@ -101,7 +101,7 @@ protected function checkFieldAccess($operation, FieldDefinitionInterface $field_
       case 'name':
         // Allow view access to anyone with access to the entity.
         // The username field is editable during the registration process.
-        if ($operation == 'view' || ($items && $items->getEntity()->isAnonymous())) {
+        if ($operation == 'view' || ($items && $items->getEntity()->isNew())) {
           return AccessResult::allowed()->cachePerPermissions();
         }
         // Allow edit access for the own user name if the permission is

That change is however crucial to this issue and also makes sense in that class since it is an access control handler specific to the user entity type. And that type has a isNew() method.

The Unit test class however that is testing that handler has been using so far a \Drupal\Core\Session\AccountInterface mock that does not have an isNew() method (which causes the breakage) but most importantly makes less sense to use for testing the access control handler for the user entity type instead of the \Drupal\user\UserInterface itself.

So, all in all, that change is not disruptive, but the Unit test needed an adjustment IMHO.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Thanks for the explanation. Will move it on and see what the committer thinks too.

  • catch committed c704db94 on 10.1.x
    Issue #3311563 by stefanos.petrakis, rpayanm, smustgrave, longwave, Wim...
catch’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: -Needs change record

There's a very small chance that this could break some existing code (for example using ::isAnonymous() as a proxy for ::isNew()) but that could would already be broken as this issue shows, and it might even fix some hidden bugs for people. So I think the change record here (which looks good) is enough.

Committed c704db9 and pushed to 10.1.x. Thanks!

Status: Fixed » Closed (fixed)

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