2

I've setup a custom page in my system which is expecting 2 parameters that will be used to generate part of the content on that page, I'm able to retrieve the information from my page, but I'm wondering what is the best method of returning an access denied when my criteria fail

I found this page which shows how you can setup a custom access controller, but this example is passing the user entity, when what I'm more interested in is the 'slugs' of the URL they've hit

routing.yml

my_module.user_subscribe:
  path: /my-module/subscribe/{entity_type}/{entity_id}
  defaults:
    _controller: \Drupal\my-module\Controller\SubscriptionPages::subscribe
    _title: Email Subscription Sucessful
    type: node
    entity: 1
  requirements:
    _permission: 'access content'
    _entity_check: 'TRUE'

Services.yml

services:
  my_module.entity_check:
    class: Drupal\my-module\Access\EntityCheck
    tags:
      - { name: access_check, applies_to: _entity_check }

Then I have my src/Controller/SubscriptionPages file, and one for src/Access/EntityCheck etc, all that part seems to be working, I can manually enforce the code in my EntityCheck to return an access denied and it does, but what I want to know is how can I pull the {entity_type} and {entity_id} from the URL they've hit so I can check they aren't trying to access something they shouldn't

eg

mywebsite.com/my-module/subscribe/node/15 - would be fine

mywebsite.com/my-module/subscribe/foo/bar - would return access denied, because I want to run the check inside my EntityCheck code

Inside the link above it has arguments: ['@current_user'] inside the services.yml file, is there an equivalent one for current URL, or ones where I can more specifically retrieve each of the items individually?

1 Answer 1

3

In your route, instead of _permission, you can use a custom access callback:

_custom_access: Drupal\[MODULE]\Controller::accessCallback()

Then you can return an AccessResult from the access callback:

public function accessCallback($entity_type, $entity_id) {
  if (!in_array($entity_type, $allowed_types)) {
    return \Drupal\Core\Access\AccessResult::forbidden();
  }

  ...

  return \Drupal\Core\Access\AAccessResult::neutral();
}

Or, you can use a _permission on the route, and instead throw a Symfony\Component\HttpKernel\Exception\NotFoundHttpException exception from the controller if the values are incorrect:

public function someRouteCallback($entity_type, $entity_id) {
  if (!$entity = \Drupal::entityTypeManager()->getStorage($entity_type)->load($entity_id)) {
    throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException();
  }
}
3
  • Ah perfect, even better, thank you Commented Apr 22, 2022 at 14:44
  • 2
    Be aware that access callbacks could be called for different reasons and they should return the access result, not act on it. You can throw the exception in the controller itself, this would be no problem, but it's not ideal. This is to have an access callback returning the access result, then any code checking the route access works as it should.
    – 4uk4
    Commented Apr 22, 2022 at 15:07
  • Whoops. Thanks, I was mixing things. I've updated the post accordingly. Andrew Morris you should review my updates and change your code accordingly.
    – Jaypan
    Commented Apr 22, 2022 at 23:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.