Issue:
SimplesamlphpDrupalAuth::roleMatchAdd currently will add missing roles if role.eval_every_time is true, but it will not remove roles that are no longer mapped to the user.
Proposed Solution:
Option A:
update roleMatchAdd() to also remove roles
Option B:
Split out role.eval_every_time into 2 separate config options for "add roles" and "remove roles", and:
create SimplesamlphpDrupalAuth::roleMatchRemove and call from SimplesamlphpDrupalAuth::externalLoginRegister if "remove roles" is true
Or:
add a boolean param to SimplesamlphpDrupalAuth::roleMatchAdd to remove roles or not, and update SimplesamlphpDrupalAuth::externalLoginRegister to add the param, set to "remove roles" value
I could code and submit a patch for either approach. Option A is much simpler and matches what "Reevaluate roles every time the user logs in" says it will do.
| Comment | File | Size | Author |
|---|---|---|---|
| #46 | 2894327-check_and_remove_roles-46.patch | 4.74 KB | undertext |
| #46 | 2894327-check_and_remove_roles-46-test-only.patch | 1.41 KB | undertext |
| #44 | 2894327-check_and_remove_roles-44.patch | 3.8 KB | julien_g |
| #43 | 2894327-check_and_remove_roles-43.patch | 3.77 KB | timcosgrove |
| #38 | interdiff-2894327-32-38.txt | 461 bytes | timcosgrove |
Comments
Comment #2
nielsvoo commentedSubscribe, indeed the roles are still available for the user while not in the attribute anymore. Will there be a solution soon?
Thanks
Nielsvoo
Comment #3
rjg commentedI can code & submit a patch for option A or option B above, but first I am looking for maintainer feedback on which approach they would prefer.
Comment #4
nielsvoo commentedOk,
i think option A is much better, why keep bindings on a user role while the source disabled them? Can you add the patch?
Thanks in advance
Comment #5
rjg commentedSubmitting a patch which will remove roles not mapped via SAML (ignoring locked roles), while adding SAML mapped roles (if
role.eval_every_timeis enabled). Though,roleMatchAdd()maybe should be renamed torolesUpdate()orrolesEvaluate().Patch should work with 8.x-3.0-rc2 & 8.x-3.x-dev.
Comment #6
rjg commentedComment #7
rwmanos commentedWe tested the patch in #5 and it works very well. The roles are properly reevaluate and no regression appears. Thanks rjg for the patch.
Hope it will be merged soon.
Comment #8
Eduardo Alvarez commentedI guess the issue exposed here was going to be implemented on the module, otherwise I don't understand the following sentence on the module's configuration form:
We have also tested patch #5 and makes the reevaluate functionality complete (by adding and/or removing roles)
Any plan to have it included in a newer version of the module soon?
Many thanks.
Eduardo
Comment #9
Eduardo Alvarez commentedComment #10
szeidler commentedThis is a quite interesting issue, as our current logic is based on roles being only added and not removed, because there are also existing local accounts with additional roles. But as pointed out in #8, this behavior seems to be a bug and has a misleading form description.
I would really like to see a change-record and message via hook_update_n(), when this issue is going to be merged into the module. Otherwise there will be some site logics silently get broken by the update.
Comment #11
rwmanos commentedI have created the change record: https://www.drupal.org/node/2939077
Can someone review it?
Comment #12
timcosgrove commentedAdded a rerolled patch that adds an empty update hook to provide messaging.
You may consider the change record at https://www.drupal.org/node/2939077 to be RBTC by me. This modified patch should be reviewed and RBTC'd.
Comment #14
timcosgrove commentedRerolled the patch applying the Codesniffer fixes. I do not have time at the moment to troubleshoot the other test failures.
Comment #15
timcosgrove commentedComment #17
nielsvoo commentedAny progress on this issue? @szeidler. I thought about your comment "This is a quite interesting issue, as our current logic is based on roles being only added and not removed, because there are also existing local accounts with additional roles"
What about combining reevaluate with exceptions of some roles, you could show a list of all roles that doesn't reevaluate at log in.
thanks
Comment #18
szeidler commentedThis could be a good option to distinguish between Drupal specific local roles and roles, that make absolutely sense to be controlled from the simplesaml endpoint.
Thanks for your change record @timcosgrove. I sounds fine for me.
Comment #19
dpagini commentedHere is a re-roll against latest, that hopefully addresses the failing tests. I'm just fixing the tests, I'm not sure there is appropriate addition here for testing the new code...
I'm also not sure how the last few comments relate to this patch and if there are changes needed. I just wanted to provide a passing patch ideally.
Comment #21
dpagini commentedHmm, I'm at a bit of a loss. My patch is running for me via the simpletest UI with no issues.
Comment #22
rjg commentedFor discussion: expanding on #5 (hopefully will find time soon to integrate with the other contributions), in the attached updated patch `roleMatchAdd()` will read from `$this->config->get('allow.default_login_roles')` to make sure those roles are not removed, and are added to the user account.Without this new logic, default login roles only applies to account creation (aka one-time; on first login). So this assumes that if default roles change, the change should get applied to all SAML users, new and existing.Update: this was not the correct usage of `allow.default_login_roles` which is:
Comment #23
rjg commentedIgnore the patch from #22 for now. Needs work, in addition to incorporating the improvements submitted by others.
Comment #24
dpagini commentedI think I see the typo for the test... as I mentioned, this is still running locally for me, so hopefully this will pass the tests now.
Comment #25
dpagini commentedOk, sweet... that typo was the problem. Not sure what to do with this one now. Going to put it back to "Needs review" - but it seems @rjgwiz found another problem here, so this may make more sense as "Needs work"? I think if we add the changes from #22, we'll probably also have to add testing for those changes as well...?
Comment #26
rjg commentedComment #27
trwill commentedComment #28
trwill commentedComment #29
trwill commentedComment #30
trwill commentedIs this needed? Param $account appears to be an instance of `UserInterface` already - note further down previously it was calling `::addRole` so I don't think it was an `AccountProxyInterface`
Comment #31
trwill commentedComment #32
dpagini commentedGood point @trwill. Removed the bonus call to load the user object from the user object we already have.
Not sure what's going on with these files, but I'm going to hide the old patches and only display this latest.
Comment #33
tannerg commentedThis patch works for me.
I conducted the following test
1) Added config to add role_x to user_y user based on an attribute
2) Logged in as user_y via simplesamlphp_auth
3) Verify the new role is added to user_y
4) Remove config to map role_x to user_y
5) Log out as user_y
6) Log back in as user_y
7) Find role_x removed from user
Comment #34
adamschan commentedIf there is an option for the site admin to select which roles to be "deleted" or "excluded from deletion", it would help a lot. The option can be in the form of prefix, or in a comma-delimited list.
The scenario is that the IdP may not have roles specifically created for each of the Drupal sites. They only have generic roles such as staff. It is very likely that someone logged in via the IdP would be manually given a role in Drupal (administrator, or admin of any content types). If deletion is applied to every role evaluation, that will render their manually assigned "admin" roles be deleted too.
Comment #35
rickward commented@adamschan - I think that's a good suggestion. You should probably open a new feature request issue to support it.
Comment #36
trwill commentedThe new patch looks good and works for me as well - bumping to RTBC \m/
Comment #37
gg4 commentedIs providing an update hook to point to a CR a pattern used in core or elsewhere? I think we should avoid adding update hooks in patches when not 100% necessary as it could greatly complicated the normal update operations of the module
Comment #38
timcosgrove commentedI added that part of the patch, and bonus is right, there are scenarios in which using patches ahead of merge into the project can be problematic. This issue has sat for many months, so happy to remove the update hook.
Comment #39
gg4 commentedThanks, @timcosgrove. To qualify my comments in #37 a bit; when relying on patches that include update hooks, site admins also assume the burden of maintaining an upgrade path. If a site admin was to apply #32 and a change was introduced into the mainline branch that included a competing
simplesamlphp_auth_update_8003, that change would never run. The patch in #38 (or similar w/o an update hook) seems like a good option for users applying this patch until #32 is considered and accepted into the project. I am still curious if there is another best practice is for these types of in-update notifications rather thanhook_update_n.Edit: hook_post_update_NAME (CR) might be a much better option. Noting in contrib, Webform uses
drupal_set_messagebanners to announce important changes on admin pages.Comment #40
gg4 commentedTests are failing on both #32 and #38.
Comment #41
gg4 commentedFailures are on the main 8.x-3.x branch as well so not related to these patches.
Comment #42
berdirI realize this is an existing issue, but wondering if we should optimize it while touching this. This always saves the user, and it's only minimally worse than before when not matching any roles didn't save.
But it would be pretty easy to add a boolean flag that is set when a change actually happens and then only save if necessary.
Also agreed with not having an update function for this.
Comment #43
timcosgrove commentedRerolling patch #38 against current HEAD.
#32 introduced an unnecessary method argument name change from
$accountto$user; undid this change while leaving the intention of the change (not loading the user object redundantly).I also addressed Berdir's concerns about only saving the user object when needed (#42).
Reroll, so no interdiff.
Comment #44
julien_g commentedHi,
just added a test on matching roles variable to remove all roles if the user is not any more in AD Groups
Comment #45
berdirThe method is called roleMatchAdd(), neither the method name nor the description say anything about removing roles, so that's a bit weird. Not sure what to do about that. At last update the description?
that said, the UI explicitly mentions that this might remove role, so it makes sense to actually do so.
I think you can simplify that a bit to foreach (array_diff($current_roles, $matching_roles) as $role_id).
that will loop over all roles in $current_roles that aren't in $matching_roles and you can skip the inner condition.
Shouldn't this also have an assertion on removeRole()? I don't think this actually fails yet without the new code?
Comment #46
undertext commentedChanged a patch according to Berdir's review:
1) Renamed a
roleMatchAddmethod toroleMatchSync, changed method description.2)
array_diff()is used now. Additionally, the cycle withaddRolechanged to behave the same. Previously there was no check if role is already assigned before adding it to user object.3) Changed unit test.
Comment #47
ben.hamelin#46 worked for me and looks good. Tested for syncing of single and multiple roles.
Moving to RTBC.
Comment #48
berdirHm, that's not quite what I meant with my comment on the method name. Technically this is an API change, but I assume that it's pretty unlikely that someone is using that directly or even overwrote it.
So, lets hope we don't break someones site with this ;)
Comment #51
Vapor52 commentedI have a function that hooks into the simplesamlphp_auth_user_roles_alter so we only sync on roles we select
$allowed_roles = array('role1', 'role2', 'role3', 'etc');
foreach ($allowed_roles as $rid) {
$account->removeRole($rid);
}
This stopped working with the update, but I can change $current_roles to $allowed_roles in the roleMatchSync function to achieve the same thing. It would be great to have a configuration to select which roles we want to revaluate on login. (I know this feature has been requested so I won't put it in again)
If there is a way to add this via a patch that would also be great, I'm just unsure how to do that.
Thanks!
Comment #52
berdirWell, that was obviously going to happen: https://twitter.com/bojan_zivanovic/status/1209477975533588480
I'm not sure I quite understand though. If you don't like the behavior, you can always disable the setting and do whatever you want yourself? there's also the other, more generic mapping hook, I'm using that in a project to map some but not other roles. If that's not enough then feel free to create a new issue.
I'm trying to avoid adding too many complex settings, but try to allow full flexibility through hooks and so on.