1

I am trying to create a custom access control in my custom module in Drupal 8.

But I am having issues in achieving it.

I cant find a proper way to inject UserInterface class in access method.

Basically what I am trying to do is similar to this

https://api.drupal.org/api/drupal/core%21modules%21contact%21src%21Access%21ContactPageAccess.php/8.7.x

Similar to above code I am tring to get details of user object in access method. But cant find a proper way to achieve this.

My Code is as follows:

custom_module\src\Access\CustomRoleAccessCheck.php

<?php
namespace Drupal\custom_module\Access;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Routing\Access\AccessInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\UserDataInterface;
use Drupal\user\UserInterface;

/**
 * Access check for Custom Controller route.
 */
class CustomRoleAccessCheck implements AccessInterface {

  /**
   * The contact settings config object.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * The user data service.
   *
   * @var \Drupal\user\UserDataInterface
   */
  protected $userData;

  /**
   * Constructs a ContactPageAccess instance.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The config factory.
   * @param \Drupal\user\UserDataInterface $user_data
   *   The user data service.
   */
  public function __construct(ConfigFactoryInterface $config_factory, UserDataInterface $user_data) {
    $this->configFactory = $config_factory;
    $this->userData = $user_data;
  }

  /**
   * Checks access to the given user's contact page.
   *
   * @param \Drupal\user\UserInterface $user
   *   The user being contacted.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The currently logged in account.
   *
   * @return \Drupal\Core\Access\AccessResultInterface
   *   The access result.
   */
  public function access(UserInterface $user, AccountInterface $account) {
    $contact_account = $user;
    dpm($contact_account); // Need this values for some other functionality.

    if ($contact_account->isAnonymous()) {
      return AccessResult::forbidden();
    }

    return AccessResult::allowed();
  }

}

services.yml

custom_module.custom_role_access_check:
    class: Drupal\custom_module\Access\CustomRoleAccessCheck
    arguments: ['@config.factory', '@user.data']
    tags:
      - { name: access_check, applies_to: _custom_role_access_check }    

routing.yml

custom_module.accesscheck:
  path: '/custom-module/accesscheck'
  defaults:
    _controller: '\Drupal\custom_module\Controller\CustomModuleAccessCheckController::accessCheck'
    _title: 'A Test Page for Custom Module Access Check'
  requirements:
    _custom_role_access_check: 'TRUE'   
    # _permission: 'access content' 

When I am trying to acess /custom-module/accesscheck I am getting the following error

The website encountered an unexpected error. Please try again later.RuntimeException: Callable "Drupal\custom_module\Access\CustomRoleAccessCheck::access" requires a value for the "$user" argument. in Drupal\Component\Utility\ArgumentsResolver->handleUnresolvedArgument() (line 142 of core\lib\Drupal\Component\Utility\ArgumentsResolver.php). Drupal\Component\Utility\ArgumentsResolver->getArgument(Object) (Line: 54) Drupal\Component\Utility\ArgumentsResolver->getArguments(Array) (Line: 157) Drupal\Core\Access\AccessManager->performCheck('custom_module.custom_role_access_check', Object) (Line: 135) Drupal\Core\Access\AccessManager->check(Object, Object, Object, 1) (Line: 112) Drupal\Core\Access\AccessManager->checkRequest(Object, Object, 1) (Line: 109) Drupal\Core\Routing\AccessAwareRouter->checkAccess(Object) (Line: 94) Drupal\Core\Routing\AccessAwareRouter->matchRequest(Object) (Line: 115) Symfony\Component\HttpKernel\EventListener\RouterListener->onKernelRequest(Object, 'kernel.request', Object) call_user_func(Array, Object, 'kernel.request', Object) (Line: 111) Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch('kernel.request', Object) (Line: 127) Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 68) Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 57) Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47) Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 99) Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 78) Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 47) Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 52) Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23) Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 693) Drupal\Core\DrupalKernel->handle(Object) (Line: 19)

I also tried to construct a $accoutData object in constructor in the following way but same error occured.

public function __construct(ConfigFactoryInterface $config_factory, UserDataInterface $user_data, AccountInterface $account_data) {
    $this->configFactory = $config_factory;
    $this->userData = $user_data;
    $this->accountData = $account_data;
  }

Can anyone please suggest a proper way to do it.

Edit :

I am trying to get details of current user.

I know there is a way to obtain current user details by using

\Drupal::currentUser();

but it doesnt have all the user information so I am trying to do that with DI.

2
  • Are you looking for the current user?
    – Jaypan
    May 2, 2019 at 22:23
  • Yes I am looking for current user. @Jaypan May 3, 2019 at 7:14

2 Answers 2

3

You need to set up your route with a {user} parameter in the path, cast to a user object.

example_route:
  path: '/some/path/with/a/{user}/object'
  ...
  requirements:
    _custom_access: '\Drupal\example\Controller\SomeController:SomeAccessCallback'
    # Require a numeric value for {user}
    user: \d+
  options:
    parameters:
      user:
        # Load a user entity using the {user} parameter
        type: entity:user

In this way, the value past in the {user} parameter is cast to a user entity, and passed to your access callback. The user object will also be passed to your controller callback for the route.

Edit:

Adding to your comments about the user you want being the current user, the current user (\Drupal\Core\Session\AccountProxyInterface) is passed to access callabacks as the first argument. You have access to all methods on that object.

If you want the current user in the controller response, you can inject the current_user into the class, which is an object of \Drupal\Core\Session\AccountProxyInterface.

3
  • thank you for the answer, but is it possible to achieve this without a {user} object in the URL parameters ? As I want to restrict access to a specific route based on user details, like the roles, signup time, password changed time (which is not available in account variable.) etc.., ? Similar example from here (Limit by custom access service) part atendesigngroup.com/blog/… May 2, 2019 at 12:01
  • Any way to get the current user details ? May 6, 2019 at 11:09
  • You have access to all current user details off the current user object. I've updated my answer to explain how to get the current user in both access callbacks as well as controller responses.
    – Jaypan
    May 6, 2019 at 11:15
0

The recommended way of getting the current user is

$account_proxy = \Drupal::currentUser();

This returns an account proxy, from where you can load the real user if the proxy is not sufficient

you can load the full user object with

$user = \Drupal\user\Entity\User::load($account_proxy->id());
7
  • Thanks for the suggestion. Yes, thats one of the way, but I am trying to achieve it with Dependency Injection. May 2, 2019 at 14:16
  • Inject the class to your class. That will give you DI. But you still need the argument as Jaypan noted. AccessCheck needs a user object to check/reject based on conditions.
    – Kevin
    May 2, 2019 at 15:05
  • @miststudent2011 You haven't mentioned anywhere that you're looking for the current user, but your response to this answer suggests that's what you're looking for? If so, inject the appropriately named current_user service. If you need the full account details, also inject entity_type.manager, and use it to load the account
    – Clive
    May 2, 2019 at 15:42
  • Access callbacks are already passed the current user as the first argument, so you don't need to do anything at all for your access callback.
    – Jaypan
    May 2, 2019 at 22:23
  • @Clive are you suggesting to pass current user as a argument in services.yml in the following way ? arguments: ['@config.factory', '@user.data','@current_user'] if not so can you give me a refernce example . May 3, 2019 at 7:21

Your Answer

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

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