node access in drupal 7 (and drupal 8)

Post on 17-May-2015

8.465 Views

Category:

Technology

8 Downloads

Preview:

Click to see full reader

DESCRIPTION

This talk will look at the features and changes in the Node Access system for Drupal 7.Out of the box, Drupal is a great system for creating and managing content. However, there are cases where your needs require additional requirements for which users can create, view, edit and delete content. To solve this problem, Drupal provides its Node Access system.Node Access provides an API for determining the grants, or permissions, that a user has for each node. By understanding how these grants work, a module developer can create and enforce complex access rules.We will cover some (or all) of the following topics.- Node Access compared to user_access() and other permission checks.- How Drupal grants node permissions.- The node_access() function.- hook_node_access() compared to {node_access}.- Controlling permission to create content.- Using hook_node_access().- When to write a Node Access module.- The {node_access} table and its role.- Defining your moduleâs access rules.- Using hook_node_access_records().- Using hook_node_grants().- Rebuilding the {node_access} table.- Modifying the behavior of other modules.- Using hook_node_access_records_alter().- Using hook_node_grants_alter().- Testing and debugging you module.- Using Devel Node Access- Roadmap for Drupal 8Ken Rickard is the maintainer of the Domain Access module and wrote several of the patches for Node Access in Drupal 7.

TRANSCRIPT

Node Access in Drupal 7(and Drupal 8)

Ken Rickard

NYCamp 2012

Sunday, July 22, 2012

• Who the heck are you?• What is Node Access?• What changed in Drupal 7?• How do I....?• What’s up for Drupal 8?

Sunday, July 22, 2012

• Ken Rickard

• Drupal since 2004

• Domain Access

• rickard@Palantir.net

• @agentrickard

Sunday, July 22, 2012

Sunday, July 22, 2012

Drupal 7 Module Development

Sunday, July 22, 2012

AaronWinborn.com

Sunday, July 22, 2012

What is Node Access?

Sunday, July 22, 2012

Sunday, July 22, 2012

• Powerful

• Unintelligible

• Unpredictable

• Multi-dimensional

Sunday, July 22, 2012

Drupal’s system for controlling the access to nodes (content) for:

• View

• Edit

• Delete

• and now...Create

What is Node Access?

Sunday, July 22, 2012

• Create permissions!

• Alter hooks!

• ‘Bypass node access’

• Access control modules

• Node Access API modules

Drupal 7 Goodness

Sunday, July 22, 2012

Sunday, July 22, 2012

chx moshe Crell DaveCohen

someguy

Sunday, July 22, 2012

Sunday, July 22, 2012

• Filter listing queries.

• Assert access rights on CRUD.

• API for managing access rights.

Node Access is multipurpose

Sunday, July 22, 2012

Sunday, July 22, 2012

• Drupal 6: hook_db_rewrite_sql()

• Drupal 7: ‘node access’ query flag.

Sunday, July 22, 2012

Filter queries in Drupal 6

$result = db_query(db_rewrite_sql( “SELECT nid FROM {node} WHERE status = 1 AND promote = 1 ORDER BY sticky DESC, created DESC” ));

Sunday, July 22, 2012

Filter queries in Drupal 7

$result = db_select('node', 'n') ->fields('n', array('nid')) ->condition('promote', 1) ->condition('status', 1) ->orderBy('sticky', 'DESC') ->orderBy('created', 'DESC') ->addTag('node_access') ->execute();

Sunday, July 22, 2012

Failure to tag your query is a security

violation.

Sunday, July 22, 2012

Sunday, July 22, 2012

Why?

Sunday, July 22, 2012

Sunday, July 22, 2012

Sunday, July 22, 2012

Blue Red Green Grey

Our Clients

Sunday, July 22, 2012

Blue Red Green Grey

Organic Groups

Sunday, July 22, 2012

Blue RecentPosts

With proper access control

Sunday, July 22, 2012

Blue

Without proper access control

Sunday, July 22, 2012

Sunday, July 22, 2012

• Node Access is not user_access().

• Node Access is an API for content CRUD permissions.

• It extends Drupal’s core permissions system.

• It is contextual.

Sunday, July 22, 2012

if (user_access(‘administer nodes’)) { return t(“I’ll be back.”);}

Boolean assertions

Sunday, July 22, 2012

Conditional access check

hook_node_grants($account, $op)

hook_node_access($node, $op, $account)

Sunday, July 22, 2012

Sunday, July 22, 2012

• Those two functions look similar.

• But they aren’t.

Sunday, July 22, 2012

node_access() Walk-through

function node_access($op, $node, $account = NULL) { $rights = &drupal_static(__FUNCTION__, array());

if (!$node || !in_array($op, array('view', 'update', 'delete', 'create'), TRUE)) { // If there was no node to check against, or the $op was not one of the // supported ones, we return access denied. return FALSE; } // If no user object is supplied, the access check is for the current user. if (empty($account)) { $account = $GLOBALS['user']; }

Sunday, July 22, 2012

// $node may be either an object or a node type. Since node types cannot be // an integer, use either nid or type as the static cache id.

$cid = is_object($node) ? $node->nid : $node;

// If we've already checked access for this node, user and op, return from // cache. if (isset($rights[$account->uid][$cid][$op])) { return $rights[$account->uid][$cid][$op]; }

if (user_access('bypass node access', $account)) { $rights[$account->uid][$cid][$op] = TRUE; return TRUE; } if (!user_access('access content', $account)) { $rights[$account->uid][$cid][$op] = FALSE; return FALSE; }

Sunday, July 22, 2012

// We grant access to the node if both of the following conditions are met: // - No modules say to deny access. // - At least one module says to grant access. // If no module specified either allow or deny, we fall back to the // node_access table. $access = module_invoke_all('node_access', $node, $op, $account); if (in_array(NODE_ACCESS_DENY, $access, TRUE)) { $rights[$account->uid][$cid][$op] = FALSE; return FALSE; } elseif (in_array(NODE_ACCESS_ALLOW, $access, TRUE)) { $rights[$account->uid][$cid][$op] = TRUE; return TRUE; }

// Check if authors can view their own unpublished nodes. if ($op == 'view' && !$node->status && user_access('view own unpublished content', $account) && $account->uid == $node->uid && $account->uid != 0) { $rights[$account->uid][$cid][$op] = TRUE; return TRUE; }

Sunday, July 22, 2012

foreach (node_access_grants($op, $account) as $realm => $gids) { foreach ($gids as $gid) { $grants->condition(db_and() ->condition('gid', $gid) ->condition('realm', $realm) ); } } if (count($grants) > 0) { $query->condition($grants); } $result = (bool) $query ->execute() ->fetchField(); $rights[$account->uid][$cid][$op] = $result; return $result; }

Sunday, July 22, 2012

elseif (is_object($node) && $op == 'view' && $node->status) { // If no modules implement hook_node_grants(), the default behavior is to // allow all users to view published nodes, so reflect that here. $rights[$account->uid][$cid][$op] = TRUE; return TRUE; } }

return FALSE;}

Sunday, July 22, 2012

Sunday, July 22, 2012

hook_node_access($node, $op, $account)

• Replaces hook_access().

• Can be run by any module!

• Can return TRUE, FALSE or NULL.

• Used for access to individual nodes.

Sunday, July 22, 2012

Access control modules

• Act on Create, View, Update & Delete.

• No tables*.

• No queries*.

• Just business logic.

• DENY, ALLOW, IGNORE

Sunday, July 22, 2012

The operation condition matters!

‘create’‘delete’‘update’‘view’

Sunday, July 22, 2012

Sunday, July 22, 2012

/** * Implement hook_node_access(). * * Only allow posts by users more than two days old. */function delay_node_access($node, $op, $account) { if ($op != 'create') { return NODE_ACCESS_IGNORE; } if (empty($account->created) || $account->created > (REQUEST_TIME - (48*3600))) { return NODE_ACCESS_DENY; } return NODE_ACCESS_IGNORE;}

Sunday, July 22, 2012

Hooray!

Sunday, July 22, 2012

• No more hook_menu_alter().

• Explicit DENY grants.

• Explicit ALLOW grants.

• Apply to all node types!

• Apply to node creation!

Sunday, July 22, 2012

Boo!

Sunday, July 22, 2012

• Individual nodes / actions only.

• Running lookup queries per node can be expensive.

• Can override other modules.

• Cannot generate accurate lists.

Sunday, July 22, 2012

Sunday, July 22, 2012

NODE ACCESS API

• Uses {node_access} for list queries.

• Creates JOINs to return lists of nodes.

• Does not act on ‘create’ operation.

• Requires numeric keys.

Sunday, July 22, 2012

The {node_access} table.

Sunday, July 22, 2012

hook_node_access_records()

Data Entry

node_save()

node_access_acquire_grants()

{node_access}

Sunday, July 22, 2012

function example_node_access_records($node) { $grants[] = array( 'realm' => 'example_author', 'gid' => $node->uid, 'grant_view' => 1, 'grant_update' => 1, 'grant_delete' => 1, 'priority' => 0, ); return $grants; }

Sunday, July 22, 2012

hook_node_grants()

Inbound Request

$user

node_access_grants()

Page / Request Render

Sunday, July 22, 2012

function example_node_grants($account, $op) { if (user_access('access private content', $account)) { $grants['example'] = array(1); } $grants['example_owner'] = array($account->uid); return $grants;}

Sunday, July 22, 2012

• Map the user’s grants to the stored grants.

• JOIN {node_access} to the query.

• Return the node or not.

• OR based access logic.

Sunday, July 22, 2012

• Two-part system!

• One query does not cover all cases!

‘create’‘delete’‘update’‘view’

Sunday, July 22, 2012

Sunday, July 22, 2012

Rebuilding the {node_access} table

Sunday, July 22, 2012

• Make sure you hook_node_load() your data.

• node_load() must match node_save()

• Other modules may depend on you!

Sunday, July 22, 2012

Devel Node Access

• Debugging tools for developers.

• And site administrators!

Sunday, July 22, 2012

hook_node_access_explain()/** * Implements hook_node_access_explain for devel.module */function domain_node_access_explain($row) { $_domain = domain_get_domain(); $active = $_domain['subdomain']; $domain = domain_lookup($row->gid); $return = t('Domain Access') . ' -- '; switch ($row->realm) { case 'domain_all': if (domain_grant_all() == TRUE) { $return .= t('True: Allows content from all domains to be shown.'); } else { $return .= t('False: Only allows content from the active domain (%domain) or from all affiliates.', array('%domain' => $active)); } break; case 'domain_site': $return .= t('Viewable on all affiliate sites.'); break; case 'domain_id': $return .= t('Viewable on %domain<br />%domain privileged editors may edit and delete', array('%domain' => $domain['subdomain'])); break; default: // This is not our grant, do not return anything. $return = NULL; break; } return $return;}

Sunday, July 22, 2012

Sunday, July 22, 2012

Add debugging to your module, too

Sunday, July 22, 2012

Sunday, July 22, 2012

hook_node_access_records_alter()

• Change storage rules before they are written to the database!

• Remember to alter node storage as needed, too!*

• * Runs after node_save() :-(

Sunday, July 22, 2012

function hook_node_access_records_alter(&$grants, $node) { // Our module allows editors to mark specific articles // with the 'is_preview' field.

if ($node->is_preview) { // Our module grants are set in $grants['example']. $temp = $grants['example']; // Now remove all module grants but our own. $grants = array('example' => $temp); }

}

Sunday, July 22, 2012

hook_node_grants_alter()

• Alter the user’s grants at request time.

• Quick and easy!

Sunday, July 22, 2012

function hook_node_grants_alter(&$grants, $account, $op) {

// Get our list of banned roles. $restricted = variable_get('example_restricted_roles', array());

if ($op != 'view' && !empty($restricted)) { foreach ($restricted as $role_id) { if (isset($user->roles[$role_id])) { $grants = array(); } } }

}

Sunday, July 22, 2012

hook_query_alter()

/** * Implements hook_query_TAG_alter(). * * If enabled, force admins to use Domain Access rules. */function domain_query_node_access_alter($query) { $admin_force = variable_get('domain_force_admin', FALSE); // In any of the following cases, do not enforce any rules. if (empty($admin_force) || !user_access('bypass node access') || domain_grant_all()) { return; } domain_alter_node_query($query);}

Sunday, July 22, 2012

Sunday, July 22, 2012

It’s not a tumor.

Sunday, July 22, 2012

• Language-sensitive

• Abstract to all entities

• Remove hook_node_access()?

• Better list queries in core?

• Support AND and OR logic?

Drupal 8 Changes

Sunday, July 22, 2012

function node_access($op, $node, $account = NULL, $langcode = NULL) { $rights = &drupal_static(__FUNCTION__, array()); ... // If we've already checked access for this node, user and op, return from // cache. if (isset($rights[$account->uid][$cid][$langcode][$op])) { return $rights[$account->uid][$cid][$langcode][$op]; }

if (user_access('bypass node access', $account)) { $rights[$account->uid][$cid][$langcode][$op] = TRUE; return TRUE; } if (!user_access('access content', $account)) { $rights[$account->uid][$cid][$langcode][$op] = FALSE; return FALSE; }

Sunday, July 22, 2012

• Make a general access API

• http://drupal.org/node/777578

• Make node_access() language aware

• http://drupal.org/node/1658814

• Query madness

• http://drupal.org/node/1349080

Key issues

Sunday, July 22, 2012

Let’s get to work

Sunday, July 22, 2012

• THANK YOU!

• Ken Rickard

• rickard@Palantir.net

• @agentrickard

Sunday, July 22, 2012

top related