Note
This is a security vulnerability but it only exists in Drupal 8 as the access shortcuts permission does not exist in Drupal 7 so I am disclosing it publicly.

See #1541094: Add 'access shortcuts' permission for proof.

Problem/Motivation

If you have configured your site to expose shortcuts via a block instead of the toolbar users without the access shortcuts permission will still be able to see shortcuts.

A mitigating factor is that they will only see links that they have access to.

Proposed resolution

Make the shortcuts block not appear at all for users without the access shortcuts permissions.

Remaining tasks

User interface changes

None.

API changes

None.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Anonymous’s picture

Status: Active » Needs review
FileSize
962 bytes

Let's see whether I broke something or not.

Is this something we need to test in the base testclass, or do we add a seperate class?

tstoeckler’s picture

Awesome that you found BlockInterface::access(), I wasn't aware this could be solved so elegantly. I had a look at BlockBase::access() and that basically just delegates to BlockBase::blockAccess() but - even though all core block plugins still do their access logic there - that is a relict of the past as the code comment in BlockBase::access() clearly indicates. See also #2375689: BlockBase::blockAccess() should return AccessResult instead of a bool. Thus, it is correct to override access() directly, and not blockAccess(), awesome!

Also awesome that you figured out the $return_as_object weirdness correctly.

One comment on the actual access checking:

+++ b/core/modules/shortcut/src/Plugin/Block/ShortcutsBlock.php
@@ -29,4 +31,20 @@ public function build() {
+
+    if ($account->hasPermission('access shortcuts')) {
+      $access = AccessResult::allowed();
+    }
+    else {
+      $access = AccessResult::forbidden();
+    }
+
+    $access->setCacheable(FALSE);

This whole hunk can be replaced with

$access = AccessResult::allowedIfHasPermission('access shortcuts');

That is not only a little bit more readble but also has the benefit of setting proper role-based cacheability, so that this check does not need to be repeated each time. (I.e. the setCacheable(FALSE) is unnecessary in this case, but if we use allowedIfHasPermission() we don't need to care about that in the first place because that handles the caching logic for us.)

Regarding tests: I would prefer introducing a new ShortcutsBlockTest which enables the shortcut block (see WebTestBase::drupalPlaceBlock()) and creates two users: one with the access shortcuts permission and one without. And then you can assert that the shortcuts block is only shown for the correct user.

joshi.rohit100’s picture

Attached test only patch

Status: Needs review » Needs work

The last submitted patch, 3: 2449633-shortcut-block-access-permission-2-test-only.patch, failed testing.

joshi.rohit100’s picture

Status: Needs work » Needs review
FileSize
1.72 KB
618 bytes

Attached test only patch again with block administer permissions.

Status: Needs review » Needs work

The last submitted patch, 5: 2449633-shortcut-block-access-permission-5-test-only.patch, failed testing.

Anonymous’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
3.82 KB
3.68 KB
3.94 KB

Re #2:
That's a nice tweak! I wasn't too sure on the caching, so I figured never caching was the safest :)
In this patch, I changed the whole chunk including the "$return_as_object weirdness" (lol), since it seems to work in the test. Could you elaborate on why this is needed? Do we need extra coverage?

Re #5:
Nice test! Thanks!
I expanded on your test a bit to have an admin, an editor and a no-rights authenticated user. I did however used the users in the base class, and I'm not fully confident that this was the right thing to do.

The last submitted patch, 7: shortcutsblock_does_not-2449633-7.test-fail.patch, failed testing.

dawehner’s picture

+++ b/core/modules/shortcut/src/Plugin/Block/ShortcutsBlock.php
@@ -29,4 +31,11 @@ public function build() {
+  /**
+   * {@inheritdoc}
+   */
+  public function access(AccountInterface $account, $return_as_object = FALSE) {
+    return AccessResult::allowedIfHasPermission($account, 'access shortcuts');
+  }
+
 }

... so this implementation is wrong ... it does not take into account $return_as_object, so this should rather implement blockAccess().

In general I'm curios whether we should mark blockAccess() as abstract, because each block plugin should at least think about its access.

dawehner’s picture

Status: Needs review » Needs work

I think yeah needs work :(

joshi.rohit100’s picture

@dawehner - As per https://www.drupal.org/node/2375689, I am not sure whether we use access() or blockAccess() ?

tstoeckler’s picture

@dawehner: Currently blockAccess() must return a bool so it circumvents the entire AccessResult logic. That's why I think it's currently preferred to implement access() directly, even though none of the other block plugins in core do it (see also #2). But it would be a nice idea to hide away the $return_as_object foo in access() and just let block plugins always return an AccessResult. Would probably need some committer feedback first, whether that's feasible at this point.

@pjonckiere: That $return_as_object foo is a BC-layer. Various access methods in core used to return TRUE/NULL/FALSE and it was deemed to disruptive to have them suddenly return an AccessResult object, which is far superior due to the caching logic. So receiving an AccessResult object is opt-in, which is why we have that argument. This, however, puts the burden on the implementors of the access logic (you, writing the block access in this case), which is rather unfortunate. So the line that was in patch #1 regarding the $return_as_object parameter is still needed.

Anonymous’s picture

Re #9: I agree with tstoeckler and joshi.rohit100, see #2375689: BlockBase::blockAccess() should return AccessResult instead of a bool

Re #12: Fair enough. Thanks for the explanation.

The last submitted patch, 13: shortcutsblock_does_not-2449633-13.test-fail.patch, failed testing.

tstoeckler’s picture

Awesome, I think we are almost done here. I just have some suggestions to make the test a little bit more concise, although in terms of test coverage it's already perfect:

  1. +++ b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
    @@ -327,4 +327,45 @@ private function verifyAccessShortcutsPermissionForEditPages() {
    +    $block_name = 'shortcuts';
    +    // Create a random title for the block.
    +    $title = $this->randomMachineName(8);
    +    // Get the default theme.
    +    $default_theme = $this->config('system.theme')->get('default');
    +    $edit = array(
    +      'id' => strtolower($this->randomMachineName(8)),
    +      'settings[label]' => $title,
    +    );
    ...
    +    // Assign block to sidebar first region.
    +    $edit['region'] = 'sidebar_first';
    +    $this->drupalPostForm('admin/structure/block/add/' . $block_name . '/' . $default_theme, $edit, t('Save block'));
    +    $this->assertText('The block configuration has been saved.', 'Block was saved');
    

    This code can be replaced with $block = $this->drupalPlaceBlock('shortcuts');.

    That uses the API instead of the UI to place the block, but that is fine, because we do not need to test that placing blocks works as part of this test. The method returns the block entity so you can then do $block->label() in the assertions below.

  2. +++ b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
    @@ -327,4 +327,45 @@ private function verifyAccessShortcutsPermissionForEditPages() {
    +    $this->drupalGet('');
    +    $this->assertText($title, 'Block was displayed on the front page.');
    

    Although it doesn't particularly hurt, I don't think it's necessary to test that an admin user can see the block since we're testing that a "regular" user can see it just below that. So in my opinion these two lines can be removed.

  3. +++ b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
    @@ -327,4 +327,45 @@ private function verifyAccessShortcutsPermissionForEditPages() {
    +    $this->drupalLogout();
    +
    +    // Verify that users without the 'access shortcuts' permission can see the
    +    // shortcut block.
    +    $this->drupalLogin($this->drupalCreateUser(array()));
    

    I think the drupalLogin() is not necessary here, as anonymous users should not have the access shortcuts permission either. In that case the drupalLogout() can just be moved below the comment (and maybe the comment should be adapted). Could you try that?

  4. +++ b/core/modules/shortcut/src/Tests/ShortcutTestBase.php
    @@ -83,7 +83,7 @@ protected function setUp() {
    -    $this->adminUser = $this->drupalCreateUser(array('access toolbar', 'administer shortcuts', 'view the administration theme', 'create article content', 'create page content', 'access content overview', 'administer users', 'link to any page', 'edit any article content'));
    +    $this->adminUser = $this->drupalCreateUser(array('access toolbar', 'administer blocks', 'administer shortcuts', 'access shortcuts', 'view the administration theme', 'create article content', 'create page content', 'access content overview', 'administer users', 'link to any page', 'edit any article content'));
    

    If my comments above are implemented I think this change is no longer necessary.

Great work so far, thanks a lot!

tstoeckler’s picture

Status: Needs review » Needs work
tstoeckler’s picture

Issue summary: View changes
joshi.rohit100’s picture

Assigned: Unassigned » joshi.rohit100
Issue tags: +SprintWeekend2015
joshi.rohit100’s picture

Status: Needs work » Needs review
FileSize
1.54 KB
3.17 KB

thanks @tstoeckle.

I have removed the code from ShortcutTestBase as this is base class for all shortcut related test cases and if we we use block module here, then it will instanciate for all tests and we only need this for just one.

Status: Needs review » Needs work

The last submitted patch, 19: shortcutsblock_does_not-2449633-19.test-fail.patch, failed testing.

joshi.rohit100’s picture

Status: Needs work » Needs review
FileSize
2.35 KB

complete patch with test.

Status: Needs review » Needs work

The last submitted patch, 21: shortcutsblock_does_not-2449633-21.patch, failed testing.

joshi.rohit100’s picture

Status: Needs work » Needs review
FileSize
2.22 KB
748 bytes

Removed the test for admin user as I think, we just need to see for normal user.

andypost’s picture

+++ b/core/modules/shortcut/src/Plugin/Block/ShortcutsBlock.php
@@ -29,4 +31,12 @@ public function build() {
+  public function access(AccountInterface $account, $return_as_object = FALSE) {

Suppose the plugin should implement blockAccess() so \Drupal\Core\Block\BlockBase::access() will do this work right

joshi.rohit100’s picture

@andypost - #24 is not clear to me. Can you please provide more detail

joshi.rohit100’s picture

@andypost - #24 is not clear to me. Can you please provide more detail

andypost’s picture

there's 2 different methods to expose access for block, custom block plugins should use protected blockAccess() implementation to expose thier access

PS: see \Drupal\Core\Block\BlockBase::blockAccess

joshi.rohit100’s picture

Status: Needs work » Needs review
FileSize
2.13 KB
694 bytes

Updated as per #27 and I think, @andypost is right as we just here only check the access of block.

andypost’s picture

FileSize
1.4 KB

Let's make sure that test works

willzyx’s picture

+++ b/core/modules/shortcut/src/Plugin/Block/ShortcutsBlock.php
@@ -29,4 +31,13 @@ public function build() {
+  protected function blockAccess(AccountInterface $account) {
+    if ($account->hasPermission('access shortcuts')) {
+      return TRUE;
+    }
+  }

should be return $account->hasPermission('access shortcuts');

joshi.rohit100’s picture

Assigned: joshi.rohit100 » Unassigned
FileSize
2.11 KB
514 bytes

@willzyx thanks :)

The last submitted patch, 29: 2449633-28-test-only.patch, failed testing.

andypost’s picture

Status: Needs review » Reviewed & tested by the community

Great, just need to remove unused "use":

+++ b/core/modules/shortcut/src/Plugin/Block/ShortcutsBlock.php
@@ -7,7 +7,9 @@
+use Drupal\Core\Access\AccessResult;

is not needed now

andypost’s picture

Status: Reviewed & tested by the community » Needs work

#33

willzyx’s picture

Status: Needs work » Needs review
FileSize
434 bytes
2.07 KB

Changes as for #33

tstoeckler’s picture

Status: Needs review » Reviewed & tested by the community
alexpott’s picture

Status: Reviewed & tested by the community » Fixed

This issue addresses a major bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 7bf1e5a and pushed to 8.0.x. Thanks!

  • alexpott committed 7bf1e5a on 8.0.x
    Issue #2449633 by joshi.rohit100, pjonckiere, willzyx, andypost:...

Status: Fixed » Closed (fixed)

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