Deprecate passing non-strings to UserSession::hasPermission() and User::hasPermission()

Created on 20 December 2023, about 1 year ago
Updated 15 March 2024, 9 months ago

Problem/Motivation

I observed the following error during functional tests:

TypeError: Drupal\Core\Session\PermissionChecker::hasPermission(): Argument #1 ($permission) must be of type string, null given, called in [...]/core/lib/Drupal/Core/Session/UserSession.php on line 123 in Drupal\Core\Session\PermissionChecker->hasPermission() (line 23 of core/lib/Drupal/Core/Session/PermissionChecker.php).

I have not seen this error in a non-test context.

This is caused because argument 1 of PermissionChecker::hasPermission() must be a string. That can be called with a non-string argument from UserSession::hasPermission(), which does not have its own type declarations, so $permission can be a non-string.

I tried adding the same type declaration to UserSession::hasPermission(). That caused the tests to fail immediately and without an error message that was helpful in tracking down the root cause.

Proposed resolution

Deprecate passing non-strings to UserSession::hasPermission() and User::hasPermission(). See #19.

In a followup cast $permission to string so that the the function signature of UserSession::hasPermission() can stay the same and the function signature of PermissionChecker::hasPermission() will always be followed.

Remaining tasks

User interface changes

None.

API changes

None.

Data model changes

None.

Release notes snippet

๐Ÿ› Bug report
Status

Fixed

Version

10.3 โœจ

Component
Baseย  โ†’

Last updated 1 day ago

Created by

๐Ÿ‡จ๐Ÿ‡ฆCanada Liam Morland Ontario, CA ๐Ÿ‡จ๐Ÿ‡ฆ

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

Merge Requests

Comments & Activities

  • Issue created by @Liam Morland
  • Issue was unassigned.
  • Status changed to Needs review about 1 year ago
  • ๐Ÿ‡จ๐Ÿ‡ฆCanada Liam Morland Ontario, CA ๐Ÿ‡จ๐Ÿ‡ฆ
  • Open in Jenkins โ†’ Open on Drupal.org โ†’
    Environment: PHP 8.1 & MariaDB 10.3.22
    last update about 1 year ago
    Build Successful
  • ๐Ÿ‡ซ๐Ÿ‡ทFrance andypost

    I bet it's won't fix because the calling code must be fixed instead of core

  • ๐Ÿ‡ซ๐Ÿ‡ทFrance andypost

    Alternatively it could use to add deprecation when UserSession::hasPermission() is called with non-string argument

  • Status changed to Closed: duplicate about 1 year ago
  • ๐Ÿ‡จ๐Ÿ‡ฆCanada Liam Morland Ontario, CA ๐Ÿ‡จ๐Ÿ‡ฆ
  • ๐Ÿ‡จ๐Ÿ‡ฆCanada Liam Morland Ontario, CA ๐Ÿ‡จ๐Ÿ‡ฆ

    Yes, that would make sense. It look like my issue specifically is caused by the issue linked above. But there may be other issues like this. This is, in effect, a change in Drupal's API. UserSession::hasPermission() must now be passed a string when it did not enforce that before.

  • Status changed to Needs review about 1 year ago
  • ๐Ÿ‡ซ๐Ÿ‡ทFrance andypost

    Kind of it, reopening as bug possible and re-title

    will need change record and test and re-parented

  • last update about 1 year ago
    Build Successful
  • Status changed to Needs work about 1 year ago
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    Seems to have caused some test failures in DrupalCI.

  • This has broken my site. It started with upgrade to 10.2.0 and now any user that isn't the root user (1) get's this message when logging on. Ideas?

    TypeError: Drupal\Core\Session\PermissionChecker::hasPermission(): Argument #1 ($permission) must be of type string, null given, called in /Users/*****/Sites/public_html/web/core/lib/Drupal/Core/Session/UserSession.php on line 123 in

    Drupal\Core\Session\PermissionChecker->hasPermission() (line 23 of core/lib/Drupal/Core/Session/PermissionChecker.php).
    Drupal\Core\Session\UserSession->hasPermission(NULL) (Line: 121)
    Drupal\Core\Session\AccountProxy->hasPermission(NULL) (Line: 212)
    form_mode_control_control_access_form_mode(Array, 'creation', NULL, Object, 'contact_message.contact_sales.', Object, Array) (Line: 40)
    form_mode_control_entity_form_display_alter(Object, Array, NULL) (Line: 545)
    Drupal\Core\Extension\ModuleHandler->alter('entity_form_display', Object, Array) (Line: 133)
    Drupal\Core\Entity\Entity\EntityFormDisplay::collectRenderDisplay(Object, 'default') (Line: 284)
    Drupal\Core\Entity\ContentEntityForm->init(Object) (Line: 97)
    Drupal\Core\Entity\EntityForm->buildForm(Array, Object)
    call_user_func_array(Array, Array) (Line: 536)
    Drupal\Core\Form\FormBuilder->retrieveForm('contact_message_contact_sales_form', Object) (Line: 283)
    Drupal\Core\Form\FormBuilder->buildForm(Object, Object) (Line: 48)
    Drupal\Core\Entity\EntityFormBuilder->getForm(Object) (Line: 86)
    Drupal\contact\Controller\ContactController->contactSitePage(Object)
    call_user_func_array(Array, Array) (Line: 123)
    Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 627)
    Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 124)
    Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
    Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 181)
    Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 76)
    Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
    Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
    Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 106)
    Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 85)
    Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
    Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
    Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 36)
    Drupal\Core\StackMiddleware\AjaxPageState->handle(Object, 1, 1) (Line: 51)
    Drupal\Core\StackMiddleware\StackedHttpKernel->handle(Object, 1, 1) (Line: 704)
    Drupal\Core\DrupalKernel->handle(Object) (Line: 19)

  • Open in Jenkins โ†’ Open on Drupal.org โ†’
    Environment: PHP 8.1 & MariaDB 10.3.22
    last update about 1 year ago
    25,902 pass, 1,785 fail
  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia Akhil Babu Chengannur

    Akhil Babu โ†’ made their first commit to this issueโ€™s fork.

  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia Akhil Babu Chengannur

    Test failures in patch #7 look strange

    /tmp/build_workspace_04dcabf34eade6f2685220200c46ced7IA9qsD/core/lib/Drupal
    /Component/Serialization/vendor does not exist and could not be created:
    Failed to create "/tmp/build_workspace_7f381338ba97106e55c4fb8ea08a06c6Eeef
    Dz/core/lib/Drupal/Component/Render/vendor/drupal/core-utility": mkdir(): N
    o space left on device

    Creating a new MR with same changes to see the actual tests that are failing

  • Pipeline finished with Success
    12 months ago
    #69128
  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia Akhil Babu Chengannur

    No failed tests

  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia Akhil Babu Chengannur

    We probably need a test to confirm the deprecation message. But I am not able to find a 'realistic' scenario where a non string argument will be passed as an argument to hasPermission(). The proposed solution is to simply cast $permission to string. The only option to create a failing test for this approach is to call hasPermission() with NULL. If hasPermission is called with an array to create a failing test like hasPermission(['access content']), then both test and code will fail (Array to string conversion).

  • Status changed to Needs review 12 months ago
  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia Akhil Babu Chengannur
  • Status changed to Needs work 12 months ago
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    Can probably create a kernel test and call the function.

    Also trigger_errors shouldn't points to the ticket but a change record.

  • Pipeline finished with Success
    12 months ago
    #70016
  • Pipeline finished with Failed
    12 months ago
    #70022
  • Status changed to Needs review 12 months ago
  • Pipeline finished with Failed
    12 months ago
    #70026
  • Pipeline finished with Failed
    12 months ago
    #70028
  • Pipeline finished with Success
    12 months ago
    #70032
  • Status changed to RTBC 12 months ago
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    Deprecation looks good. CR reads fine too.

  • Status changed to Needs work 12 months ago
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom catch

    The MR doesn't match the issue title - we're deprecating passing a non-string to ::hasPermission() but not adding the type hint yet.

    To add the type hint, we'd need to add it to AccountInterface::hasPermission(), this will require using the mechanism documented at https://www.drupal.org/about/core/policies/core-change-policies/drupal-d... โ†’ - however, we've only actually done that at most once or twice I think so far.

    Given this is a bugfix, I think we should just add the cast here with an inline comment, and then open a follow-up to do the full deprecation and type hinting process.

  • ๐Ÿ‡จ๐Ÿ‡ฆCanada Liam Morland Ontario, CA ๐Ÿ‡จ๐Ÿ‡ฆ

    @#14 I encountered this problem when the value passed was NULL. It worked as before when cast to string.

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium kristiaanvandeneynde Antwerp, Belgium

    The documentation for adding the typehint seems wrong? it says:

    Or, change the typehint of an argument. For example:

    -  public function foo(string $bar);
    +  public function foo(/* string|Stringable $bar */);
    

    But shouldn't that be:

    -  public function foo(string $bar);
    +  public function foo(/* string|Stringable */$bar);
    

    As it stands the documentation seems to want you to temporarily remove the argument by commenting it out.

  • ๐Ÿ‡ซ๐Ÿ‡ทFrance andypost

    I see no reason to cast as interface expects string, so added type-hints as deprecation already is here

  • Pipeline finished with Success
    11 months ago
    #77770
  • Status changed to Needs review 11 months ago
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium kristiaanvandeneynde Antwerp, Belgium

    Looks good to me, but waiting for a second approval because of my assumption that the deprecation docs are slightly incorrect.

  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    #19 mentions a follow up, could that be created, then can mark it.

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium kristiaanvandeneynde Antwerp, Belgium

    Created a child issue and tagged it

  • Status changed to RTBC 11 months ago
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    Sorry @kristiaanvandeneynde for this following off my radar. Marking.

  • ๐Ÿ‡ณ๐Ÿ‡ฟNew Zealand quietone
  • ๐Ÿ‡ณ๐Ÿ‡ฟNew Zealand quietone

    I'm triaging RTBC issues โ†’ . I read the IS, the comments for this issue and the followup and the MR for this issue. I don't see any unanswered questions.

    The proposed resolution is not correct. I have updated it.

    What I did find that needs work is the follow up. The issue summary over there is too brief. If I had not read this issue first I would have no idea what is to be done in that issue. I will tag that issue for an issue summary update.

    Leaving at RTBC

  • Status changed to Needs work 10 months ago
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom alexpott ๐Ÿ‡ช๐Ÿ‡บ๐ŸŒ

    I'm not sure why we're suggesting Stringable in the future typehint. As far as I can see it'll be wrong.

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium kristiaanvandeneynde Antwerp, Belgium

    Yeah that will be wrong, I think the author perhaps got confused because the example we linked to makes mention of Stringable too?

  • First commit to issue fork.
  • Pipeline finished with Failed
    10 months ago
    #107834
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium kristiaanvandeneynde Antwerp, Belgium

    Looks good to me now, seems to have a random test failure.

  • Pipeline finished with Running
    10 months ago
    #107894
  • Status changed to Needs review 10 months ago
  • Status changed to RTBC 10 months ago
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium kristiaanvandeneynde Antwerp, Belgium

    #29 has been addressed, back to RTBC

  • Status changed to Fixed 10 months ago
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom alexpott ๐Ÿ‡ช๐Ÿ‡บ๐ŸŒ

    Committed and pushed a9f3b75195 to 11.x and 1122c8d387 to 10.3.x. Thanks!

  • ๐Ÿ‡ซ๐Ÿ‡ทFrance andypost

    Thank you! send a patch to follow-up ๐Ÿ“Œ [11.x] Tighten typehint in UserSession::hasPermission() and User::hasPermission() Needs review

    Please close the MR

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

Production build 0.71.5 2024