Support from Acquia helps fund testing for Drupal Acquia logo

Comments

chrisrockwell created an issue. See original summary.

chrisrockwell’s picture

TR’s picture

Can you explain what the problem is with those lines you changed in the patch? Clearly it used to work - is this just a PHP version issue or ... ?

I would also be very useful if you could add a test case for this.

chrisrockwell’s picture

Hi TR. The issue is that this conditional will never be true:
if (!in_array($rid, array_keys($edit['roles'])) && $rid != DRUPAL_AUTHENTICATED_RID) as $edit['roles'] has every role available.

I can't speak to whether or not it worked before but, thinking about it, it's possible that it didn't and no one noticed. I came across this with what I would consider an extreme edge case:
1. Admin was removing a role the Drupal way
2. If site user didn't have role but there was an expiration set for this role on this user, a custom module was granting the role(s) again when the site user visited their 'dashboard'

Because this custom module was checking the expiration, it led me to this implementation of hook_user_presave(). I can imagine that a site admin would think they were revoking a role the Drupal way and never check - the role would be revoked, but the expiration would not be. Therefore, it could stand to reason that role expirations were never properly done but, because the role was revoked, no one was ever the wiser. If there is an "expiring roles" admin report of sorts, I would imagine it would be incorrect.

I am yet to write test cases so I'll try to get some decent ones for you.

TR’s picture

OK, sounds good.

If $edit['roles'] contains all roles, then couldn't we also eliminate the first two tests here

if (isset($edit['roles']) && is_array($edit['roles']) && isset($account->roles)) {

(authenticated and administrator are always present, so the $edit['roles'] should always exist and always be an array)

and perhaps just change

!in_array($rid, array_keys($edit['roles'])

to

!$edit['roles'][$rid]

That would seem to be simpler than your fix.

chrisrockwell’s picture

Yes it would be :). I went ahead and put it in a patch

TR’s picture

Status: Active » Needs review
FileSize
745 bytes

Here's the patch in the correct format - if the testbot likes it I'll commit.

TR’s picture

Version: 7.x-3.8 » 7.x-3.x-dev

Status: Needs review » Needs work

The last submitted patch, 7: 2589043-7.patch, failed testing.

TR’s picture

Category: Bug report » Task

It would have been nice to have a testable patch sooner so this issue didn't have to hang around so long before something was done about it.

Apparently, this statement:

$edit['roles'] has every role available

is not true. That's why the patch is failing. Given that, I don't see what is wrong with the original code. Perhaps a contributed module that you have installed is doing something to $edit['roles'], which is populated by the core User module and which should only contain the roles that are assigned to that user.

I'll try to write a test to verify if this code works or doesn't work correctly, but currently I don't see a bug. Moving to 'Task' until the test is available.

chrisrockwell’s picture

Category: Task » Bug report
Status: Needs work » Needs review
FileSize
643 bytes

Sorry about this TR, I completely forgot about this after implementing a fix. I think this is definitely a bug so I'm going to switch it back (if only to make sure you see it again), I apologize if that's out-of-order for me to do.

To restate the issue: If a user has an existing expiration (stored in uc_roles_expirations) and an admin manually removes the role by unchecking it on the user profile form, that expiration is not removed from uc_roles_expirations which would be the expected behavior as indicated in uc_roles.module.

The issue is only somewhat mitigated by the fact that the user no longer has a role, so access checks work as expected. However, in a situation where an admin removes a role that had an expiration date with 20 days left, for example, and then the user makes another purchase which grants that same role - that purchased expiration date is *added* to the existing expiration (the one that should have been deleted).

Unfortunately, I only tested this on the user profile form - in that case my statement is true: `$edit['roles'] `always contains every role except for anonymous (#options on that form element is built using `user_roles(TRUE)` ). Therefore, the role expiration will never be deleted if done from the user profile page.

However, I failed to consider when that form is submitted programmatically with a direct call to user_login_submit(). If the $form argument is passed as an empty array(), then we get the error the test produced. In this instance, your modifications to the patch actually delete the role expirations when they should not be.

I'd like to propose the attached patch which accounts for the valid use case of calling `user_login_submit(array(), $form_state)`. It just maintains the old conditional around your revision to my patch.

To reproduce this I just did a fresh install with only these modules:

chrisrockwell@RockwellMBPro  ~/Sites/quick-drupal-20160220154705/drupal-7.x/sites/all/modules/ubercart  drush pml --status=enabled --no-core
Package                     Name                          Type    Version
 Chaos tool suite            Chaos tools (ctools)          Module  7.x-1.9
 Development                 Devel (devel)                 Module  7.x-1.5
 Other                       Entity API (entity)           Module  7.x-1.6
 Other                       Entity tokens (entity_token)  Module  7.x-1.6
 Rules                       Rules (rules)                 Module  7.x-2.9
 Rules                       Rules UI (rules_admin)        Module  7.x-2.9
 Ubercart - core             Cart (uc_cart)                Module  7.x-3.9+19-dev
 Ubercart - core             Order (uc_order)              Module  7.x-3.9+19-dev
 Ubercart - core             Product (uc_product)          Module  7.x-3.9+19-dev
 Ubercart - core             Store (uc_store)              Module  7.x-3.9+19-dev
 Ubercart - core (optional)  Roles (uc_roles)              Module  7.x-3.9+19-dev
 Views                       Views (views)                 Module  7.x-3.13

Status: Needs review » Needs work

The last submitted patch, 11: 2589043-11.patch, failed testing.

chrisrockwell’s picture

I'll need to spend some time going through the tests; if it's getting caught on offset 1 I think that means it's checking anonymous users as well, should it be? (I'm sorry, I'm completely unfamiliar with tests so it'll take me sometime to figure it out). It looks like a new method in uc_roles.test would be the correct place to start.

TR’s picture

Status: Needs work » Needs review
FileSize
2.58 KB

OK, here's a patch which adds a test function to uc_roles.test.

What the new test does is to create a role, attach a role to a product, purchase that product, check that the user now has the role. So far so good, that stuff is already tested. Now comes the new stuff:

1) First verify that the "Ubercart roles" fieldset shows up on the user edit page, and verify that the user has a role expiration listed there.

2) Now delete the role from the user edit page by unchecking the role and submitting the changes. Verify that the role was removed from the user entity.

3) Lastly, see if the role expiration was removed from the "Ubercart roles" fieldset.

#3 fails, which is the subject of your bug report, which I can now confirm. (I am setting this issue to "needs review" so the testbot will run this new test, but the expected result is 1 FAIL which demonstrates the current bug.)

The next step is to create a patch which includes this new test and makes this new test and all other existing tests PASS. You can do that by concatenating your patch with my patch - make sure you don't add any blank lines in there anywhere.

The patch in #11 doesn't work - it causes the existing tests to fail.

Status: Needs review » Needs work

The last submitted patch, 14: roletest.patch, failed testing.

chrisrockwell’s picture

Status: Needs work » Needs review
FileSize
3.31 KB

This checks to make sure uc_roles even has control over the role in question; I thought about doing if ($rid != DRUPAL_AUTHENTICATED_RID && $rid != DRUPAL_ANONYMOUS_RID && !edit['roles'][$rid] but I feel the anonymous user rid should never be there anyway as uc_roles shouldn't have the ability to take that role away.

gaurav_manerkar’s picture

Hi i had same issue

gaurav_manerkar’s picture

Assigned: Unassigned » gaurav_manerkar
Priority: Normal » Critical
Status: Needs review » Postponed
FileSize
3.29 KB

My patch works fine

gaurav_manerkar’s picture

Status: Postponed » Needs review
FileSize
3.29 KB

My patch works fine

chrisrockwell’s picture

@gauravmanerkar, did #16 not work?

DamienMcKenna’s picture

#19 is a duplicate of #18, and #18 is the same as #16 with the exception of a file permissions change.

TR’s picture

Assigned: gaurav_manerkar » Unassigned
Priority: Critical » Normal

@gauravmanerkar: What we need is a review of the patch in #16. I'm not sure why you posted a patch that is basically a duplicate of #16, as that doesn't help us move this forward.

@chrisrockwell: Actually, I had intended to commit your patch last month, but this issue fell off my radar. I'll get around to it this weekend when I have time to do some work.

TR’s picture

Version: 7.x-3.x-dev » 8.x-4.x-dev
Status: Needs review » Patch (to be ported)

Committed the fixes in #16. Thanks for the bug report and the work on the patch.

The patch and the test need to be ported to 8.x-4.x.

  • TR committed c18254a on 7.x-3.x
    Issue #2589043 by TR: Add test to ensure expiring roles are deleted if...
  • TR committed eebff0b on 7.x-3.x authored by chrisrockwell
    Issue #2589043 by chrisrockwell: Expiring roles are not deleted if...
TR’s picture

Version: 7.x-3.x-dev » 8.x-4.x-dev
Status: Needs review » Patch (to be ported)

Hmm, not sure how/why this issue version and status was reverted from the values I set in #23 ... setting them back to what they should be.

TR’s picture

Marked #2770193: Notice: Undefined offset: # in uc_roles_user_presave() as a duplicate, but it does point out a potential problem with the fix for 7.x-3.x.

chrisrockwell’s picture

I can reproduce #2770193 and I see why.

1. user_admin_account_submit() calls user_multiple_role_edit(), which only passes the roles the user *should* have (see the array_diff at line 3329).
2. user_profile_form_submit() passes #edit['roles'] with all available roles, in the format 'role_id' => (bool)

To prevent the warning, we need to check the following:
1. The role id is not in $edit['roles']
2. The role id is in $edit['roles'] but is FALSE

There is an alternative to the now-3-part conditional, and that's to use hook_user_update() which always includes *only* the roles the user should have. In that case we could just use array_key_exists.

chrisrockwell’s picture

I'm not sure what to change the status to on this (needs review and back to 7.x?)

TR’s picture

Version: 8.x-4.x-dev » 7.x-3.x-dev
Status: Patch (to be ported) » Needs review

Yup.

The last submitted patch, 2: ubercart-roles-expiration-not-deleted-2589043-2-7.x.patch, failed testing.

The last submitted patch, 6: ubercart-roles-expiration-not-deleted-2589043-6-7.x.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 27: 2589043-27.patch, failed testing.

chrisrockwell’s picture

Fixed path

chrisrockwell’s picture

Status: Needs work » Needs review
chrisrockwell’s picture

TR’s picture

OK. Would you be able to modify the existing test or add a new test to trigger the failure? That way we won't fall into the same trap when we refactor this code (and it really does need some work...).

chrisrockwell’s picture

This should produce one undefined offset exception in uc_roles_user_presave on line 340.

chrisrockwell’s picture

This one includes test (#37) and patch (#33).

The last submitted patch, 37: 2589043-37.patch, failed testing.

TR’s picture

Awesome. Thanks! I'll commit that when I get back next week - I only have my phone right now and will be in a no reception area until next Monday.

TR’s picture

Status: Needs review » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 38: 2589043-38.patch, failed testing. View results

TR’s picture

Status: Needs work » Needs review
GUE-Andreas’s picture

This problem just appeared on our site a few days ago, seemingly out of the blue (meaning, I haven't updated or installed any modules).

EDIT (previous explanation removed):
Flushing the CSS and Javascript cache separately seems to have fixed it.

EDIT 2:
Problem returned, but more selectively. The expirations are deleted if I remove a role from my own account (user ID 1), but not from any other account.

Any ideas of what might be going on here..?

Thanks in advance.

cmseasy’s picture

I made a simple rule with the rules module to solve this:

{ "rules_bij_annulering_gebruikersrol_weghalen" : {
"LABEL" : "Bij annulereing de gebruikersrol verwijderen",
"PLUGIN" : "reaction rule",
"OWNER" : "rules",
"TAGS" : [ "User" ],
"REQUIRES" : [ "rules" ],
"ON" : { "user_delete" : [] },
"DO" : [
{ "user_remove_role" : { "account" : [ "account" ], "roles" : { "value" : { "3" : "3" } } } }
]
}
}