Skip to content

Commit

Permalink
Add a constraint for field_member_of for entity references. (#1)
Browse files Browse the repository at this point in the history
* Add a constraint for field_member_of for entity references.
* Adds php test for this
  • Loading branch information
jordandukart authored Jul 14, 2021
1 parent c696d81 commit e1c2e4e
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 1 deletion.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"type": "drupal-module",
"description": "Customizations for the Islandora integration at JHU's Sheridan Libraries.",
"require": {
"islandora/islandora": "dev-8.x-1.x"
"islandora/islandora": "dev-8.x-1.x",
"drupal/workbench_access": "^1.0@beta"
}
}
8 changes: 8 additions & 0 deletions idc_defaults.info.yml
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
17 changes: 17 additions & 0 deletions idc_defaults.module
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');
}
}
32 changes: 32 additions & 0 deletions src/Plugin/Validation/Constraint/WorkbenchAccess.php
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 src/Plugin/Validation/Constraint/WorkbenchAccessValidator.php
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);
}
}
}
}

}
145 changes: 145 additions & 0 deletions tests/src/Kernel/ConstraintTest.php
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.');

}

}

0 comments on commit e1c2e4e

Please sign in to comment.