[wp-trac] [WordPress Trac] #36376: current_user_can/has_cap fails when user has multiple roles
WordPress Trac
noreply at wordpress.org
Mon Dec 4 07:25:12 UTC 2017
#36376: current_user_can/has_cap fails when user has multiple roles
-----------------------------------------------+---------------------------
Reporter: mikejolley | Owner:
Type: defect (bug) | Status: new
Priority: normal | Milestone: Awaiting
Component: Role/Capability | Review
Severity: normal | Version:
Keywords: has-unit-tests dev-feedback close | Resolution:
| Focuses:
-----------------------------------------------+---------------------------
Changes (by dd32):
* keywords: has-patch has-unit-tests dev-feedback => has-unit-tests dev-
feedback close
Comment:
This is a fun bug involving a very little known functionality of roles I
believe. (I could be completely off track here though)
WordPress roles are rather over complicated for how simple they look on
the surface.
In WordPress, a user can be assigned to any number of roles (A Role being
a group of capabilities) AND any capabilities directly.
Ie. User bob can be in the roles `Role1`, `Role2` and have the capability
`Cap99`.
Roles are groups of capabilities, but they're not just which capabilities
that should be added to a user, they can also explicitly deny a user that
capability. ([attachment:fix.36376.diff] removes this functionality I
believe).
ie. `Role2` could specifically say `You shalt not have Cap99, even if you
already do.`.
The problem I think may be occurring here, is two fold - 1) Someones role
is denying a cap, and the author didn't realise they were doing so and 2)
the order of operations here is special.
The following example demonstrates it kind of:
{{{
<?php
add_role( 'has_test_cap', 'Has the "test" cap', array( 'test' => true ) );
add_role( 'denied_test_cap', 'Denied the "test" cap', array( 'test' =>
false ) );
$user = new WP_User( 1 );
// Verify no caps = should be false.
var_dump( $user->has_cap( 'test' ) ); // FALSE (Correct)
// Add cap via role
$user->add_role( 'has_test_cap' );
var_dump( $user->has_cap( 'test' ) ); // TRUE! (Correct)
// Deny the cap via role
$user->add_role( 'denied_test_cap' );
var_dump( $user->has_cap( 'test' ) ); // FALSE! (Correct)
// -- User now has both roles, and is denied the capability.
// Add the cap directly:
$user->add_cap( 'test', true );
var_dump( $user->has_cap( 'test' ) ); // TRUE! (Correct)
// -- User now has both roles AND the capability, and is allowed to
perform the action
// Cleanup
$user->remove_role( 'has_test_cap' ); $user->remove_role(
'denied_test_cap' ); $user->remove_cap( 'test' );
echo '<hr>';
// Lets add them in the reverse order now.
// Verify no caps = should be false.
var_dump( $user->has_cap( 'test' ) ); // FALSE (Correct)
// Deny the cap via role
$user->add_role( 'denied_test_cap' );
var_dump( $user->has_cap( 'test' ) ); // FALSE! (correct)
// Add cap via role
$user->add_role( 'has_test_cap' );
var_dump( $user->has_cap( 'test' ) ); // TRUE! (Correct)
// -- User now has both roles, and is is allowed to perform the action.
// Cleanup
$user->remove_role( 'has_test_cap' ); $user->remove_role(
'denied_test_cap' ); $user->remove_cap( 'test' );
echo '<hr>';
// Verify no caps = should be false.
var_dump( $user->has_cap( 'test' ) ); // FALSE (Correct)
// Add cap directly:
$user->add_cap( 'test', true );
var_dump( $user->has_cap( 'test' ) ); // TRUE! (Correct)
// Deny the cap via role
$user->add_role( 'denied_test_cap' );
var_dump( $user->has_cap( 'test' ) ); // TRUE! Wait, what? (Correct)
// -- User has denied_test_cap role, but yet is allowed to perform action
as capabilities take precedence
// Cleanup
$user->remove_role( 'has_test_cap' ); $user->remove_role(
'denied_test_cap' ); $user->remove_cap( 'test' );
remove_role( 'has_test_cap' );
remove_role( 'denied_test_cap' );
}}}
So clearly, the order to which the roles are assigned to a user is
important, and it's likely that roles being applied in different orders
when a role defines a capability as denied.
I don't think there's a bug here, I think it's working as intended, even
if a little funky at present.
The issue boils down to:
- If a user has a role which denies a capability, should it take
precedence over all roles (current and future) which allow it?
or
- If a user has a role which denies a capability, it should only take the
capability away from current roles, and future additional roles can allow
it
Based on the above, I'm fairly sure this ticket is `invalid` or `wontfix`.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/36376#comment:10>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list