-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a constraint for field_member_of for entity references. (#1)
* Add a constraint for field_member_of for entity references. * Adds php test for this
- Loading branch information
1 parent
c696d81
commit e1c2e4e
Showing
6 changed files
with
236 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
name: iDC Defaults | ||
description: Customizations for the Islandora integration at JHU's Sheridan Libraries. | ||
package: iDC | ||
type: module | ||
core_version_requirement: ^8 || ^9 | ||
dependencies: | ||
- islandora:islandora | ||
- drupal:workbench_access |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
|
||
/** | ||
* @file | ||
* Defines hooks used for JHU's iDC integration. | ||
*/ | ||
|
||
use Drupal\Core\Entity\EntityTypeInterface; | ||
|
||
/** | ||
* Implements hook_entity_bundle_field_info_alter(). | ||
*/ | ||
function idc_defaults_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type, $bundle) { | ||
if (isset($fields['field_member_of'])) { | ||
$fields['field_member_of']->addConstraint('WorkbenchAccess'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
|
||
namespace Drupal\idc_defaults\Plugin\Validation\Constraint; | ||
|
||
use Symfony\Component\Validator\Constraint; | ||
|
||
/** | ||
* Checks that the user has access to an entity. | ||
* | ||
* @Constraint( | ||
* id = "WorkbenchAccess", | ||
* label = @Translation("Workbench Access", context = "Validation"), | ||
* type = "string" | ||
* ) | ||
*/ | ||
class WorkbenchAccess extends Constraint { | ||
|
||
/** | ||
* Entity passed in does not exist or isn't a node. | ||
* | ||
* @var string | ||
*/ | ||
public $badType = 'The entity does not exist or is not a node.'; | ||
|
||
/** | ||
* User does not have access to the entity referenced via workbench. | ||
* | ||
* @var string | ||
*/ | ||
public $noAccess = 'The user does not have access to ingest into this object.'; | ||
|
||
} |
32 changes: 32 additions & 0 deletions
32
src/Plugin/Validation/Constraint/WorkbenchAccessValidator.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
|
||
namespace Drupal\idc_defaults\Plugin\Validation\Constraint; | ||
|
||
use Drupal\node\NodeInterface; | ||
use Symfony\Component\Validator\Constraint; | ||
use Symfony\Component\Validator\ConstraintValidator; | ||
|
||
/** | ||
* Validates that the user has access to the entity. | ||
*/ | ||
class WorkbenchAccessValidator extends ConstraintValidator { | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function validate($items, Constraint $constraint) { | ||
foreach ($items as $item) { | ||
if (!isset($item->entity) || !$item->entity instanceof NodeInterface) { | ||
$this->context->addViolation($constraint->badType); | ||
} | ||
else { | ||
// Use the "update" op as it applies to an already existing entity and | ||
// not a new entity being created. | ||
if (!$item->entity->access('update')) { | ||
$this->context->addViolation($constraint->noAccess); | ||
} | ||
} | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
<?php | ||
|
||
namespace Drupal\Tests\idc_defaults\Kernel; | ||
|
||
use Drupal\taxonomy\Entity\Term; | ||
use Drupal\Tests\node\Kernel\NodeAccessTestBase; | ||
use Drupal\Tests\workbench_access\Traits\WorkbenchAccessTestTrait; | ||
use Drupal\workbench_access\WorkbenchAccessManagerInterface; | ||
|
||
/** | ||
* Tests the field_member_of constraint. | ||
* | ||
* @group idc_defaults | ||
*/ | ||
class ConstraintTest extends NodeAccessTestBase { | ||
|
||
use WorkbenchAccessTestTrait; | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected static $modules = [ | ||
'node', | ||
'datetime', | ||
'user', | ||
'system', | ||
'filter', | ||
'field', | ||
'text', | ||
'taxonomy', | ||
'workbench_access', | ||
'idc_defaults', | ||
'devel', | ||
]; | ||
|
||
/** | ||
* Access control scheme. | ||
* | ||
* @var \Drupal\workbench_access\Entity\AccessSchemeInterface | ||
*/ | ||
protected $scheme; | ||
|
||
/** | ||
* User section storage. | ||
* | ||
* @var \Drupal\workbench_access\UserSectionStorage | ||
*/ | ||
protected $userStorage; | ||
|
||
/** | ||
* The state service. | ||
* | ||
* @var \Drupal\Core\State\StateInterface | ||
*/ | ||
protected $state; | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function setUp() { | ||
parent::setUp(); | ||
$this->installConfig('workbench_access'); | ||
$this->installEntitySchema('taxonomy_term'); | ||
$this->installEntitySchema('section_association'); | ||
$this->installSchema('system', 'key_value'); | ||
|
||
$node_type = $this->createContentType(['type' => 'repository_item']); | ||
$this->vocabulary = $this->setUpVocabulary(); | ||
$this->accessHandler = $this->container->get('entity_type.manager')->getAccessControlHandler('node'); | ||
$this->state = $this->container->get('state'); | ||
$this->setUpTaxonomyFieldForEntityType('node', $node_type->id(), $this->vocabulary->id()); | ||
$this->scheme = $this->setUpTaxonomyScheme($node_type, $this->vocabulary); | ||
$this->userStorage = $this->container->get('workbench_access.user_section_storage'); | ||
$this->createEntityReferenceField('node', 'repository_item', 'field_member_of', 'Member Of', 'node', 'default', [], 2); | ||
} | ||
|
||
/** | ||
* Tests the WorkbenchAccess constraint. | ||
*/ | ||
public function testConstraint() { | ||
// Permissions matching the collection level and global admins for repo | ||
// items. | ||
$permissions = [ | ||
'create repository_item content', | ||
'edit any repository_item content', | ||
'edit own repository_item content', | ||
'access content', | ||
]; | ||
|
||
// Create a section. | ||
$term = Term::create([ | ||
'vid' => $this->vocabulary->id(), | ||
'name' => 'Some collection', | ||
]); | ||
$term->save(); | ||
|
||
// Create two collection_admin users with the same permissions but one will | ||
// be part of a community. | ||
$collection_admin_community_user = $this->createUser($permissions); | ||
$this->userStorage->addUser($this->scheme, $collection_admin_community_user, [$term->id()]); | ||
|
||
$collection_admin_user = $this->createUser($permissions); | ||
|
||
// Global admin can bypass workbench. | ||
$permissions[] = 'bypass workbench access'; | ||
$global_admin_user = $this->createUser($permissions); | ||
|
||
// Node that belongs to a community. | ||
$community_node = $this->drupalCreateNode([ | ||
'type' => 'repository_item', | ||
WorkbenchAccessManagerInterface::FIELD_NAME => $term->id(), | ||
]); | ||
|
||
// Switch to the collection admin community user. | ||
$this->setCurrentUser($collection_admin_community_user); | ||
$node_referencing_a_community_node = $this->drupalCreateNode([ | ||
'type' => 'repository_item', | ||
'field_member_of' => $community_node->id(), | ||
]); | ||
|
||
$violations = $node_referencing_a_community_node->validate(); | ||
$this->assertCount(0, $violations, 'Collection admin within a community able to add to a node within a community.'); | ||
|
||
// Switch to the collection admin user. | ||
$this->setCurrentUser($collection_admin_user); | ||
$node_referencing_a_community_node_no_access = $this->drupalCreateNode([ | ||
'type' => 'repository_item', | ||
'field_member_of' => $community_node->id(), | ||
]); | ||
$violations = $node_referencing_a_community_node_no_access->validate(); | ||
$this->assertCount(1, $violations, 'Collection admin not part of a community unable to add to a node.'); | ||
$this->assertEquals('The user does not have access to ingest into this object.', $violations->get(0)->getMessage(), 'Incorrect constraint validation message found'); | ||
|
||
// Switch to the global admin user. | ||
$this->setCurrentUser($global_admin_user); | ||
$node_referencing_a_community_global_admin = $this->drupalCreateNode([ | ||
'type' => 'repository_item', | ||
'field_member_of' => $community_node->id(), | ||
]); | ||
$violations = $node_referencing_a_community_global_admin->validate(); | ||
$this->assertCount(0, $violations, 'Global admin can add to whatever collection they want.'); | ||
|
||
} | ||
|
||
} |