Commit e8364f51 authored by webchick's avatar webchick

#309007 by moshe weitzman, agentrickard, and mcarbone: Add...

#309007 by moshe weitzman, agentrickard, and mcarbone: Add hook_node_access_records_alter() and hook_node_grants_alter() to allow complex interactions between two or more node access modules.
parent 9eeec334
......@@ -108,6 +108,113 @@ function hook_node_access_records($node) {
}
}
/**
* Alter permissions for a node before it is written to the database.
*
* Node access modules establish rules for user access to content. Node access
* records are stored in the {node_access} table and define which permissions
* are required to access a node. This hook is invoked after node access modules
* returned their requirements via hook_node_access_records(); doing so allows
* modules to modify the $grants array by reference before it is stored, so
* custom or advanced business logic can be applied.
*
* @see hook_node_access_records()
*
* Upon viewing, editing or deleting a node, hook_node_grants() builds a
* permissions array that is compared against the stored access records. The
* user must have one or more matching permissions in order to complete the
* requested operation.
*
* @see hook_node_grants()
* @see hook_node_grants_alter()
*
* @param &$grants
* The $grants array returned by hook_node_access_records().
* @param $node
* The node for which the grants were acquired.
*
* The preferred use of this hook is in a module that bridges multiple node
* access modules with a configurable behavior, as shown in the example
* by the variable 'example_preview_terms'. This variable would
* be a configuration setting for your module.
*
* @ingroup node_access
*/
function hook_node_access_records_alter(&$grants, $node) {
// Our module allows editors to tag specific articles as 'preview'
// content using the taxonomy system. If the node being saved
// contains one of the preview terms defined in our variable
// 'example_preview_terms', then only our grants are retained,
// and other grants are removed. Doing so ensures that our rules
// are enforced no matter what priority other grants are given.
$preview = variable_get('example_preview_terms', array());
// Check to see if we have enabled complex behavior.
if (!empty($preview)) {
foreach ($preview as $term_id) {
if (isset($node->taxonomy[$term_id])) {
// Our module grants are set in $grants['example'].
$temp = $grants['example'];
// Now remove all module grants but our own.
$grants = array('example' => $temp);
// No need to check additonal terms.
break;
}
}
}
}
/**
* Alter user access rules when trying to view, edit or delete a node.
*
* Node access modules establish rules for user access to content.
* hook_node_grants() defines permissions for a user to view, edit or
* delete nodes by building a $grants array that indicates the permissions
* assigned to the user by each node access module. This hook is called to allow
* modules to modify the $grants array by reference, so the interaction of
* multiple node access modules can be altered or advanced business logic can be
* applied.
*
* @see hook_node_grants()
*
* The resulting grants are then checked against the records stored in the
* {node_access} table to determine if the operation may be completed.
*
* @see hook_node_access_records()
* @see hook_node_access_records_alter()
*
* @param &$grants
* The $grants array returned by hook_node_grants().
* @param $account
* The user account requesting access to content.
* @param $op
* The operation being performed, 'view', 'update' or 'delete'.
*
* Developers may use this hook to either add additional grants to a user
* or to remove existing grants. These rules are typically based on either the
* permissions assigned to a user role, or specific attributes of a user
* account.
*
* @ingroup node_access
*/
function hook_node_grants_alter(&$grants, $account, $op) {
// Our sample module never allows certain roles to edit or delete
// content. Since some other node access modules might allow this
// permission, we expressly remove it by returning an empty $grants
// array for roles specified in our variable setting.
// Get our list of banned roles.
$restricted = variable_get('example_restricted_roles', array());
if ($op != 'view' && !empty($restricted)) {
// Now check the roles for this account against the restrictions.
foreach ($restricted as $role_id) {
if (isset($user->roles[$role_id])) {
$grants = array();
}
}
}
}
/**
* Add mass node operations.
*
......
......@@ -2523,6 +2523,10 @@ function _node_access_where_sql($op = 'view', $node_access_alias = 'na', $accoun
* access module should implement hook_node_grants() to provide a grant
* list for the user.
*
* After the default grants have been loaded, we allow modules to alter
* the grants array by reference. This hook allows for complex business
* logic to be applied when integrating multiple node access modules.
*
* @param $op
* The operation that the user is trying to perform.
* @param $account
......@@ -2538,7 +2542,12 @@ function node_access_grants($op, $account = NULL) {
$account = $GLOBALS['user'];
}
return array_merge(array('all' => array(0)), module_invoke_all('node_grants', $account, $op));
// Fetch node access grants from other modules.
$grants = module_invoke_all('node_grants', $account, $op);
// Allow modules to alter the assigned grants.
drupal_alter('node_grants', $grants, $account, $op);
return array_merge(array('all' => array(0)), $grants);
}
/**
......@@ -2562,7 +2571,7 @@ function node_access_view_all_nodes() {
$grants = db_or();
foreach (node_access_grants('view') as $realm => $gids) {
foreach ($gids as $gid) {
$or->condition(db_and()
$grants->condition(db_and()
->condition('gid', $gid)
->condition('realm', $realm)
);
......@@ -2636,6 +2645,12 @@ function node_query_node_access_alter(QueryAlterableInterface $query) {
* called by modules whenever something other than a node_save causes
* the permissions on a node to change.
*
* After the default grants have been loaded, we allow modules to alter
* the grants array by reference. This hook allows for complex business
* logic to be applied when integrating multiple node access modules.
*
* @see hook_node_access_records()
*
* This function is the only function that should write to the node_access
* table.
*
......@@ -2644,6 +2659,9 @@ function node_query_node_access_alter(QueryAlterableInterface $query) {
*/
function node_access_acquire_grants($node) {
$grants = module_invoke_all('node_access_records', $node);
// Let modules alter the grants.
drupal_alter('node_access_records', $grants, $node);
// If no grants are set, then use the default grant.
if (empty($grants)) {
$grants[] = array('realm' => 'all', 'gid' => 0, 'grant_view' => 1, 'grant_update' => 0, 'grant_delete' => 0);
}
......
......@@ -684,6 +684,72 @@ class NodeRSSContentTestCase extends DrupalWebTestCase {
}
}
/**
* Test case to verify hook_node_access_records_alter functionality.
*/
class NodeAccessRecordsAlterUnitTest extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => t('Node access records alter'),
'description' => t('Test hook_node_access_records_alter when acquiring grants.'),
'group' => t('Node'),
);
}
function setUp() {
// Enable dummy module that implements hook_node_grants(),
// hook_node_access_records(), hook_node_grants_alter() and
// hook_node_access_records_alter().
parent::setUp('node_test');
}
/**
* Create a node and test the creation of node access rules.
*/
function testGrantAlter() {
// Create an article node.
$node1 = $this->drupalCreateNode(array('type' => 'article'));
$this->assertTrue(node_load($node1->nid), t('Article node created.'));
// Check to see if grants added by node_test_node_access_records made it in.
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = %d', $node1->nid)->fetchAll();
$this->assertEqual(count($records), 1, t('Returned the correct number of rows.'));
$this->assertEqual($records[0]->realm, 'test_article_realm', t('Grant with article_realm acquired for node without alteration.'));
$this->assertEqual($records[0]->gid, 1, t('Grant with gid = 1 acquired for node without alteration.'));
// Create an unpromoted page node.
$node2 = $this->drupalCreateNode(array('type' => 'page', 'promote' => 0));
$this->assertTrue(node_load($node1->nid), t('Unpromoted page node created.'));
// Check to see if grants added by node_test_node_access_records made it in.
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = %d', $node2->nid)->fetchAll();
$this->assertEqual(count($records), 1, t('Returned the correct number of rows.'));
$this->assertEqual($records[0]->realm, 'test_page_realm', t('Grant with page_realm acquired for node without alteration.'));
$this->assertEqual($records[0]->gid, 1, t('Grant with gid = 1 acquired for node without alteration.'));
// Create a promoted page node.
$node3 = $this->drupalCreateNode(array('type' => 'page', 'promote' => 1));
$this->assertTrue(node_load($node3->nid), t('Promoted page node created.'));
// Check to see if grant added by node_test_node_access_records was altered
// by node_test_node_access_records_alter.
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = %d', $node3->nid)->fetchAll();
$this->assertEqual(count($records), 1, t('Returned the correct number of rows.'));
$this->assertEqual($records[0]->realm, 'test_alter_realm', t('Altered grant with alter_realm acquired for node.'));
$this->assertEqual($records[0]->gid, 2, t('Altered grant with gid = 2 acquired for node.'));
// Check to see if we can alter grants with hook_node_grants_alter().
$operations = array('view', 'update', 'delete');
// Create a user that is allowed to access content.
$web_user = $this->drupalCreateUser(array('access content'));
foreach ($operations as $op) {
$grants = node_test_node_grants($op, $web_user);
$altered_grants = drupal_alter($grants, $web_user, $op);
$this->assertNotEqual($grants, $altered_grants, t('Altered the %op grant for a user.', array('%op' => $op)));
}
}
}
/**
* Test case to check node save related functionality, including import-save
*/
......
......@@ -33,3 +33,70 @@ function node_test_node_view($node, $teaser) {
);
}
}
/**
* Implementation of hook_node_grants().
*/
function node_test_node_grants($account, $op) {
// Give everyone full grants so we don't break other node tests.
// Our node access tests asserts three realms of access.
// @see testGrantAlter()
return array(
'test_article_realm' => array(1),
'test_page_realm' => array(1),
'test_alter_realm' => array(2),
);
}
/**
* Implementation of hook_node_access_records().
*/
function node_test_node_access_records($node) {
$grants = array();
if ($node->type == 'article') {
// Create grant in arbitrary article_realm for article nodes.
$grants[] = array(
'realm' => 'test_article_realm',
'gid' => 1,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 0,
);
}
elseif ($node->type == 'page') {
// Create grant in arbitrary page_realm for page nodes.
$grants[] = array(
'realm' => 'test_page_realm',
'gid' => 1,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 0,
);
}
return $grants;
}
/**
* Implementation of hook_node_access_records_alter().
*/
function node_test_node_access_records_alter(&$grants, $node) {
if (!empty($grants)) {
foreach ($grants as $key => $grant) {
// Alter grant from test_page_realm to test_alter_realm and modify the gid.
if ($grant['realm'] == 'test_page_realm' && $node->promote) {
$grants[$key]['realm'] = 'test_alter_realm';
$grants[$key]['gid'] = 2;
}
}
}
}
/**
* Implementation of hook_node_grants_alter().
*/
function node_test_node_grants_alter(&$grants, $account, $op) {
// Return an empty array of grants to prove that we can alter by reference.
$grants = array();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment