343 lines
11 KiB
Text
343 lines
11 KiB
Text
<?php
|
|
/**
|
|
* @file
|
|
*
|
|
* This module allows site administrators to grant some roles the authority to
|
|
* assign selected roles to users, without them needing the 'administer access
|
|
* control' permission.
|
|
*
|
|
* It provides its own tab in the user profile so that roles can be assigned
|
|
* without needing access to the user edit form.
|
|
*/
|
|
|
|
/**
|
|
* Implementation of hook_help().
|
|
*/
|
|
function role_delegation_help($section) {
|
|
switch ($section) {
|
|
case 'admin/help#role_delegation':
|
|
return '<p>'. t('This module allows site administrators to grant some roles the authority to assign selected roles to users, without them needing the <em>administer permissions</em> permission.') .'</p><p>'. t('It provides its own tab in the user profile so that roles can be assigned without needing access to the user edit form.') .'</p>';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_perm().
|
|
*/
|
|
function role_delegation_perm() {
|
|
$roles = _role_delegation_roles();
|
|
$perms = array('assign all roles');
|
|
foreach ($roles as $role) {
|
|
$perms[] = _role_delegation_make_perm($role);
|
|
}
|
|
return $perms;
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_menu().
|
|
*/
|
|
function role_delegation_menu() {
|
|
global $user;
|
|
$items = array();
|
|
|
|
$items['user/%user/roles'] = array(
|
|
'title' => 'Roles',
|
|
'page callback' => 'drupal_get_form',
|
|
'page arguments' => array('role_delegation_roles_form', 1),
|
|
'access callback' => 'role_delegation_access',
|
|
'type' => MENU_LOCAL_TASK,
|
|
);
|
|
return $items;
|
|
}
|
|
|
|
/**
|
|
* Provides a form for assigning roles to the current user.
|
|
*/
|
|
function role_delegation_roles_form(&$form_state, $account) {
|
|
$form['roles'] = array(
|
|
'#type' => 'fieldset',
|
|
'#title' => t('Roles'),
|
|
'#tree' => TRUE,
|
|
);
|
|
// Provide a separate checkbox for each role but hide those the user has no authority over.
|
|
$roles = _role_delegation_roles();
|
|
$roles_preserve = array('authenticated user');
|
|
foreach ($roles as $rid => $role) {
|
|
if (!(user_access('assign all roles') || user_access(_role_delegation_make_perm($role)) || user_access('administer permissions'))) {
|
|
// Hide roles the user can't assign.
|
|
$form['roles'][$rid] = array(
|
|
'#type' => 'value',
|
|
'#value' => isset($account->roles[$rid]),
|
|
);
|
|
if (isset($account->roles[$rid])) {
|
|
$roles_preserve[] = $role;
|
|
}
|
|
}
|
|
else {
|
|
$form['roles'][$rid] = array(
|
|
'#type' => 'checkbox',
|
|
'#title' => check_plain($role),
|
|
'#default_value' => isset($account->roles[$rid]),
|
|
);
|
|
}
|
|
}
|
|
$form['roles']['#description'] = t('The user receives the combined permissions of the %roles role(s), and all roles selected here. ', array('%roles' => implode(', ', $roles_preserve)));
|
|
$form['account'] = array(
|
|
'#type' => 'value',
|
|
'#value' => $account,
|
|
);
|
|
$form['submit'] = array(
|
|
'#type' => 'submit',
|
|
'#value' => t('Submit'),
|
|
);
|
|
|
|
drupal_set_title(check_plain($account->name));
|
|
return $form;
|
|
}
|
|
|
|
/**
|
|
* Saves the roles assigned to the account given in the form.
|
|
*/
|
|
function role_delegation_roles_form_submit($form, &$form_state) {
|
|
if (is_array($form_state['values']['roles']) && isset($form_state['values']['account']->uid)) {
|
|
$account = user_load(array('uid' => (int)$form_state['values']['account']->uid));
|
|
$myroles = array();
|
|
$rolenames = user_roles(TRUE);
|
|
foreach (array_keys(array_filter($form_state['values']['roles'])) as $rid) {
|
|
$myroles[$rid] = $rolenames[$rid];
|
|
}
|
|
user_save($account, array('roles' => $myroles));
|
|
|
|
// Delete the user's menu cache.
|
|
cache_clear_all($form_state['values']['account']->uid .':', 'cache_menu', TRUE);
|
|
|
|
drupal_set_message(t('The roles have been updated.'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Access callback for menu hook.
|
|
*/
|
|
function role_delegation_access() {
|
|
// Check access to user profile page.
|
|
if (!user_access('access user profiles')) {
|
|
return FALSE;
|
|
}
|
|
// Check if they can edit users. In that case, the Roles tab is not needed.
|
|
if (user_access('administer users')) {
|
|
return FALSE;
|
|
}
|
|
// Check access to role assignment page.
|
|
if (user_access('administer permissions')) {
|
|
return TRUE;
|
|
}
|
|
$perms = role_delegation_perm();
|
|
foreach ($perms as $perm) {
|
|
if (user_access($perm)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Returns all existing roles, except anonymous and authenticated user.
|
|
*/
|
|
function _role_delegation_roles() {
|
|
$roles = user_roles(TRUE);
|
|
unset($roles[DRUPAL_AUTHENTICATED_RID]);
|
|
return $roles;
|
|
}
|
|
|
|
/**
|
|
* Returns the delegation permission for a role.
|
|
*/
|
|
function _role_delegation_make_perm($role) {
|
|
return "assign $role role";
|
|
}
|
|
|
|
|
|
/**
|
|
* Implementation of hook_form_FORM_ID_alter() for user_admin_role().
|
|
*/
|
|
function role_delegation_form_user_admin_role_alter(&$form, $form_state)
|
|
{
|
|
// Put our submit function in line ahead of user_admin_role_submit(),
|
|
// so that it can make use of the old role name before it's changed to the new.
|
|
array_unshift($form['#submit'], 'role_delegation_form_user_admin_role_submit');
|
|
}
|
|
|
|
/**
|
|
* Submit function for the user_admin_role form:
|
|
* When a role is renamed or deleted, rename or delete the permission
|
|
* to assign that role.
|
|
*/
|
|
function role_delegation_form_user_admin_role_submit($form, &$form_state) {
|
|
$op = $form_state['values']['op'];
|
|
if ($op != t('Save role') && $op != t('Delete role')) {
|
|
return;
|
|
}
|
|
$rid = $form_state['values']['rid'];
|
|
$oldrole = db_result(db_query('SELECT name FROM {role} WHERE rid = %d', $rid));
|
|
$newrole = $form_state['values']['name'];
|
|
if ($op == t('Save role') && $oldrole == $newrole) {
|
|
return;
|
|
}
|
|
// Role is being renamed or deleted.
|
|
// Loop through permission lists for all roles, renaming or deleting the
|
|
// 'assign' permission for this role.
|
|
$oldperm = _role_delegation_make_perm($oldrole);
|
|
$result = db_query('SELECT * FROM {permission}');
|
|
while ($row = db_fetch_object($result)) {
|
|
$perms = explode(', ', $row->perm);
|
|
for ($i = 0; $i < count($perms); ++$i) {
|
|
if ($perms[$i] == $oldperm) {
|
|
switch ($op) {
|
|
case t('Save role'):
|
|
$perms[$i] = _role_delegation_make_perm($newrole);
|
|
break;
|
|
case t('Delete role'):
|
|
unset($perms[$i]);
|
|
break;
|
|
}
|
|
if (count($perms)) {
|
|
db_query("UPDATE {permission} SET perm = '%s' WHERE pid = %d",
|
|
implode(', ', $perms), $row->pid);
|
|
} else {
|
|
db_query("DELETE FROM {permission} WHERE pid = %d", $row->pid);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_user().
|
|
*/
|
|
function role_delegation_user($op, &$edit, &$account, $category = NULL) {
|
|
if ($op == 'register' || ($op == 'form' && $category == 'account')) {
|
|
// Only alter user form when user can't assign permissions without Role Delegation.
|
|
if (!user_access('administer permissions')) {
|
|
// Split up roles based on whether they can be delegated or not.
|
|
$current_roles = isset($account->roles) ? $account->roles : array();
|
|
$rids_default = array();
|
|
$rids_preserve = array(DRUPAL_AUTHENTICATED_RID => DRUPAL_AUTHENTICATED_RID);
|
|
$roles_preserve = array('authenticated user');
|
|
$roles_options = array();
|
|
$roles = _role_delegation_roles();
|
|
foreach ($roles as $rid => $role) {
|
|
if (user_access('assign all roles') || user_access(_role_delegation_make_perm($role))) {
|
|
if (array_key_exists($rid, $current_roles)) {
|
|
$rids_default[] = $rid;
|
|
}
|
|
$roles_options[$rid] = $role;
|
|
}
|
|
else {
|
|
if (array_key_exists($rid, $current_roles)) {
|
|
$rids_preserve[$rid] = $rid;
|
|
$roles_preserve[] = $role;
|
|
}
|
|
}
|
|
}
|
|
if (empty($roles_options)) {
|
|
// No role can be assigned.
|
|
return;
|
|
}
|
|
// Generate the form items.
|
|
$form['roles_preserve'] = array(
|
|
'#type' => 'value',
|
|
'#value' => $rids_preserve,
|
|
);
|
|
$roles_assign = array(
|
|
'#type' => 'checkboxes',
|
|
'#title' => t('Roles'),
|
|
'#description' => t('The user receives the combined permissions of the %roles role(s), and all roles selected here. ', array('%roles' => implode(', ', $roles_preserve))),
|
|
'#options' => $roles_options,
|
|
'#default_value' => $rids_default,
|
|
'#weight' => 10,
|
|
);
|
|
if ($op == 'register') {
|
|
// Since the user module does array_merge() instead of array_merge_recursive() (see bug http://drupal.org/node/227690),
|
|
// we must move this under 'account' later at role_delegation_form_user_register_alter()
|
|
$form['roles_assign'] = $roles_assign;
|
|
}
|
|
else {
|
|
$form['account']['roles_assign'] = $roles_assign;
|
|
}
|
|
return $form;
|
|
}
|
|
}
|
|
elseif (isset($edit['roles_assign']) && ($op == 'insert' || $op == 'submit')) {
|
|
$edit['roles'] = $edit['roles_preserve'] + array_filter($edit['roles_assign']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_form_FORM_ID_alter() for user_register().
|
|
*/
|
|
function role_delegation_form_user_register_alter(&$form, $form_state) {
|
|
// Move our field where it belongs
|
|
if (isset($form['roles_assign'])) {
|
|
$form['account']['roles_assign'] = $form['roles_assign'];
|
|
unset($form['roles_assign']);
|
|
$form['account']['notify']['#weight'] = 11;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_user_operations().
|
|
*/
|
|
function role_delegation_user_operations($form_state = array()) {
|
|
// Only provide role add/remove operations when user can't assign permissions
|
|
// without Role Delegation.
|
|
if (user_access('administer permissions')) {
|
|
return;
|
|
}
|
|
|
|
// Provide add/remove operations for delegated roles.
|
|
$roles = _role_delegation_roles();
|
|
$add_roles = array();
|
|
$remove_roles = array();
|
|
foreach ($roles as $rid => $role) {
|
|
if (user_access('assign all roles') || user_access(_role_delegation_make_perm($role))) {
|
|
// Use different operation names than those from user_user_operations(),
|
|
// to keep user_user_operations() from emitting a warning about the
|
|
// permissions.
|
|
$add_roles['role_delegation_add_role-' . $rid] = $role;
|
|
$remove_roles['role_delegation_remove_role-' . $rid] = $role;
|
|
}
|
|
}
|
|
if (!count($add_roles)) {
|
|
return;
|
|
}
|
|
$operations = array(
|
|
t('Add a role to the selected users') => array(
|
|
'label' => $add_roles,
|
|
),
|
|
t('Remove a role from the selected users') => array(
|
|
'label' => $remove_roles,
|
|
),
|
|
);
|
|
|
|
// If the form has been posted, insert the proper data for role editing if necessary.
|
|
if (!empty($form_state['submitted'])) {
|
|
$operation_rid = explode('-', $form_state['values']['operation']);
|
|
$operation = $operation_rid[0];
|
|
if ($operation == 'role_delegation_add_role' || $operation == 'role_delegation_remove_role') {
|
|
$rid = $operation_rid[1];
|
|
if ($add_roles['role_delegation_add_role-' . $rid]) {
|
|
$operations[$form_state['values']['operation']] = array(
|
|
'callback' => 'user_multiple_role_edit',
|
|
'callback arguments' => array(str_replace('role_delegation_', '', $operation), $rid),
|
|
);
|
|
}
|
|
else {
|
|
watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $operations;
|
|
}
|