Problem/Motivation
The user.group_permissions
cache context behaves unreliably in cases where Insider/Outsider permissions are involved. Some blocks on our site using this cache context are displayed (or not displayed) incorrectly for certain users.
After debugging, the reason seems to be that the GroupPermissionsHashGenerator::generateHash()
method is not taking into account group memberships for the account, producing the same hash for a member and a non-member in cases where otherwise the calculated permission sets of the 2 users are identical. As the cache context is using this hash, users might get incorrectly cached results after leaving/joining a group (or when another user leaves/joins a group).
Note: I'm not 100% sure if omitting the memberships from the hash is intentional or not, as I see there is a separate cache context for membership. My understandig is that the user.group_permissions
cache context should be invalidating caches when a user has a different set of permissions and that should take into account the membership status also.
Note 2: Although the issue is opened for Group 3.2.1 (as the issue was reproduced on 3.2.1), maybe it's worth mentioning that we encountered these issues on our project during upgrade from Group 1.6 to 2.2.1. During the upgrade we converted some Individual roles to Insider roles (in Group 1, we had to use 'normal' group roles that were automatically assigned to members having certain global roles, as the Insider role feature was not available). The caching issues appeared with these Insider roles, we didn't have them with the original Individual roles.
Steps to reproduce
Note: I chose the Primary tabs block, because it is available without the need of additional steps, and it's using the user.group_permissions
cache context. However, similar issues can be observed using any other block that only uses this cache context. For example: a views block listing members with group permission 'View individual group members' for access check, or a custom block that uses the cache context. Group module's Group operations block is not affected, because it always also adds the 'user' cache context that prevents 2 different users getting each others cached result.
Preconditions:
- fresh D9 install with Group 3.2.1 (didn't test on D10, but probably same)
- Internal Page Cache not enabled, Internal Dynamic Page Cache enabled
- the creator of the group is admin in the group (not relevant to the problem, just to complete the steps)
- Primary tabs block enabled (tested with Olivero front theme, but not relevant)
1. Create global role 'Teacher'
2. Create group type 'School'
3. Add 'Teacher' Group role to School group type: (scope: Insider, global role: Teacher)
4. Edit permissions for School group type, and assign the following:
- Teacher (Insider): 'Edit group'
- Outsider, Member: View published group
5. Add 2 users (A, B) with Teacher global role only (it is important, that the users have the same roles)
6. Create a new group (School group type) and add the 2 users to the group (don't assign any individual roles)
7. In 2 new separate browser sessions login with the 2 users and visit the group page: /group/1
Expected: The 'View' and 'Edit' tabs should be displayed for both users
8. Remove user A from the group
9. Check back to user A's browser and refresh the page
Expected: The tabs should not displayed (the user lost the permission to edit the group when removed from group)
Result: The 'View' and 'Edit' tabs are both displayed
10. Clicking on 'Edit' the message 'Access denied' should be displayed (this is expected, and the result of the access check working correctly)
12. Go back to /group/1 (with user A)
11. Rebuild the cache and refresh the page for user A
Expected: The 'View' and 'Edit' tabs are not displayed (cache rebuild 'fixed' the issue, the block is not displayed)
12. Switch to user B session and reload the /group/1 page
Expected: The 'View' and 'Edit' tabs should be displayed (this user is still a member and the Teacher insider role gives permission to edit the group)
Result: The tabs are not displayed.
The issue also exists across different groups:
Starting from step 7. again (A and B are both members of group 1):
8. Create a second group (School group type) and add only user A to the group
9. In browser for user A go to /group/2
Expected: The 'View' and 'Edit' tabs should be displayed.
10. In browser for user B go to /group/2
Expected: The 'View' and 'Edit' tabs should not be displayed (user B is not in this group).
Result: The tabs are displayed. (again, clicking on Edit results in Access denied)
Proposed resolution
For reliable results, the generated hash should take into account the user's group memberships IMO. I think that simply adding the list of group IDs that the user is member of to the array used for generating the hash would do the trick, but would like to hear the opinion of the maintainer.