Problem/Motivation
When an access check is created for the entity.node.delete_form
route which prevents the current user from accessing the delete route, the Delete button is still rendered on the node edit form. The button does not work because the access check is ran when the button is clicked. I believe the button should not render at all if the user does not have access, exactly the same way as most links in Drupal work.
Steps to reproduce
- Create an access check. Here is a very simplified version that you would never want to actually use, but demonstrates the problem:
my_module.services.yml
my_module.route_subscriber:
class: Drupal\my_module\EventSubscriber\RouteSubscriber
tags:
- { name: event_subscriber }
my_module.access_check.delete_node_access:
class: Drupal\my_module\Access\DeleteNodeAccessChecker
tags:
- { name: access_check, applies_to: _delete_node_access }
RouteSubscriber.php
<?php
namespace Drupal\my_module\EventSubscriber;
use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;
/**
* Route subscriber.
*/
class RouteSubscriber extends RouteSubscriberBase {
/**
* {@inheritdoc}
*/
protected function alterRoutes(RouteCollection $collection) {
foreach ($collection->all() as $route_name => $route) {
// Add access check to node delete routes.
$node_delete_routes = [
// /node/{node}/delete.
'entity.node.delete_form',
];
if (in_array($route_name, $node_delete_routes)) {
$route->setRequirement('_delete_node_access', 'my_module.access_check.delete_node_access::access');
}
}
}
}
DeleteNodeAccessChecker.php
<?php
namespace Drupal\my_module\Access;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Routing\Access\AccessInterface;
use Drupal\Core\Routing\RouteMatch;
use Drupal\Core\Session\AccountProxy;
use Symfony\Component\Routing\Route;
/**
* Access check for Drupal core node delete route.
*
* Intended to be run on these routes:
* - entity.node.delete_form.
*
* @see \Drupal\my_module\EventSubscriber\RouteSubscriber
*/
class DeleteNodeAccessChecker implements AccessInterface {
/**
* Access callback.
*
* @param \Symfony\Component\Routing\Route $route
* The Symfony route.
* @param \Drupal\Core\Routing\RouteMatch $routeMatch
* The current route.
* @param \Drupal\Core\Session\AccountProxy $account
* The current user account.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function access(Route $route, RouteMatch $routeMatch, AccountProxy $account): AccessResultInterface {
return AccessResult::forbidden("User does not have permission to delete node.");
}
}
- Clear caches.
- Edit a node. You will see the Delete button.
- Click the delete button. You will see an "Oops, something went wrong" error message.
- Check wd logs. You will see a "User does not have permission to delete node." access denied log.
Proposed resolution
The rendering of the delete button should depend on whether or not the access check returns allowed or forbidden.
Remaining tasks
User interface changes
API changes
Data model changes