diff options
author | Angie Byron <webchick@24967.no-reply.drupal.org> | 2009-05-27 02:01:54 +0000 |
---|---|---|
committer | Angie Byron <webchick@24967.no-reply.drupal.org> | 2009-05-27 02:01:54 +0000 |
commit | e8364f5156decde5b6d5c9f79dbe01910c39a89a (patch) | |
tree | 8c00563b0557f12cab5a6aaac510d983bde2cdeb /modules | |
parent | 9eeec33415ac04ac78431eae9e902aed385650b6 (diff) | |
download | brdo-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.
Diffstat (limited to 'modules')
-rw-r--r-- | modules/node/node.api.php | 107 | ||||
-rw-r--r-- | modules/node/node.module | 22 | ||||
-rw-r--r-- | modules/node/node.test | 66 | ||||
-rw-r--r-- | modules/node/tests/node_test.module | 67 |
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(); +} |