summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAngie Byron <webchick@24967.no-reply.drupal.org>2009-05-27 02:01:54 +0000
committerAngie Byron <webchick@24967.no-reply.drupal.org>2009-05-27 02:01:54 +0000
commite8364f5156decde5b6d5c9f79dbe01910c39a89a (patch)
tree8c00563b0557f12cab5a6aaac510d983bde2cdeb
parent9eeec33415ac04ac78431eae9e902aed385650b6 (diff)
downloadbrdo-e8364f5156decde5b6d5c9f79dbe01910c39a89a.tar.gz
brdo-e8364f5156decde5b6d5c9f79dbe01910c39a89a.tar.bz2
#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.
-rw-r--r--modules/node/node.api.php107
-rw-r--r--modules/node/node.module22
-rw-r--r--modules/node/node.test66
-rw-r--r--modules/node/tests/node_test.module67
4 files changed, 260 insertions, 2 deletions
diff --git a/modules/node/node.api.php b/modules/node/node.api.php
index 6e46b6cb7..ca801d51d 100644
--- a/modules/node/node.api.php
+++ b/modules/node/node.api.php
@@ -109,6 +109,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.
*
* This hook enables modules to inject custom operations into the mass operations
diff --git a/modules/node/node.module b/modules/node/node.module
index 351b754ee..5d1ab920d 100644
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -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);
}
diff --git a/modules/node/node.test b/modules/node/node.test
index 1e6d57e1e..1a77a4588 100644
--- a/modules/node/node.test
+++ b/modules/node/node.test
@@ -685,6 +685,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
*/
class NodeSaveTestCase extends DrupalWebTestCase {
diff --git a/modules/node/tests/node_test.module b/modules/node/tests/node_test.module
index 1ad3227ff..27a303e06 100644
--- a/modules/node/tests/node_test.module
+++ b/modules/node/tests/node_test.module
@@ -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();
+}