Problem/Motivation
After #332796: Add permissions to the update.module to hide warnings, a message like this is shown to anyone with the new "View update notifications" permission, if there is pending security update:
There is a security update available for your version of Drupal. To ensure the security of your server, you should update immediately! See the available updates page for more information.
The link ("available updates") goes to /admin/reports/updates. This link is shown even if the user does not have permission to view that page.
Steps to reproduce
- Install Drupal 9.4.0-rc1.
- Grant these permissions to the 'Content editor' user role:
- Use the administration pages and help (should already have this one)
- View update notifications
- Install an older version of a module missing a security update. E.g. Quick Edit (contrib) version 1.0.0.
- Create a user with the 'Content editor' role, login, and visit
/admin - See a warning message about a missing security update that includes a link to "available updates". Note the link in the message and follow it.
- Land on /admin/reports/updates which is a 404 for this user.
Proposed resolution
Check link access when generating the messages to print in the UI, and if the user doesn't have access to the 'Available updates' report, don't include a link to it.
Meanwhile, Update status emails will always include the link (as an absolute URL), since they can be sent to arbitrary users (not the current user), and even to folks without an account on the site at all. Not much we can do to properly check access in those cases, so do not change the format of emails in this regard.
Remaining tasks
User interface changes
Users with limited permissions will only see warning messages from Update Manager that include text, not a link to a page they can't access.
Before

After

API changes
None
Data model changes
None
Release notes snippet
None
| Comment | File | Size | Author |
|---|
Issue fork drupal-3285176
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
Comment #2
benjifisherComment #3
benjifisherWe discussed this issue at #3284204: Drupal Usability Meeting 2022-06-10. That issue will have a link to a recording of the meeting.
For the record, the attendees at today's usability meeting were @AaronMcHale, @andregp, @rkoller, @shaal, @simohell, @worldlinemine, and me.
Comment #4
cilefen commentedI would test for the permission and render the message differently.
Comment #5
dwwxjm had some concerns on the message in the parent issue, not sure if we should fix those here or there.Edit: sorry, that was a different message. This one should be fixable on its own.Comment #6
dwwThought I’d have a chance to get this started, so I clicked the branch button, but my day just changed. I probably won’t be able to work on this until Tuesday now. So if anyone else wants to run with it in the meantime, please do.
Comment #7
dwwFor tracking, calling this a child of #332796: Add permissions to the update.module to hide warnings.
Comment #8
smustgrave commentedCould it be this simple?
Comment #9
smustgrave commentedThis one includes some simple tests (still very novice with testing).
Comment #10
smustgrave commentedComment #11
feyp commentedThanks for your patch.
+ if (\Drupal::currentUser()->hasPermission('administer site configuration')) {I think it would be better to just call
Url::access()instead of checking for a specific permission. That way, if the access requirements for the route ever change, we don't have to change the code again.In the tests, I see that the patch tests whether the access check for
/admin/reports/updatesworks as intended. But that's not really what this issue is about. I think we should test whether the link in the warning message is present or not with the different permissions as detailed in the steps to reproduce in the issue summary.Comment #12
smustgrave commentedCan take another crack at it!
Comment #13
smustgrave commentedThoughts?
Comment #14
feyp commentedThanks, this is going in the right direction! I see that you found a few more places in the code that needed changes :).
We don't have to pass an argument to
access()in this case. If$accountisNULL, which is the default, the access check will already be performed for the current user.I think we should use
$url->toString();in the message parameters. It will be shorter, more clear that the access check is performed for the same Url and less prone to error, if the Url ever needs updating.Why do we use different parameters here for the access check and for the Url we print?
For the tests, they now test what we want to test. That's great! I think we could probably test both cases in one test method. That would save us one additional bootstrap. You could just call drupalLogin() again to re-login with the changed permissions. We might even put this into
testModulePageRegularUpdate(), where other aspects of the message display are already tested, but I'm not entirelysure on that one, so maybe take a look yourself, if you think that would be better.
For the next iteration, it would also be good to have a tests only patch that contains only the changes to the tests. Use the suffix
-tests-only.patch. Upload that patch first and then your actual patch that contains all changes. We'd expect the tests only patch to fail and the patch with all changes to pass. That way, we can proof, that the tests will properly fail, if the the bug is present.Comment #15
smustgrave commentedComment #16
feyp commentedLet's set this to needs review so that the tests are run.
Comment #17
dwwGreat work, thanks!
Agree with most of #14. Here's a detailed review on #15:
We already did all of this above, and it should persist across a
drupalLogin(). I think all we need for the 2nd half of this test is:- drupalLogin()
- drupalGet('/admin/appearance')
- assert links...
I also wonder if these new assertions could be folded into an existing test, so we don't have to completely re-install Drupal for this.
Maybe this is more accurate:
I think we need to revert this part of the patch.
update_mail()is generating email messages to arbitrary accounts. We don't care about the permissions of the current user to determine the contents of these emails. They could be sent to users without an account on the site at all, in fact.This is tricky, since
_update_message_text()is reused by bothupdate_mail()(where we do *not* want access checks, per above), andupdate_requirements()where we do need the checks.So probably we need to add another param to
_update_message_text()that controls whether it does the link access checks or not.Looking at all this with "fresh" eyes, it seems like a bug that this version of the link is relative, even when used by
update_mail()where we generally want absolute links.So, perhaps the new bool param should really be called something like
$is_emailand that toggles 2 things:- access checks
- absolute vs. relative
For both of these
$url->access()checks, we need anelseclause to still generate the text of the message, but don't format it as a link. E.g.:Thanks again!
-Derek
Comment #18
smustgrave commentedI'll take a look but when I tried to add the tests into the function above testModulePageRegularUpdate they just stopped working.
Comment #19
dwwp.s. Looking more closely at update.module, there's another spot in
update.installwhere this could be a problem:However, I just convinced myself we're currently safe, since both the status report and available updates report require only 'administer site configuration' permission. 😅 So the only people seeing these requirement check warnings are users that can see the status report, and therefore can also see the available updates report. However, if we ever get fancy and have a new permission to control access to the available updates report, this is probably going to break. 🤓
Comment #20
dwwWhoops, nope. I was wrong/right at #19. That *also* needs to be fixed. We reuse
update_requirements()inupdate_page_top(). So we can be showing these requirements check messages to users that don't haveadminister site configurationperm, which is exactly what this issue is trying to fix. 😅Comment #21
dwwSorry, one last note, copied from Slack. @smustgrave asked if the new
bool $is_emailparam to_update_message_text()should default to TRUE or FALSE. I vote FALSE:update_requirements()is calling it as_update_message_text($type, $status)update_mail()is already explicitly defining the$langcode, and calls it as$message['body'][] = _update_message_text($msg_type, $msg_reason, $langcode);So, it's easier for the caller in the mail case to override both 3rd and 4th default args with what makes sense for email, while the caller in the requirements case only needs to specify 2 params and get the right behavior.
Comment #22
dwwp.s. Since this issue is definitely changing some translatable strings, it will require a new minor version of core to fix, so the earliest this could now be backported to is 9.5.x to be included in 9.5.0.
Comment #23
smustgrave commentedIs it just the else statement you are concerned about?
Since the other line of text deals with update.report_update
Comment #24
dwwRe: #23. Exactly. The code I pasted into #19 actually needs to handle 3 cases:
administer software updates).administer site configuration).view update notifications)In this instance, the 3rd case probably shouldn't see the line at all, since "See the available updates page for more information." is useless text for someone who can't see that page...
Comment #25
smustgrave commentedAlso instead of doing a url check should we just make a function for checking if they have the administer site configuration. Like the one currently exists for update-manager-access
Comment #26
dwwGood question. But I think no need.
_update_manager_access()is a helper since it’s dealing with both a settings.php killswitch and permission checks. The else case doesn’t care about all that, and it can just be a regular url access check.Thanks,
-Derek
Comment #27
smustgrave commentedFrom #17 believe I have addressed 1, 3, 4, 5, and 6. I didn't address 2 because I couldn't get the tests working when adding to testModulePageRegularUpdate
Also believe I covered #24
Let me know if anything needs to be changed.
Comment #28
smustgrave commentedTypo
Comment #29
dwwSweet, thanks! Almost there...
Here's an interdiff between #15 and #28. Mostly looks really good. Confirming that all of #17 and #19-#24 are now addressed.
However, a few last minor problems / nits with #28:
s/Test/Tests/
We're supposed to wrap these at 80 chars.
Seems we should also assert that the non-link text we expect *is* present.
This isn't the right type. I think we have to call this
?stringsince it supports both NULL and a langcode string.However, changing this at all seems out of scope for this issue. I think we should revert this.
a. We should say it defaults to FALSE.
b. Needs to wrap to 80 chars.
c. Probably more helpful to say how access checks and relative URLs are impacted, not just "Will toggle".
So how about:
This also seems out of scope for this bug fix.
We should add the type for at least the new arg.
It'd be scope creep to add them for the existing 3, but I'd love to see those fixed, too. 😅 #NeedsFollowup. Although probably this function should completely go away and we can replace it with something OO with proper types.
If we define the param type, there's no need to cast it to a
(bool)here. We already know it's a bool. 😉Comment #30
ravi.shankar commentedAddressed points 1, 2, 4, 5, 6, 7, and 8 of comment #29, just point 3 is left, so needs work for that.
Comment #31
smustgrave commentedComment #33
dwwThanks, y'all!
With the test-only patch applied, UpdateSemverCoreTest.php:435 is here:
Which is in the "user without the right perms" part of the test.
Nothing left to do to improve this (that I can see). Therefore, RTBC!
Thanks again,
-Derek
Comment #34
dwwFull disclosure: This patch is changing more code paths than the new test coverage is exercising. The changes impact multiple places where these messages are printed, but the tests are only showing one possible scenario. I think it's probably fine as-is, but a committer might want more thorough test coverage. Let's see what they think...
Comment #35
larowlaneek, nested if/else in a switch statement 🤢
Personally I'd prefer to start putting some early returns here instead of else (but only on the lines we're touching).
For the rest of the function we should add a follow up to get rid of the switch and move to early returns (or even better, to match in D10).
Setting to needs review to see if anyone else agrees or its just me going too far with my
else is a code-smellcampaign hereComment #37
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #47
dwwArgh, I totally missed this. It moved from RTBC to NR while I was offline for a month, and then it was totally buried/lost in my issue tracker backlog.
Converted this into an MR, and ported to main. Once the bot is happy, I'll push a commit to address #35 and make sure that doesn't break anything.
Comment #48
acbramley commentedAdded a comment on the MR, also it looks like the IS and screenshots need an update as there is no mention about the email part of this change and the screenshots show messages that don't align with the code changes.
Comment #49
dwwOMFG, can we please skip InlineBlockTest?!?! That's randomly failed like 5 times for me today.
@acbramley: Thanks for the review! I pushed some commits to simplify the code / logic. It was indeed broken. Per the param description for
$is_emailwe should always be including links in that case. The previous code did not accomplish that, but the latest commit I just pushed should cover it.Updated the summary to mention we're not changing the format of the emails (and why).
But the screenshots are accurate. Those messages at the top of admin pages are from
hook_page_top().UpdateHooks::pageTop()does this:The MR diff against
src/hooks/UpdateRequirements.phpis for dealing with the last line in the error message. If the current UI user doesn't have permissions to view the available updates report, the warning message doesn't include the final sentence about that report.Comment #50
dwwPushed another commit to update a code comment to better explain this (I hope). 😅
Comment #51
acbramley commentedLooking good, I wonder if we should have a follow up to add test coverage for that email?
Comment #52
dww@acbramley: Thanks for the re-review!
We've already got
core/modules/update/tests/src/Unit/UpdateMailTest.phpI suspect that didn't fail with the broken logic here due to assumptions in the mocks. I'd have to spend a lot more time digging into that than I have time for right now. But if you want a follow-up to investigate that, I'm willing to open it.
Comment #53
dwwRe:
UpdateMailTest- hah, right. We never added cases for the NOT_FETCHED or UNKNOWN cases. 😅 And those are the ones that were broken in here. Whoops. #3571896: Add coverage to UpdateMailTest for NOT_FETCHED and other error conditionsComment #54
godotislateOne question: since there's no way to check access on email recipients, does it make sense not to provide the link on emails at all? Or maybe a link to the home page with a directive to log in and check?
Comment #55
dww@godotislate: That's a reasonable question. However, I vote we spin that off to a follow-up, if we want to explore it at all. The emails have been formatted basically like this for close to 20 years now. I don't remember any support requests or bug reports about it. I think generally speaking, if site admins configure email notifications at all, they have them go to full site admins. A direct link to the relevant report seems more useful than a link to the site front page and instructions how to navigate to the page you care about. But anyway, all that seems out of scope for this bug fix, which is ultimately a very minor change to the UI to hide links from a new class of site quasi-admins that have permissions to see these warnings, but not the underlying report. A hypothetical role in this situation is only possible after #332796: Add permissions to the update.module to hide warnings landed. Honestly, it's the UX team and myself the subsystem maintainer that noticed or care. Again, I don't remember ever seeing other support requests or bug reports about this. That said, I'd rather this bug didn't linger for another 4 years while we sort out the UX questions about reformatting the notification emails that "no one" has complained about for decades. 😅 Thanks!
Comment #56
acbramley commentedIMO emails with potentially inaccessible links are fine. Most sites will redirect to a login page with a destination back to the page. But if we do want to change that it can/should be in a follow-up.
Comment #57
godotislateAgreed that it'd be a follow up, if anything, since it's always been like this, but wanted to confirm. I don't have strong feelings about it. Thanks!
Comment #61
godotislateCommitted f5b6b5e and pushed to main and 420765c and pushed to 11.x. Thanks!