diff --git a/dgi_fixity.routing.yml b/dgi_fixity.routing.yml index 9d5cffb..cf006aa 100644 --- a/dgi_fixity.routing.yml +++ b/dgi_fixity.routing.yml @@ -28,7 +28,7 @@ entity.fixity_check.revision: _controller: '\Drupal\Core\Entity\Controller\EntityViewController::viewRevision' _title_callback: '\Drupal\Core\Entity\Controller\EntityController::title' requirements: - _permission: 'view fixity checks' + _access_fixity_check_revision: 'view' fixity_check: \d+ fixity_check_revision: \d+ options: @@ -45,8 +45,13 @@ entity.fixity_check.revision_delete_confirm: _form: '\Drupal\dgi_fixity\Form\RevisionDeleteForm' _title: 'Delete earlier check' requirements: - _permission: 'administer fixity checks' + _access_fixity_check_revision: 'delete' fixity_check: \d+ fixity_check_revision: \d+ options: _admin_route: TRUE + parameters: + fixity_check: + type: entity:fixity_check + fixity_check_revision: + type: entity_revision:fixity_check diff --git a/dgi_fixity.services.yml b/dgi_fixity.services.yml index ae9da2e..145c7f6 100644 --- a/dgi_fixity.services.yml +++ b/dgi_fixity.services.yml @@ -16,3 +16,8 @@ services: arguments: ['@entity_type.manager', '@entity.repository', '@dgi_fixity.fixity_check'] tags: - { name: paramconverter } + access_check.fixity_check.revision: + class: Drupal\dgi_fixity\Access\FixityCheckRevisionAccessCheck + arguments: ['@entity_type.manager'] + tags: + - { name: access_check, applies_to: _access_fixity_check_revision } \ No newline at end of file diff --git a/src/Access/FixityCheckRevisionAccessCheck.php b/src/Access/FixityCheckRevisionAccessCheck.php new file mode 100644 index 0000000..874ef76 --- /dev/null +++ b/src/Access/FixityCheckRevisionAccessCheck.php @@ -0,0 +1,133 @@ +storage = $entity_type_manager->getStorage('fixity_check'); + $this->accessControlHandler = $entity_type_manager->getAccessControlHandler('fixity_check'); + } + + /** + * Checks routing access for the fixity_check revision. + * + * @param \Symfony\Component\Routing\Route $route + * The route to check against. + * @param \Drupal\Core\Session\AccountInterface $account + * The currently logged in account. + * @param int $fixity_check_revision + * (optional) The fixity_check revision ID. If not specified, but + * $fixity_check is, access is checked for that object's revision. + * @param \Drupal\dgi_fixity\FixityCheckInterface $fixity_check + * (optional) A fixity_check object. Used for checking access to a + * fixity_check's default revision when $fixity_check_revision is + * unspecified. Ignored when $fixity_check_revision is specified. + * If neither $fixity_check_revision nor $fixity_check are specified, + * then access is denied. + * + * @return \Drupal\Core\Access\AccessResultInterface + * The access result. + */ + public function access(Route $route, AccountInterface $account, $fixity_check_revision = NULL, FixityCheckInterface $fixity_check = NULL) { + if ($fixity_check_revision) { + $fixity_check = $this->storage->loadRevision($fixity_check_revision); + } + $operation = $route->getRequirement('_access_fixity_check_revision'); + return AccessResult::allowedIf($fixity_check && $this->checkAccess($fixity_check, $account, $operation))->cachePerPermissions()->addCacheableDependency($fixity_check); + } + + /** + * Checks fixity_check revision access. + * + * @param \Drupal\dgi_fixity\FixityCheckInterface $fixity_check + * The fixity_check revision to check. + * @param \Drupal\Core\Session\AccountInterface $account + * A user object representing the user for whom the operation is to be + * performed. + * @param string $op + * (optional) The specific operation being checked. Defaults to 'view'. + * + * @return bool + * TRUE if the operation may be performed, FALSE otherwise. + */ + public function checkAccess(FixityCheckInterface $fixity_check, AccountInterface $account, $op = 'view') { + $map = [ + 'view' => 'view fixity checks', + 'delete' => 'administer fixity checks', + ]; + + if (!$fixity_check || !isset($map[$op])) { + // If there was no fixity_check to check against, or the $op was not one of the + // supported ones, we return access denied. + return FALSE; + } + + // Statically cache access by revision ID, user account ID, and operation. + $cid = $fixity_check->getRevisionId() . ':' . $account->id() . ':' . $op; + + if (!isset($this->access[$cid])) { + $has_perm = $account->hasPermission($map[$op]); + $has_admin_perm = $account->hasPermission($fixity_check->getEntityType()->getAdminPermission()); + // Perform basic permission checks first. + if (!$has_perm && !$has_admin_perm) { + $this->access[$cid] = FALSE; + return $this->access[$cid]; + } + // Do not allow for the deletion of the the default revision. + elseif ($fixity_check->isDefaultRevision() && $op === 'delete') { + $this->access[$cid] = FALSE; + } + elseif ($has_admin_perm) { + $this->access[$cid] = TRUE; + } + else { + // First check the access to the default revision and finally, if the + // fixity_check passed in is not the default revision then check access to + // that, too. + $this->access[$cid] = $this->accessControlHandler->access($this->storage->load($fixity_check->id()), $op, $account) && ($fixity_check->isDefaultRevision() || $this->accessControlHandler->access($fixity_check, $op, $account)); + } + } + return $this->access[$cid]; + } + +} diff --git a/src/Form/RevisionDeleteForm.php b/src/Form/RevisionDeleteForm.php index 3d9d9db..c96a261 100644 --- a/src/Form/RevisionDeleteForm.php +++ b/src/Form/RevisionDeleteForm.php @@ -7,6 +7,7 @@ use Drupal\Core\Entity\RevisionableStorageInterface; use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; +use Drupal\dgi_fixity\FixityCheckInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -97,8 +98,8 @@ class RevisionDeleteForm extends ConfirmFormBase { /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state, $fixity_check_revision = NULL) { - $this->revision = $this->storage->loadRevision($fixity_check_revision); + public function buildForm(array $form, FormStateInterface $form_state, FixityCheckInterface $fixity_check_revision = NULL) { + $this->revision = $fixity_check_revision; $form = parent::buildForm($form, $form_state); return $form;