Up until now, Group had several ways to check for someone's permissions; be it Group::hasPermission(), GroupMembership::hasPermission(), etc. The same logic was usually repeated in each of these implementations, leading to a large amount of similar code and, consequentially, a good chance of security issues stemming from oversights/bugs.
New permission related services
In order to fix this, the logic for both gathering someone's permissions and for checking access has been centralized in two new services. THe gatherer is called the group_permission.chain_calculator service and the checker is called the group_permission.checker service.
1. The permission calculator service
You can find its interface at \Drupal\group\Access\ChainGroupPermissionCalculatorInterface.
It allows you to calculate all of the group permissions for an account, whether that be anonymous, outsider or member. All of these calculations are then cached using a very optimized caching system, leading to fast retrieval of someone's group permissions.
The permissions are returned as a \Drupal\group\Access\CalculatedGroupPermissions value object, which contains a series of
\Drupal\group\Access\CalculatedGroupPermissionsItem value objects. The latter stores the account's permissions for one item of a given scope.
The currently defined scopes are group_type and group. So a CalculatedGroupPermissionsItem for scope group, identifier 1 represents the account's permissions in group with ID 1. Likewise, an item of scope group_type, identifier 'class' represents the account's permissions for all groups of type 'class'. The group_type scope is generally used to represent someone's "anonymous permissions" or "outsider permissions".
2. The permission checker service
You can find its interface at \Drupal\group\Access\GroupPermissionCheckerInterface. All methods that used to check permissions now call this service and so should you when checking for someone's group permissions.
One thing of note is that it first checks whether you have a CalculatedGroupPermissionsItem for the specific group scope that is requested and then searches for a fallback CalculatedGroupPermissionsItem that covers the requested group's type. This is how it's always been, but with the new services it's made much clearer to understand.
Refactored classes
Because of this new permission layer, a lot of classes could be optimized.
GroupPermissionsHashGenerator
This class can now quickly generate hashes because the calculated permissions come from a cache. As an added bonus, it can therefore statically cache the generated hash per user because most of the cache heavy-lifting is done in its stead by the permission calculator.
In order to refactor this properly, a backwards incompatible change was made. Please read the separate change record on that here: https://www.drupal.org/node/3035042
GroupPermissionsCacheContext
This class has become a mere wrapper around the hash generator because calculated permissions can pass along cacheable metadata and thus remove the need for the cache context to copy that logic.