1

I created a page in a custom module. I want only the current user to access this page on their behalf.

For example, I am using it with ID 51. If I go to :

/user/51/tasks

I can see this page.

If I go on :

/user/78/tasks

I must have an error 403 access denied.

How to do this ?

task_notify.user_page.tasks:
  path: '/user/{user}/tasks'
  defaults:
    _controller: '\Drupal\task_notify\Controller\TaskNotifyUserController::Tasks'
    _title: 'Liste des tâches'
  requirements:
    _permission: 'access content'

UPDATE :

My custom module is here :

https://git.drupalcode.org/sandbox/zenimagine-3076032

I applied kiamlaluno's answer but I have the following error :

enter image description here

/task_notify/src/Controller/TaskNotifyUserController.php

<?php

namespace Drupal\task_notify\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\UserInterface;

class TaskNotifyUserController extends ControllerBase {

  public function Tasks() {
    return [
      '#theme' => 'task_notify_user_template',
    ];
  }

  public function taskAccess(UserInterface $user, AccountInterface $account) {
    return AccessResult::allowedIf($account->isAuthenticated() && $user->id() == $account->id())
      ->orIf(AccessResult::allowedIfHasPermission($account, 'administer users'))
      ->cachePerUser();
  }

}
4

_permission only allows to give access to the route to the users who have that permission; it doesn't allow to give access basing on more complex conditions, as in the case of this question. As described in Custom route access checking, the route definition should be similar to the following one.

task_notify.user_page.tasks:
  path: '/user/{user}/tasks'
  defaults:
    _controller: '\Drupal\task_notify\Controller\TaskNotifyUserController::tasks'
    _title: 'Tasks list'
  requirements:
    _custom_access:  '\Drupal\task_notify\Controller\TaskNotifyUserController::taskAccess'
    user: \d+

The TaskNotifyUserController class would need to contain at least the following code (and other methods you need from that controller, including tasks()).

class TaskNotifyUserController extends ControllerBase {

  /**
   * Checks access for the users task page.
   *
   * @param \Drupal\user\UserInterface $user
   *   The user being viewed.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The account viewing the page.
   *
   * @return \Drupal\Core\Access\AccessResultInterface
   *   The access result.
   */
  public function taskAccess(UserInterface $user, AccountInterface $account) {
  public function taskAccess(UserInterface $user) {
    return AccessResult::allowedIf($account->isAuthenticated() && $user()->id() == $account->id())
      ->cachePerUser();
  }

}

If then the access to the route should be allowed also to users with a specific permission (for example, the permission to administer users), the taskAccess() code would be similar to the following one.

  /**
   * Checks access for the users task page.
   *
  /**
   * Checks access for the tasks tab.
   *
   * @param \Drupal\user\UserInterface $user
   *   The user being viewed.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The account viewing the page.
   *
   * @return \Drupal\Core\Access\AccessResultInterface
   *   The access result.
   */
  public function taskAccess(UserInterface $user, AccountInterface $account) {
    return AccessResult::allowedIf($account->isAuthenticated() && $user->id() == $account->id())
      ->orIf(AccessResult::allowedIfHasPermission($account, 'administer users'))
      ->cachePerUser();
  }

Don't forget to import the class used from the controller, adding the following lines at the top of the controller file.

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\UserInterface;

If the controller class uses other classes from different namespaces, similar lines must be added for each class.

I took the code used from Drupal core for the Tracker module as example. The Tracker module shows its tracker page on /activity/{user} using a controller to render it. See TrackerController.php.

  /**
   * Checks access for the users recent content tracker page.
   *
   * @param \Drupal\user\UserInterface $user
   *   The user being viewed.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The account viewing the page.
   *
   * @return \Drupal\Core\Access\AccessResult
   *   The access result.
   */
  public function checkAccess(UserInterface $user, AccountInterface $account) {
    return AccessResult::allowedIf($account->isAuthenticated() && $user
      ->id() == $account->id())->cachePerUser();
  }

The custom access callback is defined in tracker.routing.yml.

tracker.users_recent_content:
  path: '/activity/{user}'
  defaults:
    _controller: '\Drupal\tracker\Controller\TrackerController::buildContent'
    _title: 'My recent content'
  requirements:
    _permission: 'access content'
    _custom_access: '\Drupal\tracker\Controller\TrackerController::checkAccess'
    user: \d+
| improve this answer | |
  • Each time you use a class from a different namespace, you need to add a line similar to use Drupal\Core\Session\AccountInterface; on the top of that file. – kiamlaluno Jul 2 at 12:48
  • You need to add them in the files where those classes/interfaces are used. Adding them to the task_notify.module file, while those classes/interfaces are used in the TaskNotifyUserController.php would cause the error message you saw. – kiamlaluno Jul 2 at 13:01
  • git.drupalcode.org/sandbox/zenimagine-3076032/-/blob/master/src/… What's wrong with my code ? – mathieu Jul 3 at 11:00
  • I just saw that you have updated your answer, thank you but there is an error RuntimeException: Callable "Drupal\task_notify\Controller\TaskNotifyUserController::taskAccess" requires a value for the "$user" argument. in Drupal\Component\Utility\ArgumentsResolver->handleUnresolvedArgument() (line 142 of core/lib/Drupal/Component/Utility/ArgumentsResolver.php). – mathieu Jul 3 at 11:14
  • I rewrote the code basing on the code Drupal core is using on one of its modules. – kiamlaluno Jul 4 at 13:50
0

In TaskNotifyUserController::Task you can access the current user with Drupal::currentUser(). Then compare that id with the id from the current Request, or if you're dealing with a node, the node's author's id (uid). If they don't match, issue a RedirectResponse or regular Response('', Response::HTTP_FORBIDDEN).

| improve this answer | |
  • 3
    The answer from @kiamlaluno is the better solution, because access checks in routes can do other things like control the visibility of menu items which your code in a controller can't. However, if you need to deny access from the inside of a controller then throw AccessDeniedHttpException, see drupal.stackexchange.com/questions/215627/… – 4k4 Jul 2 at 8:49
  • Thanks! Good to know – beltouche Jul 2 at 16:38
  • I have updated my question – mathieu Jul 4 at 17:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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