Problem definition
Currently, group roles are either assigned, special (anon/outsider/member) or synchronized to a global role provided you're not a member.
The drawbacks of these last "advanced outsider permissions" are plenty:
- You lose them once you join a group
- A role is created for every global role on every group type (yuck)
- You have to assign permissions to a role that might be identical to a group role you already set up
Proposed alternative
We allow ALL group roles to indicate whether they belong to a Drupal role and if they apply to either outsiders, insiders or an individual member. A global role can be added to a group twice, once for outsider, once for insider. Then we convert the Anonymous, Outsider and Member group roles to these new versions.
Anyone with a synchronized role will automatically inherit the corresponding group role, like they do now, but they will now have the chance to also gain these permissions when they are a member of the group by inheriting a role from the insider scope. We will only allow one pair of scope/global_role per group type so you cannot set up the Anonymous group role twice.
Permission implications
The above will require a rewrite of the permission calculators and how they are cached. For example: outsider audience roles will now vary per user.roles cache context. The benefit is that the calculators are now agnostic of Group's internal kitchen and can be split off into its own module space.
The new scopes allow us to get rid of the huge IN conditions that list all of your membership IDs and instead write a few joins. After this rewrite, we know that all outsider audience roles will be granted based on your Drupal roles, so we can simply join the group_content data table and do an WHERE IS NULL to find all of the rows that state you're not a member. For that to work, we need to add 2 fields to a group_content data table: plugin and group_type. This is a denormalization that needs to happen because config entities are not stored in tables we can easily join.
| Comment | File | Size | Author |
|---|
Issue fork group-3204083
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
soul88Comment #3
kristiaanvandeneyndeUpdating the IS to provide way more details.
Comment #4
kristiaanvandeneyndeA key difference is how we will join said table to figure out which groups you have outsider permissions to:
As you can see, the former will join the groups table with all of the group memberships for user 3 or NULL if there is none. The latter will join ALL of the group memberships for all groups and then filter those owned by user 3. We definitely need the former.
This is because we want to be able to run a condition WHERE entity_id IS NULL so that we can find all of the groups the user isn't part of but are of the selected group types we know the user would have outsider access for. This should be a hell of a lot faster than an IN(thousands of memberships) condition on sites where people can join a large amount of groups.
Comment #5
kristiaanvandeneynde#3254311: Denormalize group_type and plugin_id for GroupContent entity and add indexes has landed and should make this issue easier to get done.
Comment #6
kristiaanvandeneyndeAs far as the roles are concerned, I've given it some more thought and I think we can do without special-casing anonymous. If we mark the role as non-member, we can still cache it using group.roles, BUT people can add extra calculators to it to perhaps give anonymous access to some specific groups (e.g.: free membership for one month to select groups).
The idea would be to have all roles flag as non-member or member and then add them to the calculated permissions as a "group type scoped" set of permissions. With the reworked table structure, we can then easily join on all of the group types that would give access and where the user is not a member of.
You can clearly see why we can roll 3 into 2 and 5 into 4 here: They have become the same thing :) We can still optimize the anonymous permissions also because we know they can never be a member, so all the query has to check for is group types, we don't even need to join the group_content_field_data table for those.
Comment #7
kristiaanvandeneyndeAlso, you should now be allowed to edit and delete the Member and Outsider role as you can always restore them. Probably the Anonymous role too, but I need to put some more thought into that.
Comment #8
kristiaanvandeneyndeFollowing up on #4, we can simplify the access query to the one shown there and then adding on the following:
Where we would make sure that the list of IDs is not populated with any ID that is of a type allowed by types_with_global_insider_access. That should keep the list of IDs smaller.
Comment #9
jozzy_a commented@kristiaan is this ticket going to bring in the functionality provided by the Group Outsider In module?
Comment #10
kristiaanvandeneynde@jozzy_a judging by that module's description: Yup.
Comment #11
brad.bulger commentedWould it be feasible to extend this to cover roles in other groups? The subgroup modules have something like that, I know. In our case, neither group is really a subgroup of the other, they're just related.
Comment #12
kristiaanvandeneyndeThis system would automatically work for Subgroup as it merely introduces one more calculator. It already does so in the current version and will continue to do so in the next.
Comment #13
brad.bulger commentedSorry, I worded that unclearly. What I meant is, just as a person has access like Group Role X if they have Drupal Role Y, would it be feasible to grant them access like Group Role X if they are members of OtherGroup Z?
But I've been rereading and I may have a more basic misunderstanding. "A global role can be added to a group twice, once for every audience." - would that mean that you would create two group roles for a given Drupal role, like "Drupal Role Y for Non-Members" and "Drupal Role Y for Members"? or that you have two connections/rules that apply to one Group Role X and one Drupal Role Y?
Comment #14
kristiaanvandeneyndeThat is correct. It is necessary for the use case where you want the old behavior or you want a global user to have different permissions based on whether they're in or out of the group.
That is how Subgroup works, yeah.
Comment #15
brad.bulger commentedOK, so if it is left to the subgroup modules, and the two groups are not hierarchically related, I need to use ggroup ("Subgroup(Graph)") to set up that access connection. The Group module itself will only deal with Drupal sitewide roles. I wanted to understand that better. Thanks.
Comment #16
kristiaanvandeneyndeThis should break a bunch of tests, especially those that still use now removed constants, those that rely on default group roles being created, etc. These are good things as we needed to refactor those tests anyway. But let's see just how much we can break :D
Comment #18
kristiaanvandeneyndeError: Call to a member function grantPermission() on nullis because the roles no longer get automatically created, which is a good thing.Fixed a bunch of unit tests already. Going to use a merge request branch from this point forward.
Comment #20
kristiaanvandeneyndeSo all that's left is to fix the field_storage going missing in Views tests, fix the EntityOperationsTest and move the query tests to new places while refactoring them. And then handle the todos, perhaps in follow-ups.
Comment #21
kristiaanvandeneyndeComment #22
kristiaanvandeneyndeThis patch is getting way too huge, I would normally reject that right away, but screw it. No-one's reviewing this beast and I'll make sure to write all the change records before this is marked fixed. Anyways: Introduced a base Views test class and fixed some issues.
Comment #23
kristiaanvandeneyndeI removed the chain permission calculator test and moved the code to specific tests per calculator. We might want to unit test the chain calculator, though, with dummy calculators.
Comment #24
kristiaanvandeneyndeOkay so I significantly updated the code for group and group content query alters and their tests. We should definitely have a follow-up issue where we look at EntityQueryAlter and also upgrade it accordingly.
If the tests go green (which they should), then the only thing that remains is to create smaller follow-up issues for all the todos added in this patch and to write a bunch of change records for the changes introduced here.
Comment #25
kristiaanvandeneyndeUpdated title and IS to better reflect what happened here.
Comment #26
kristiaanvandeneyndeComment #27
kristiaanvandeneyndeThings that need a change record:
Group role:
Calculators:
Query access:
Various:
Issues that need to be created:
Comment #29
kristiaanvandeneyndeCRs and follow-ups created, let's get this in.