Posted by Jacine on February 16, 2010 at 9:37pm
| Project: | Drupal core |
| Version: | 7.x-dev |
| Component: | dashboard.module |
| Category: | bug report |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | closed (fixed) |
Issue Summary
I can access this page anonymously, even though I did not give anonymous users permission. The page title clearly indicates "Access Denied" but that doesn't stop the dashboard content from loading.
Screenshot: http://www.flickr.com/photos/jacine-rodriguez/4359768171/sizes/o/
Could this have something to do with the awkward region manipulation/markup going on there?
Comments
#1
This is because the individual blocks are accesible by permission. Its a bit strange indeed.
#2
Hmm, so what is the point of having a "View the administrative dashboard" permission?
#3
Well, its a bug? I think its pretty clear that it is.
#4
@Bojhan: could you elaborate:
... what you mean by this?
#5
The blocks you can see, because permissions allow you to see it.
#6
As in,
because they have not explicitly been restricted to certain roles via the block visibility settings?
#7
I can't reproduce on fresh HEAD, getting a plain 'acces denied' for anonymous on /admin (which is where dashboard lives now), without seeing anything dashboardy.
#8
Downgrading for now…
#9
@yoroy - you need to on one of the following to reproduce:
1 - Make sure your admin theme is the same as your front end theme.
2 - Place block(s) in dashboard regions for the front end theme.
I just tried it on a fresh install, and it's the same. I have a bad feeling this is "by design" though.
#10
Here's a quick patch that should fix the bug.
This doesn't address the awkward markup, though. The problem is that Drupal 7 theoretically allows the main content block to be placed in any region of the page (too much flexibility?) so it's therefore not so easy for the dashboard module to find where it is and insert stuff into it. I'm guessing it's possible to fix - but probably best as a separate issue.
#11
Sweet! Thanks David_Rothstein :) It works. Will wait for the testbot to RTBC and open a separate issue for the markup problem.
#12
#13
I'd be interested in a little explanation of what you are doing here. I see in the patch that you add some kind of acces check. Can you do the 'for dummies' breakdown of this problem and fix? Just for kicks and learning :)
#14
Not sure whether this patch is based on the assumption that #652122: Fix dashboard as the default /admin local task is given. If it is, then it needs a different solution.
#15
@yoroy: I'll try for #13. I'm not too sure myself, so please correct me, if that's incorrect.
When you visit the admin/dashboard page (or whatever the URL is currently), the menu router (is that the right word?) is called to determine what to show to you. Part of that menur router is the page callback, which is already being referenced in $is_visible as you can see in the patch. Menu routers also have an access callback though, to see if you (as in the user) is allowed to see the page. That access callback returns either 1 or 0 depending on if you're allowed to see it. So the patch introduces a check to see if the access callback actually does return 1.
#16
Yes, I think that explanation is about right... Although actually, this is probably a really bad example to learn from, since the dashboard is doing some odd things :)
But a bit more detail: If you look at the list of dashboard module menu items, you see this code:
<?php$items['admin/dashboard'] = array(
'title' => 'Dashboard',
'description' => 'View and customize your dashboard',
'page callback' => 'dashboard_admin',
'access arguments' => array('access dashboard'),
);
?>
The last part tells Drupal that only users with the 'access dashboard' permission can view the page. When the page is actually visited, the menu item gets processed to add a new element 'access' which is set to 0 or 1 (or maybe FALSE or TRUE - same thing basically) depending on whether or not the current user actually has that permission.
Drupal automatically uses this information to replace the main page content with the "Access Denied" message when necessary. However, the other blocks on the page besides the main page content still need to be displayed; that's how "access denied" pages still get things like menus, the user login block, etc. For most pages in Drupal, that automatic behavior is enough and is exactly what you want.
In the case of the Dashboard module, the dashboard content is actually added in these other page regions (not part of the main page content) - basically in order to interface with the Block module's method of managing page regions. So it has a custom function dashboard_is_visible() that is called from various places and determines when to add all the dashboard content to the page. So from the patch:
- $is_visible = isset($menu_item['page_callback']) && $menu_item['page_callback'] == 'dashboard_admin';+ $is_visible = isset($menu_item['page_callback']) && $menu_item['page_callback'] == 'dashboard_admin' && !empty($menu_item['access']);
Previously it just checked the 'page callback' from above - to see if we are on the correct page. And afterwards, it checks the 'access' property too, to make sure the current user is actually supposed to be able to see it.
#17
Re #14:
I think it's OK since it's specifically checking the page callback, not the URL, so it shouldn't matter what happens there. We are basically just saying that dashboard_is_visible() should return TRUE only when we are visiting whichever menu item is trying to display the dashboard (and if the current user has access to that menu item).
#18
Wow, thanks. #16 is a *really* good explanation!
#19
Excellent, thanks for this to you both :)
#20
I recommend that we include some of the explanation in #16 in the PHPdoc of dashboard_is_visible(). I do think, though, if we want Drupal core to support much more advanced layouts/regions, that this needs to be addressed in a better way. Might be a good topic for the Drupal Developer Summit in SF.
I think it could also be a security concern. Menu routers are applied to paths (i.e. pages or URLs) so one would expect the entire page to be protected. It doesn't make sense to apply it to one region only.
#21
Needs work to add the comments then. Agree that this is a summit-worthy topic :)
#22
Here's a patch with code comments.
Also marking this as critical, since it's a (minor) access bypass vulnerability.
#23
Patch looks fine but this looks like it a test would be easy to write and quite useful.
#24
Subscribing.
#25
I'm writing a test.
#26
Here's the patch with a corresponding dashboard.test
#27
Yay, the first dashboard module test!
But...
+ $this->assertNoText(t('Access denied'), t('Admin has access to the dashboard.'));+ $this->assertRaw($custom_block['title'], t('Admin has access to a dasboard block.'));
For the first one, I'd use assertResponse() - check for 200 vs 403 - rather than looking for text on the page. For the second one, "dashboard" is misspelled. (And the same thing goes for the similar block of code that appears in the patch below this one.)
#28
sub
#29
Reroll #22 and #26 against CVS, with suggestions from #27.
#30
Oops, didn't get my changes regarding #27 part 2 into that patch.
#31
The last submitted patch, 716302-dashboard_access-30.patch, failed testing.
#32
#33
Thanks!
Found a couple minor issues:
files[] = dashboard.moduledependencies[] = block
+files[] = dashboard.test
I think this should be moved up one line so that the files[] stuff is all together.
+ * @see dashboard_block_info_alter()This is a out of date (due to API changes in the interim). It should now refer to dashboard_block_list_alter() instead.
+ * Tests for the dashboard moduleSentence should end in a period.
+class DashboardTestCase extends DrupalWebTestCase {+
+ public static function getInfo() {
(very minor) Probably no need for a space in between these.
+ 'description' => 'Test access control to dashboard',This should be a complete sentence - I'd suggest maybe "Test access control for the dashboard."
+ function testDashboardAccess() {+
+ // Add a new custom block to a dashboard region.
(very minor) Probably no need for a space in between these either.
#34
Suggestions from #33, check application against CVS.
#35
Awesome... dashboard.test! A couple suggestions didn't make it into the patch...
+ * @see dashboard_block_info_alter()should now refer to dashboard_block_list_alter() instead.
+ 'description' => 'Test access control for the dashboard',should end with a period.
fix those and it's RTBC.
#36
Oops. Missed those.
#37
#38
#39
+++ modules/dashboard/dashboard.test@@ -0,0 +1,54 @@
+ theme_enable(array('stark'));
+ variable_set('theme_default', 'stark');
+ variable_set('admin_theme', 'stark');
+ }
It is not clear why we fiddle with the theme?
#40
I assume it's because the bug above only happens under those circumstances. And it's certainly the case that the test will (erroneously) pass even without the rest of the patch, if the code were taken out...
I rerolled with some code comments attempting to explain that, and also moved it into the test function itself since it's so intimately tied to that particular test. I also renamed the overall test case to "Dashboard access" since that's specifically what it's testing (and is what the description already said).
#41
Er, scratch that - I only did the second of those two things, not the first :) I was going to do the first, but then realized the second would be better. Given that the test case is now specifically only for dashboard access, it is OK to leave this code in the setUp() method, since its purpose is to be able to correctly do dashboard access tests.
#42
I'm thinking that's a good enough explanation :)
#43
Nice. This looks great, adds tests, has awesome comments, and nails a critical at the same time! What's not to love? :)
Committed to HEAD!
#44
Automatically closed -- issue fixed for 2 weeks with no activity.
#45
The fact that the dashboard tests added here were never committed!! :)
These still pass when run locally and were already RTBC above as part of the original patch, so I'm boldly setting this directly to RTBC.
#46
Seems like if all that is left is tests then it's no longer critical, no?
#47
D'oh! My bad. :( Committed to HEAD for real this time. ;) Thanks, David!
#48
Automatically closed -- issue fixed for 2 weeks with no activity.