secure_permissions.module in Secure Permissions 6
Same filename and directory in other branches
Secure Permissions module file.
This module was inspired by the Plone security paradigm of only allowing permissions to be set in code.
Inspired by @djay75 via Twitter.
File
secure_permissions.moduleView source
<?php
/**
* @file
* Secure Permissions module file.
*
* This module was inspired by the Plone security paradigm
* of only allowing permissions to be set in code.
*
* @see http://plone.org/products/plone/security/overview/security-overview-of-plone
*
* Inspired by @djay75 via Twitter.
*/
/**
* These definitions set the security options for the module, and
* can be reset in settings.php using $conf.
*/
// Disable forms?
define('SECURE_PERMISSIONS_DISABLE_FORMS', FALSE);
// Run the rebuild process?
define('SECURE_PERMISSIONS_ACTIVE', FALSE);
// Show the permissions page at all?
define('SECURE_PERMISSIONS_SHOW_PERMISSIONS_PAGE', TRUE);
// Show the roles page at all?
define('SECURE_PERMISSIONS_SHOW_ROLES_PAGE', TRUE);
// Define an 'administrative user' role?
define('SECURE_PERMISSIONS_ADMINISTRATIVE_ROLE', TRUE);
// Name of the administrator role.
define('SECURE_PERMISSIONS_ROLE_NAME', 'administrator');
// Display message when rebuilding permisisons?
define('SECURE_PERMISSIONS_VERBOSE', TRUE);
// Rebuild default site permissions?
define('SECURE_PERMISSIONS_USE_DEFAULT', FALSE);
/**
* Internal variable hook.
*
* @param $name
* The name of the variable to return.
* @return
* The value of the variable.
*/
function secure_permissions_variable($name) {
if (!empty($name)) {
return variable_get($name, constant(strtoupper($name)));
}
}
/**
* Implement hook_menu().
*/
function secure_permissions_menu() {
$items = array();
$items['admin/user/secure_permissions'] = array(
'title' => 'Secure permissions',
'description' => 'Configuration for the secure permissions module.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'secure_permissions_form',
),
'access arguments' => array(
'export secure permissions',
),
);
$items['admin/user/secure_permissions/view'] = array(
'title' => 'Settings',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/user/secure_permissions/export'] = array(
'title' => 'Export permissions',
'description' => 'Export site permissions for use by Secure Permissions.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'secure_permissions_export',
),
'access arguments' => array(
'export secure permissions',
),
'type' => MENU_LOCAL_TASK,
'weight' => 5,
);
return $items;
}
/**
* Implement hook_perm().
*/
function secure_permissions_perm() {
return array(
'export secure permissions',
);
}
/**
* Implement hook_menu_alter().
*
* If required, remove access to the permissions and roles screens.
*/
function secure_permissions_menu_alter(&$items) {
// Permission administration pages.
$show_roles = secure_permissions_variable('secure_permissions_show_roles_page');
if (!$show_roles) {
$items['admin/user/roles'] = array(
'access callback' => 'secure_permissions_deny_access',
'type' => MENU_CALLBACK,
);
$items['admin/user/roles/edit'] = array(
'access callback' => 'secure_permissions_deny_access',
'type' => MENU_CALLBACK,
);
}
$show_permissions = secure_permissions_variable('secure_permissions_show_permissions_page');
if (!$show_permissions) {
$items['admin/user/permissions'] = array(
'access callback' => 'secure_permissions_deny_access',
'type' => MENU_CALLBACK,
);
}
}
/**
* Menu access callback; always return FALSE to deny access
* to the roles and permissions screens.
*/
function secure_permissions_deny_access() {
return FALSE;
}
/**
* Rebuild permissions, based on presets from the API.
*
* It is important to always call this function, instead of the individual
* build functions, since this rebuild call sanity-checks the module settings.
*/
function secure_permissions_rebuild() {
// Killswitch for the module, to let admins export permissions before continuing.
// If only one module responds, it is the core module and we cannot rebuild.
$modules = module_implements('secure_permissions');
if (!secure_permissions_variable('secure_permissions_active') || count($modules) < 2) {
return;
}
$rebuild_roles = secure_permissions_build_roles();
$rebuild_perms = secure_permissions_build_permissions();
if (secure_permissions_variable('secure_permissions_verbose') && $rebuild_roles && $rebuild_perms) {
drupal_set_message(t('Site roles and permissions have been rebuilt successfully.'), 'status', FALSE);
}
}
/**
* Get all roles defined by the API.
*/
function secure_permissions_get_roles() {
$roles = array_unique(module_invoke_all('secure_permissions_roles'));
sort($roles);
return $roles;
}
/**
* Build the roles table correctly.
*/
function secure_permissions_build_roles() {
// Get the currently defined roles for the site, and sort() them so
// we can diff the arrays properly.
$roles = user_roles();
sort($roles);
// Get the roles defined by this module's hook.
$secure_roles = secure_permissions_get_roles();
if (empty($secure_roles)) {
return FALSE;
}
// Compute the difference for add/delete.
$new_roles = array_diff($secure_roles, $roles);
$remove_roles = array_diff($roles, $secure_roles);
// Add new roles.
foreach ($new_roles as $rid => $name) {
$role = new stdClass();
$role->name = $name;
drupal_write_record('role', $role);
}
// Delete old roles.
foreach ($remove_roles as $rid => $name) {
// Never delete the default roles.
if (!in_array($rid, array(
DRUPAL_ANONYMOUS_RID,
DRUPAL_AUTHENTICATED_RID,
))) {
db_query("DELETE FROM {role} WHERE name = '%s'", $name);
db_query("DELETE FROM {permission} WHERE rid = %d", $rid);
}
}
return TRUE;
}
/**
* Build function to create the permissions arrays.
*/
function secure_permissions_build_permissions() {
// Get the active roles on the site.
$roles = user_roles();
// List all permissions.
$permissions = array_values(module_invoke_all('perm'));
// Now set permissions per role, using our hook.
foreach ($roles as $rid => $role) {
$new_permissions = module_invoke_all('secure_permissions', $role);
if (empty($new_permissions)) {
return FALSE;
}
// Revoke all permissions.
db_query("DELETE FROM {permission} WHERE rid = %d", $rid);
permissions_grant_permissions($role, $new_permissions);
}
return TRUE;
}
/**
* Implement hook_secure_permissions().
*
* If configured to do so, this function will restore the default site
* permissions that ship with Drupal. It will also maintain an
* administrative role that has all permissions.
*
* Important: If you use this module, you must implement this
* hook in your own code, or else risk having permissions reset.
*/
function secure_permissions_secure_permissions($role) {
$permissions = array();
// Use the default permissions granted by Drupal core?
if (secure_permissions_variable('secure_permissions_use_default')) {
$permissions['anonymous user'] = array(
'access content',
);
$permissions['authenticated user'] = array(
'access comments',
'access content',
'post comments',
'post comments without approval',
);
}
// Add all permissions to the administrative role?
if ($role == secure_permissions_variable('secure_permissions_role_name') && secure_permissions_variable('secure_permissions_administrative_role')) {
$permissions[$role] = array_values(module_invoke_all('perm'));
}
// Return the permissions.
if (isset($permissions[$role])) {
return $permissions[$role];
}
}
/**
* Implement hook_secure_permissions_roles().
*
* Defines the roles available on a site.
*
* Important: If you have custom roles on your site, you must
* implement this hook to retain those roles.
*/
function secure_permissions_secure_permissions_roles() {
$roles = array(
'anonymous user',
'authenticated user',
);
if (secure_permissions_variable('secure_permissions_administrative_role')) {
$roles[] = secure_permissions_variable('secure_permissions_role_name');
}
return $roles;
}
/**
* Implement hook_form_alter().
*
* Disables editing of permissions through the user interface.
*/
function secure_permissions_form_user_admin_perm_alter(&$form, $form_state) {
if (!secure_permissions_variable('secure_permissions_disable_forms')) {
return;
}
foreach (element_children($form['checkboxes']) as $key) {
$form['checkboxes'][$key]['#disabled'] = TRUE;
}
drupal_set_message(t('Editing of permissions is not permitted through the user interface. The table below shows the active permissions for the site.'));
unset($form['submit']);
}
/**
* Implement hook_form_alter().
*
* Disables editing of roles through the user interface.
*/
function secure_permissions_form_user_admin_role_alter(&$form, $form_state) {
if (!secure_permissions_variable('secure_permissions_disable_forms')) {
return;
}
$form['name']['#disabled'] = TRUE;
drupal_set_message(t('Editing of roles is not permitted through the user interface.'));
unset($form['submit']);
unset($form['delete']);
}
/**
* Implement hook_form_alter().
*
* Disables creation of roles through the user interface.
*/
function secure_permissions_form_user_admin_new_role_alter(&$form, $form_state) {
if (!secure_permissions_variable('secure_permissions_disable_forms')) {
return;
}
drupal_set_message(t('Editing of roles is not permitted through the user interface.'));
unset($form['submit']);
unset($form['name']);
}
/**
* Page callback to generate roles and permissions in code.
*/
function secure_permissions_export(&$form_state) {
$form = array();
$form['help'] = array(
'#value' => t('The Secure permissions module stores the permissions in a module (file) that is inaccessible through
the user interface.<br />You now need to create and enable that module in 4 easy steps.<ol><li>Create directory.
cd to /sites/all/modules and issue the command: mkdir secure_permissions_data<li>Create 2 empty files. cd to
/sites/all/modules/secure_permissions_data and issue the command: touch secure_permissions_data.info
secure_permissions_data.module<li>Copy data. Copy the text from the fields below into the respective files you just
created using the tools of your choice.<li>Enable the module. Navigate to admin/build/modules/list and enable your
new module.</ol>To change permissions with the module enabled, you must now edit your
/sites/all/modules/secure_permissions_data/secure_permissions_data.module file. After editing the file navigate to
/admin/user/secure_permissions/view select \'Load permissions from code\' and click \'Save configuration\' to update
the permissions. You may rename the module; remember to rename all the functions.'),
);
$output = '';
$output .= "name = Secure Permissions Data\n";
$output .= "description = Role and permission settings for the site.\n";
$output .= "core = 6.x\n";
$output .= "dependencies[] = secure_permissions\n";
$lines = explode("\n", $output);
$form['info'] = array(
'#title' => t('Permissions output -- secure_permissions_data.info'),
'#type' => 'textarea',
'#cols' => 40,
'#rows' => count($lines),
'#default_value' => $output,
'#description' => t('Module .info file for storing secure permissions.'),
);
$output = '';
// Get roles.
$roles = user_roles();
$output .= <<<EOT
<?php
/**
* @file Secure Permissions Data
* Module file for secure permissions in code.
*/
/**
* Define site roles in code.
*
* Create a secure_permissions_data module directory and place this function
* in secure_permissions_data.module.
*
* @return
* An array defining all the roles for the site.
*/
EOT;
$output .= 'function secure_permissions_data_secure_permissions_roles() {';
$output .= "\n return array(\n";
foreach ($roles as $role) {
$output .= " '" . $role . "',\n";
}
$output .= " );";
$output .= "\n}\n\n";
// Now get permissions.
$output .= <<<EOT
/**
* Define site permissions in code.
*
* Create a secure_permissions_data module directory and place this function
* in secure_permissions_data.module.
*
* @param \$role
* The role for which the permissions are being requested.
*
* @return
* An array defining all the permissions for the site.
*/
EOT;
$output .= 'function secure_permissions_data_secure_permissions($role) {';
$output .= "\n \$permissions = array(\n";
foreach ($roles as $rid => $role) {
$output .= " '{$role}' => array(\n";
$permissions = permissions_get_permissions_for_role($role);
foreach ($permissions as $key => $permission) {
$output .= " '{$permission}',\n";
}
$output .= " ),\n";
}
$output .= " );\n";
$output .= " if (isset(\$permissions[\$role])) {";
$output .= "\n return \$permissions[\$role];\n";
$output .= " }\n";
$output .= "}";
$lines = explode("\n", $output);
$form['export'] = array(
'#title' => t('Permissions output -- secure_permissions_data.module'),
'#type' => 'textarea',
'#cols' => 40,
'#rows' => count($lines),
'#default_value' => $output,
'#description' => t('Module .module file for storing secure permissions.'),
);
return $form;
}
/**
* Configuration form for the module.
*/
function secure_permissions_form() {
$form = array();
// Check which modules run our hook,
$modules = module_implements('secure_permissions');
$files = module_rebuild_cache();
$items = array();
foreach ($modules as $module) {
if ($module != 'secure_permissions') {
$items[] = check_plain($files[$module]->info['name']);
}
}
$module_list = theme('item_list', $items);
$extra = '';
if (count($modules) == 1) {
$extra = t('Your permissions have not been <a href="!url">exported to code</a> yet. You may need to do so before activating this module.', array(
'!url' => url('admin/user/secure_permissions/export'),
));
}
$form['help'] = array(
'#type' => 'markup',
'#value' => t('The following modules implement secure permissions: !list !extra', array(
'!list' => $module_list,
'!extra' => $extra,
)),
);
$form['user_interface'] = array(
'#type' => 'fieldset',
'#title' => t('User interface settings'),
);
$form['user_interface']['secure_permissions_disable_forms'] = array(
'#type' => 'checkbox',
'#default_value' => secure_permissions_variable('secure_permissions_disable_forms'),
'#title' => t('Disable permissions and roles forms'),
'#description' => t('Disables the ability to edit or add permissions and roles through the user interface.'),
);
$form['user_interface']['secure_permissions_show_permissions_page'] = array(
'#type' => 'checkbox',
'#default_value' => secure_permissions_variable('secure_permissions_show_permissions_page'),
'#title' => t('Show permissions page'),
'#description' => t('Allows administrators to view the permissions overview page.'),
);
$form['user_interface']['secure_permissions_show_roles_page'] = array(
'#type' => 'checkbox',
'#default_value' => secure_permissions_variable('secure_permissions_show_roles_page'),
'#title' => t('Show roles page'),
'#description' => t('Allows administrators to view the roles overview page.'),
);
$form['user_interface']['secure_permissions_verbose'] = array(
'#type' => 'checkbox',
'#default_value' => secure_permissions_variable('secure_permissions_verbose'),
'#title' => t('Display permissions updates'),
'#description' => t('Prints a message to the screen whenever permissions are updated.'),
);
$form['code'] = array(
'#type' => 'fieldset',
'#title' => t('API settings'),
'#description' => t('If the <em>Load permissions from code</em> setting is not enabled, none of the features below will be enabled.'),
);
$form['code']['secure_permissions_active'] = array(
'#type' => 'checkbox',
'#default_value' => secure_permissions_variable('secure_permissions_active'),
'#title' => t('Load permissions from code'),
'#description' => t('Allows permissions and roles to be defined in code, replacing values set through the user interface.'),
);
$form['code']['secure_permissions_use_default'] = array(
'#type' => 'checkbox',
'#default_value' => secure_permissions_variable('secure_permissions_use_default'),
'#title' => t('Reload default permissions on rebuild'),
'#description' => t('Sets the default Drupal permissions for anonymous and authenticated users.'),
);
$form['code']['secure_permissions_administrative_role'] = array(
'#type' => 'checkbox',
'#default_value' => secure_permissions_variable('secure_permissions_administrative_role'),
'#title' => t('Use administrative role'),
'#description' => t('Maintains an administrative role that is granted all permissions.'),
);
$form['code']['secure_permissions_role_name'] = array(
'#type' => 'textfield',
'#default_value' => secure_permissions_variable('secure_permissions_role_name'),
'#title' => t('Administrative role name'),
'#required' => TRUE,
'#description' => t('The name of the administrative role. Normally this need not be changed.'),
);
// Make sure the menu is rebuilt correctly.
$form['#submit'][] = 'secure_permissions_form_submit';
if (isset($_SESSION['secure_permissions_rebuild'])) {
unset($_SESSION['secure_permissions_rebuild']);
menu_rebuild();
}
return system_settings_form($form);
}
/**
* We cannot rebuild the menu baed on a setting during form submit,
* at least, not without special handling.
*
* So set a session value to indicate we must rebuild the menus.
*
* Also rebuilds permissions, if necessary.
*/
function secure_permissions_form_submit($form, &$form_state) {
global $conf;
$modules = module_implements('secure_permissions');
$_SESSION['secure_permissions_rebuild'] = TRUE;
if ($form_state['values']['secure_permissions_active'] && count($modules) > 1) {
// We must do this to pass the value to the calling function during submit.
$conf['secure_permissions_active'] = TRUE;
$conf['secure_permissions_administrative_role'] = $form_state['values']['secure_permissions_administrative_role'];
$conf['secure_permissions_role_name'] = $form_state['values']['secure_permissions_role_name'];
$conf['secure_permissions_use_default'] = $form_state['values']['secure_permissions_use_default'];
secure_permissions_rebuild();
}
else {
drupal_set_message(t('Permissions cannot be rebuilt from code at this time.'));
}
}
/**
* In Drupal 6, we do not have a hook for when modules are enabled or
* disabled, so we must add a submit to the form.
*/
function secure_permissions_form_system_modules_alter($form, &$form_state) {
$modules = module_implements('secure_permissions');
$form['secure_permissions'] = array(
'#type' => 'value',
'#value' => $modules,
);
$form['#submit'][] = 'secure_permissions_modules_submit';
}
/**
* Rebuild permissions on module page submission.
*/
function secure_permissions_modules_submit($form, &$form_state) {
$status = $form_state['values']['status'];
$modules = $form_state['values']['secure_permissions'];
$modules_new = module_implements('secure_permissions');
// If module_implements is greater than the form value, a new module
// has been activated for secure_permissions.
$rebuild = TRUE;
// If only the core module is enabled, do not rebuild.
if (count($modules_new) == 1) {
variable_set('secure_permissions_active', 0);
drupal_set_message(t('Loading permissions from code has been disabled. You may <a href="!url">re-enable it</a>.', array(
'!url' => url('admin/user/secure_permissions'),
)));
return;
}
foreach ($modules as $module) {
if (empty($status[$module])) {
$rebuild = FALSE;
}
}
if ($rebuild) {
secure_permissions_rebuild();
}
}
Functions
Name | Description |
---|---|
secure_permissions_build_permissions | Build function to create the permissions arrays. |
secure_permissions_build_roles | Build the roles table correctly. |
secure_permissions_deny_access | Menu access callback; always return FALSE to deny access to the roles and permissions screens. |
secure_permissions_export | Page callback to generate roles and permissions in code. |
secure_permissions_form | Configuration form for the module. |
secure_permissions_form_submit | We cannot rebuild the menu baed on a setting during form submit, at least, not without special handling. |
secure_permissions_form_system_modules_alter | In Drupal 6, we do not have a hook for when modules are enabled or disabled, so we must add a submit to the form. |
secure_permissions_form_user_admin_new_role_alter | Implement hook_form_alter(). |
secure_permissions_form_user_admin_perm_alter | Implement hook_form_alter(). |
secure_permissions_form_user_admin_role_alter | Implement hook_form_alter(). |
secure_permissions_get_roles | Get all roles defined by the API. |
secure_permissions_menu | Implement hook_menu(). |
secure_permissions_menu_alter | Implement hook_menu_alter(). |
secure_permissions_modules_submit | Rebuild permissions on module page submission. |
secure_permissions_perm | Implement hook_perm(). |
secure_permissions_rebuild | Rebuild permissions, based on presets from the API. |
secure_permissions_secure_permissions | Implement hook_secure_permissions(). |
secure_permissions_secure_permissions_roles | Implement hook_secure_permissions_roles(). |
secure_permissions_variable | Internal variable hook. |