2

So I basically have a custom module created and I just want to know how I can limit the users who can access the 2 forms I have by predefined roles -- the IT and Facilities roles that I have defined in Drupal (7) already so that only IT and Facilities people can access them..

I've been looking at access callback/access arguments but I'm not exactly sure what to use in my form definition/hook_menu in order to limit access to just those two roles.

Here's my hook_menu function:

//hook_menu
function ibbr_inv_menu(){
    //'Inventory Items' page
    $items['inventory_scan'] = array(
    'title' => 'Inventory Items',
    'page callback' => 'ibbr_inv_menu_page_callback',
    'access callback' => TRUE,
    'page arguments' => array(1, 2),
    'type'=> MENU_CALLBACK,
);
    //'Add new item' page
    $items['inventory_scan/add_item'] = array(
    'title' => 'Add new item',
    'page callback' => 'ibbr_inv_add_new_item_callback',
    'access callback' => TRUE,
    'page arguments' => array(1, 2),
    'type'=> MENU_CALLBACK,
);
return $items;
}//end hook

Thank you!

2 Answers 2

2

Since you are setting 'access callback' to TRUE, you are actually giving access to those routes to every user, including the anonymous users.
You could easily set the access callback to a function in your module 'access callback' => 'ibbr_inv_access_callback' and check the roles the global $user variable has, but that would be a bad idea, since every time you need to give access to those routes to a new role, you would need to change the code of your access callback. Plus, since you are hard-coding the roles in your code, the module could not easily be adapted to different needs, such as sites using totally different roles.

The Drupal way is to assign permissions to the roles that should be able to access the routes you define, and use user_permission() as access callback for your routes. In this way, the code would check for the same permission, which could be given to new roles, or to totally different roles; any change in the roles having that permission would not reflect on your code, which would still work in any case.

As permission, you could use one that Drupal already use, or (if that is not restrictive enough) define a new permission.

function ibbr_inv_menu() {
  $items['inventory_scan'] = array(
    'title' => 'Inventory items',
    'page callback' => 'ibbr_inv_menu_page_callback',
    'access arguments' => array('administer inventory items'),
    'type'=> MENU_CALLBACK,
  );
  $items['inventory_scan/add_item'] = array(
    'title' => 'Add new item',
    'page callback' => 'ibbr_inv_add_new_item_callback',
    'access arguments' => array('administer inventory items'),
    'type'=> MENU_CALLBACK,
  );
  return $items;
}

function ibbr_inv_permission() {
  $permissions['administer inventory items'] = array(
    'title' => t('Administer inventory items'),
    'description' => t('Creates/deletes inventory items.'),
  );
  return $permissions;
}

To use a access callback that checks the roles the currently logged-in user has, I would use the following code. It's not the code I would use, but if you need to use such code for any reason, this is what I would suggest.

function ibbr_inv_menu() {
  $items['inventory_scan'] = array(
    'title' => 'Inventory Items',
    'page callback' => 'ibbr_inv_menu_page_callback',
    'access callback' => 'ibbr_inv_access_callback',
    'access arguments' => array('the role the user needs to have'),
    'type'=> MENU_CALLBACK,
  );
  $items['inventory_scan/add_item'] = array(
    'title' => 'Add new item',
    'page callback' => 'ibbr_inv_add_new_item_callback',
    'access callback' => 'ibbr_inv_access_callback',
    'access arguments' => array('the role the user needs to have'),
    'type'=> MENU_CALLBACK,
  );
  return $items;
}

function ibbr_inv_access_callback($role_name) {
  if ($role = user_role_load_by_name($role_name)) {
    return user_has_role($role->rid);
  }

  // The role doesn't exists; return FALSE as safe value.
  return FALSE;
}

The code works on Drupal 7.36 and higher, since it is using a function that previous versions didn't implement (user_has_role()).

As side note, since for inventory_scan/add_item you are using array(1, 2) as page arguments, the page callback would receive 'add_item' as argument, which doesn't make much sense; it would make sense if you were using the same page callback for different routes, and you would use 'add_item' to make the page callback know which action to perform.
In the same way, it doesn't make much sense to use array(1, 2) as page arguments for a route like inventory_scan; if you are not visiting inventory_scan/first_value/second_value, the page callback will not receive any arguments at all.

8
  • So I see what you did, it's the same as Pierre did above you correct? yet the only thing that confuses me if I do it your way the way you demonstrated above, is regarding what I have to do with Drupal then? How can I set the specifics regarding the certain roles I want to access this url I have now, in Drupal..? Thanks!
    – Y. Ben
    Jul 3, 2016 at 21:18
  • I have added my answer because I also explained what in your code was wrong. I will add an example of access callback that checks the role the logged-in user has.
    – apaderno
    Jul 4, 2016 at 11:26
  • 1. I understand what you're saying. I'm think I have Drupal 7.36 but bear with me, so basically when I pass in the name of the roles I have in the access callback (meaning the ones I predefined in drupal), then It'll compare it in my access callback function and either let them access or not!? Can I have multiple roles in that array separated by commas? And would i have to do anything on the drupal side or is this callback method all I have to do for it to work? 2. You said you wouldn't use the callback code, but why not? It seems logical to me!? Which method do you prefer best?
    – Y. Ben
    Jul 4, 2016 at 17:45
  • Checking for role is wrong for the reasons I said in the answer: If you need to check for other roles, you need to change the code; the code should also be changed every time you install the module in a site that uses other roles.
    – apaderno
    Jul 4, 2016 at 17:54
  • The approach I would use is implementing the first code I shown. In that way, you assign a user permission implemented from the module to the roles that need it. If you need to assign the user permission to other roles, you can do it, without changing any code. If you need to use the module in another site, with different roles, your code doesn't change, since it is always checks for the same user permission which you can assign to any roles you want.
    – apaderno
    Jul 4, 2016 at 17:55
1

Drupal access control is build on permissions granted to roles. So in order to restrict access to users with at least one of two given roles, you actually need to restrict access to users with a role to which the correct permission has been granted.

When defining a menu entry with a hook_menu() implementation, you can specify the function used for access control with the #access callback property, and the arguments for this function with the #access arguments property. They work just like the #page callback and #page arguments ones. The default value for #access callback is the user_access() function which is exactly the function you need to use to check if the currently logged user has a given permission. Therefore, you only need to set the #access arguments property so the function will receive the permission. Access control is inherited in menu items, so children does not need to repeat the settings from their parents.

/**
 * Implements hook_menu().
 */
function ibbr_inv_menu(){
  $items = array();
  //'Inventory Items' page
  $items['inventory_scan'] = array(
    'title' => 'Inventory Items',
    'page callback' => 'ibbr_inv_menu_page_callback',
    'access arguments' => array('administer ibbr inventory'),
    'page arguments' => array(1, 2),
    'type'=> MENU_CALLBACK,
  );
  //'Add new item' page
  $items['inventory_scan/add_item'] = array(
    'title' => 'Add new item',
    'page callback' => 'ibbr_inv_add_new_item_callback',
    'page arguments' => array(1, 2),
    'type'=> MENU_CALLBACK,
  );
  return $items;
}

You also need to define the permission with an implementation of hook_permission()

/**
 * Implements hook_permission()
 */
function ibbr_inv_permission() {
  $permissions = array();
  $permissions['administer ibbr inventory'] = array(
    'title' => t('Administer IBBR iventory'),
    'description' => t('Perform administration tasks for inventory items.'),
  );
  return $permissions;
}

And of course, you need to grant that permissions to the relevant roles.

4
  • Thank you Pierre, sorry i'm kind of a newbie with PHP and drupal 7 so please bear with me! So I understand that I dont need to repeat the same thing in my "add new item page" which is fine since its a child. But where am I actually passing in my role names? Because there are certain roles that I want the user to have in order to access it so where in my function or hook_menu can I pass them in to be checked? Thank you!
    – Y. Ben
    Jun 30, 2016 at 17:27
  • You don't reference the roles in your code. You reference a permission. Then, from the people > permissions administration interface, you grant the permission to the roles. Jun 30, 2016 at 17:29
  • See drupal.org/node/120614 Jun 30, 2016 at 17:31
  • I see, I don't know how to do that exactly hmm.. so do I filter the Permissions (by choosing the roles I want) and then choosing my custom module (so that only those roles have access to my module)?
    – Y. Ben
    Jun 30, 2016 at 17:36

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.