-
Notifications
You must be signed in to change notification settings - Fork 16
Check group content entity operation access #217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8de919a
73d803b
551fa5d
764cf74
78fbae2
78ee472
a4b2de2
ab4159d
4167dfe
d6491e1
15611c7
3096020
970b78c
9a7ef40
aa14a43
c79fd2e
b886f85
291aa23
4736124
e35e3be
a2c2c4d
e21cec4
38bc56d
46f9cb5
c97e381
7bbca54
2362ffe
fba96ed
6c51593
7c238c4
ff655f9
7c6a46c
e820655
47b2f0c
6d666de
4f1b190
297e6c1
517224b
f1cdff3
bf8c41c
d600774
60a2aa9
4b0650b
2d21042
d55ba51
635227d
b7fce65
5125496
fd88473
2323d35
9f8815c
25a6c3b
9a9ee7d
b32285e
d14bb31
101f8ce
7a21262
f8e5779
bbc5f94
db804e6
e11a717
0d89fba
3394ad5
b03c5b7
fe9170d
463ef9f
7a30183
5bf96e7
7078165
878164f
a42b96a
c63d48b
74dac29
2128386
1fbba71
8dd3c9b
7796f5a
6f64986
2f2caa7
a841f2c
b2e37e8
37d2216
2a67361
f6c5af0
774e5c9
370d543
0694f45
9cf7f4d
2063574
82f016f
768cbbe
b7df48b
a2ffe60
cad927c
ea97a93
d1e6ebc
a9622bb
bbceea5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ | |
| use Drupal\Core\Extension\ModuleHandlerInterface; | ||
| use Drupal\Core\Session\AccountInterface; | ||
| use Drupal\Core\Session\AccountProxyInterface; | ||
| use Drupal\og\Entity\OgRole; | ||
| use Drupal\user\EntityOwnerInterface; | ||
|
|
||
| /** | ||
|
|
@@ -62,6 +63,22 @@ class OgAccess implements OgAccessInterface { | |
| */ | ||
| protected $moduleHandler; | ||
|
|
||
| /** | ||
| * The group manager. | ||
| * | ||
| * @var \Drupal\og\GroupManager | ||
| * | ||
| * @todo This should be GroupManagerInterface. | ||
| */ | ||
| protected $groupManager; | ||
|
|
||
| /** | ||
| * The OG permission manager. | ||
| * | ||
| * @var \Drupal\og\PermissionManagerInterface | ||
| */ | ||
| protected $permissionManager; | ||
|
|
||
| /** | ||
| * Constructs an OgManager service. | ||
| * | ||
|
|
@@ -71,11 +88,17 @@ class OgAccess implements OgAccessInterface { | |
| * The service that contains the current active user. | ||
| * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler | ||
| * The module handler. | ||
| * @param \Drupal\og\GroupManager $group_manager | ||
| * The group manager. | ||
| * @param \Drupal\og\PermissionManagerInterface $permission_manager | ||
| * The permission manager. | ||
| */ | ||
| public function __construct(ConfigFactoryInterface $config_factory, AccountProxyInterface $account_proxy, ModuleHandlerInterface $module_handler) { | ||
| public function __construct(ConfigFactoryInterface $config_factory, AccountProxyInterface $account_proxy, ModuleHandlerInterface $module_handler, GroupManager $group_manager, PermissionManagerInterface $permission_manager) { | ||
| $this->configFactory = $config_factory; | ||
| $this->accountProxy = $account_proxy; | ||
| $this->moduleHandler = $module_handler; | ||
| $this->groupManager = $group_manager; | ||
| $this->permissionManager = $permission_manager; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -89,7 +112,7 @@ public function userAccess(EntityInterface $group, $operation, AccountInterface | |
| $config = $this->configFactory->get('og.settings'); | ||
| $cacheable_metadata = (new CacheableMetadata) | ||
| ->addCacheableDependency($config); | ||
| if (!Og::isGroup($group_type_id, $bundle)) { | ||
| if (!$this->groupManager->isGroup($group_type_id, $bundle)) { | ||
| // Not a group. | ||
| return AccessResult::neutral()->addCacheableDependency($cacheable_metadata); | ||
| } | ||
|
|
@@ -100,6 +123,9 @@ public function userAccess(EntityInterface $group, $operation, AccountInterface | |
|
|
||
| // From this point on, every result also depends on the user so check | ||
| // whether it is the current. See https://www.drupal.org/node/2628870 | ||
| // @todo This doesn't really vary by user but by the user's roles inside of | ||
| // the group. We should create a cache context for OgRole entities. | ||
| // @see https://github.com/amitaibu/og/issues/219 | ||
| if ($user->id() == $this->accountProxy->id()) { | ||
| $cacheable_metadata->addCacheContexts(['user']); | ||
| } | ||
|
|
@@ -140,22 +166,30 @@ public function userAccess(EntityInterface $group, $operation, AccountInterface | |
| $permissions = []; | ||
| $user_is_group_admin = FALSE; | ||
|
|
||
| if ($membership = Og::getMembership($group, $user)) { | ||
| foreach ($membership->getRoles() as $role) { | ||
| // Check for the is_admin flag. | ||
| /** @var \Drupal\og\Entity\OgRole $role */ | ||
| if ($role->isAdmin()) { | ||
| $user_is_group_admin = TRUE; | ||
| break; | ||
| $states = [ | ||
| OgMembershipInterface::STATE_ACTIVE, | ||
| OgMembershipInterface::STATE_PENDING, | ||
| OgMembershipInterface::STATE_BLOCKED, | ||
| ]; | ||
| if ($membership = Og::getMembership($group, $user, $states)) { | ||
| // Blocked users don't have any permissions. | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
| if ($membership->getState() !== OgMembershipInterface::STATE_BLOCKED) { | ||
| foreach ($membership->getRoles() as $role) { | ||
| // Check for the is_admin flag. | ||
| /** @var \Drupal\og\Entity\OgRole $role */ | ||
| if ($role->isAdmin()) { | ||
| $user_is_group_admin = TRUE; | ||
| break; | ||
| } | ||
|
|
||
| $permissions = array_merge($permissions, $role->getPermissions()); | ||
| } | ||
|
|
||
| $permissions = array_merge($permissions, $role->getPermissions()); | ||
| } | ||
| } | ||
| else { | ||
| // User is a non-member. | ||
| /** @var \Drupal\og\Entity\OgRole $role */ | ||
| $role = Og::getRole($group_type_id, $bundle, OgRoleInterface::ANONYMOUS); | ||
| $role = OgRole::loadByGroupAndName($group, OgRoleInterface::ANONYMOUS); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 the name is fine for now - things might shift a bit once we'll have the perBundle/ perGroup roles. |
||
| $permissions = $role->getPermissions(); | ||
| } | ||
|
|
||
|
|
@@ -198,16 +232,11 @@ public function userAccess(EntityInterface $group, $operation, AccountInterface | |
| public function userAccessEntity($operation, EntityInterface $entity, AccountInterface $user = NULL) { | ||
| $result = AccessResult::neutral(); | ||
|
|
||
| // Entity isn't saved yet. | ||
| if ($entity->isNew()) { | ||
| return $result->addCacheableDependency($entity); | ||
| } | ||
|
|
||
| $entity_type = $entity->getEntityType(); | ||
| $entity_type_id = $entity_type->id(); | ||
| $bundle = $entity->bundle(); | ||
|
|
||
| if (Og::isGroup($entity_type_id, $bundle)) { | ||
| if ($this->groupManager->isGroup($entity_type_id, $bundle)) { | ||
| $user_access = $this->userAccess($entity, $operation, $user); | ||
| if ($user_access->isAllowed()) { | ||
| return $user_access; | ||
|
|
@@ -233,6 +262,15 @@ public function userAccessEntity($operation, EntityInterface $entity, AccountInt | |
| $forbidden = AccessResult::forbidden()->addCacheTags($cache_tags); | ||
| foreach ($groups as $entity_groups) { | ||
| foreach ($entity_groups as $group) { | ||
| // Check if the operation matches a group content entity operation | ||
| // such as 'create article content'. | ||
| $operation_access = $this->userAccessGroupContentEntityOperation($operation, $group, $entity, $user); | ||
| if ($operation_access->isAllowed()) { | ||
| return $operation_access->addCacheTags($cache_tags); | ||
| } | ||
|
|
||
| // Check if the operation matches a group level operation such as | ||
| // 'subscribe without approval'. | ||
| $user_access = $this->userAccess($group, $operation, $user); | ||
| if ($user_access->isAllowed()) { | ||
| return $user_access->addCacheTags($cache_tags); | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not 100% sure if we still need this call to Edit: This has in the meantime been answered. The |
||
|
|
@@ -252,6 +290,77 @@ public function userAccessEntity($operation, EntityInterface $entity, AccountInt | |
| return $result; | ||
| } | ||
|
|
||
| /** | ||
| * Checks access for entity operations on group content entities. | ||
| * | ||
| * This checks if the user has permission to perform the requested operation | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
| * on the given group content entity according to the user's membership status | ||
| * in the given group. There is no formal support for access control on entity | ||
| * operations in core, so the mapping of permissions to operations is provided | ||
| * by PermissionManager::getEntityOperationPermissions(). | ||
| * | ||
| * @param string $operation | ||
| * The entity operation. | ||
| * @param \Drupal\Core\Entity\EntityInterface $group_entity | ||
| * The group entity, to retrieve the permissions from. | ||
| * @param \Drupal\Core\Entity\EntityInterface $group_content_entity | ||
| * The group content entity for which access to the entity operation is | ||
| * requested. | ||
| * @param \Drupal\Core\Session\AccountInterface $user | ||
| * Optional user for which to check access. If omitted, the currently logged | ||
| * in user will be used. | ||
| * | ||
| * @return \Drupal\Core\Access\AccessResult | ||
| * The access result object. | ||
| * | ||
| * @see \Drupal\og\PermissionManager::getEntityOperationPermissions() | ||
| */ | ||
| public function userAccessGroupContentEntityOperation($operation, EntityInterface $group_entity, EntityInterface $group_content_entity, AccountInterface $user = NULL) { | ||
| // Default to the current user. | ||
| $user = $user ?: $this->accountProxy->getAccount(); | ||
|
|
||
| // Check if the user owns the entity which is being operated on. | ||
| $is_owner = $group_content_entity instanceof EntityOwnerInterface && $group_content_entity->getOwnerId() == $user->id(); | ||
|
|
||
| // Retrieve the group content entity operation permissions. | ||
| $group_entity_type_id = $group_entity->getEntityTypeId(); | ||
| $group_bundle_id = $group_entity->bundle(); | ||
| $group_content_bundle_ids = [$group_content_entity->getEntityTypeId() => [$group_content_entity->bundle()]]; | ||
|
|
||
| $permissions = $this->permissionManager->getDefaultEntityOperationPermissions($group_entity_type_id, $group_bundle_id, $group_content_bundle_ids); | ||
|
|
||
| // Filter the permissions by operation and ownership. | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
| // If the user does not own the group content, only the non-owner permission | ||
| // is relevant (for example 'edit any article node'). However when the user | ||
| // _is_ the owner, then both permissions are relevant: an owner will have | ||
| // access if they either have the 'edit any article node' or the 'edit own | ||
| // article node' permission. | ||
| $ownerships = $is_owner ? [FALSE, TRUE] : [FALSE]; | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I'm not clear on those arrays?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yes, this little piece of magic :) I tried to clarify it, can you check if this makes more sense now? I couldn't find the right words. |
||
| $permissions = array_filter($permissions, function (GroupContentOperationPermission $permission) use ($operation, $ownerships) { | ||
| return $permission->getOperation() === $operation && in_array($permission->getOwner(), $ownerships); | ||
| }); | ||
|
|
||
| if ($permissions) { | ||
| foreach ($permissions as $permission) { | ||
| $user_access = $this->userAccess($group_entity, $permission->getName(), $user); | ||
| if ($user_access->isAllowed()) { | ||
| return $user_access; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // @todo This doesn't really vary by user but by the user's roles inside of | ||
| // the group. We should create a cache context for OgRole entities. | ||
| // @see https://github.com/amitaibu/og/issues/219 | ||
| $cacheable_metadata = new CacheableMetadata(); | ||
| $cacheable_metadata->addCacheableDependency($group_content_entity); | ||
| if ($user->id() == $this->accountProxy->id()) { | ||
| $cacheable_metadata->addCacheContexts(['user']); | ||
| } | ||
|
|
||
| return AccessResult::neutral()->addCacheableDependency($cacheable_metadata); | ||
| } | ||
|
|
||
| /** | ||
| * Set the permissions in the static cache. | ||
| * | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fix covers having entity and bundle IDs containing dashes.