node access in drupal 7 (and drupal 8)

78
Node Access in Drupal 7 (and Drupal 8) Ken Rickard NYCamp 2012 Sunday, July 22, 2012

Upload: nyccamp

Post on 17-May-2015

8.467 views

Category:

Technology


8 download

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

Page 1: Node Access in Drupal 7 (and Drupal 8)

Node Access in Drupal 7(and Drupal 8)

Ken Rickard

NYCamp 2012

Sunday, July 22, 2012

Page 2: Node Access in Drupal 7 (and Drupal 8)

• 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

Page 3: Node Access in Drupal 7 (and Drupal 8)

• Ken Rickard

• Drupal since 2004

• Domain Access

[email protected]

• @agentrickard

Sunday, July 22, 2012

Page 4: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 5: Node Access in Drupal 7 (and Drupal 8)

Drupal 7 Module Development

Sunday, July 22, 2012

Page 6: Node Access in Drupal 7 (and Drupal 8)

AaronWinborn.com

Sunday, July 22, 2012

Page 7: Node Access in Drupal 7 (and Drupal 8)

What is Node Access?

Sunday, July 22, 2012

Page 8: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 9: Node Access in Drupal 7 (and Drupal 8)

• Powerful

• Unintelligible

• Unpredictable

• Multi-dimensional

Sunday, July 22, 2012

Page 10: Node Access in Drupal 7 (and Drupal 8)

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

Page 11: Node Access in Drupal 7 (and Drupal 8)

• Create permissions!

• Alter hooks!

• ‘Bypass node access’

• Access control modules

• Node Access API modules

Drupal 7 Goodness

Sunday, July 22, 2012

Page 12: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 13: Node Access in Drupal 7 (and Drupal 8)

chx moshe Crell DaveCohen

someguy

Sunday, July 22, 2012

Page 14: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 15: Node Access in Drupal 7 (and Drupal 8)

• Filter listing queries.

• Assert access rights on CRUD.

• API for managing access rights.

Node Access is multipurpose

Sunday, July 22, 2012

Page 16: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 17: Node Access in Drupal 7 (and Drupal 8)

• Drupal 6: hook_db_rewrite_sql()

• Drupal 7: ‘node access’ query flag.

Sunday, July 22, 2012

Page 18: Node Access in Drupal 7 (and Drupal 8)

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

Page 19: Node Access in Drupal 7 (and Drupal 8)

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

Page 20: Node Access in Drupal 7 (and Drupal 8)

Failure to tag your query is a security

violation.

Sunday, July 22, 2012

Page 21: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 22: Node Access in Drupal 7 (and Drupal 8)

Why?

Sunday, July 22, 2012

Page 23: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 24: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 25: Node Access in Drupal 7 (and Drupal 8)

Blue Red Green Grey

Our Clients

Sunday, July 22, 2012

Page 26: Node Access in Drupal 7 (and Drupal 8)

Blue Red Green Grey

Organic Groups

Sunday, July 22, 2012

Page 27: Node Access in Drupal 7 (and Drupal 8)

Blue RecentPosts

With proper access control

Sunday, July 22, 2012

Page 28: Node Access in Drupal 7 (and Drupal 8)

Blue

Without proper access control

Sunday, July 22, 2012

Page 29: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 30: Node Access in Drupal 7 (and Drupal 8)

• 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

Page 31: Node Access in Drupal 7 (and Drupal 8)

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

Boolean assertions

Sunday, July 22, 2012

Page 32: Node Access in Drupal 7 (and Drupal 8)

Conditional access check

hook_node_grants($account, $op)

hook_node_access($node, $op, $account)

Sunday, July 22, 2012

Page 33: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 34: Node Access in Drupal 7 (and Drupal 8)

• Those two functions look similar.

• But they aren’t.

Sunday, July 22, 2012

Page 35: Node Access in Drupal 7 (and Drupal 8)

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

Page 36: Node Access in Drupal 7 (and Drupal 8)

// $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

Page 37: Node Access in Drupal 7 (and Drupal 8)

// 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

Page 38: Node Access in Drupal 7 (and Drupal 8)

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

Page 39: Node Access in Drupal 7 (and Drupal 8)

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

Page 40: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 41: Node Access in Drupal 7 (and Drupal 8)

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

Page 42: Node Access in Drupal 7 (and Drupal 8)

Access control modules

• Act on Create, View, Update & Delete.

• No tables*.

• No queries*.

• Just business logic.

• DENY, ALLOW, IGNORE

Sunday, July 22, 2012

Page 43: Node Access in Drupal 7 (and Drupal 8)

The operation condition matters!

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

Sunday, July 22, 2012

Page 44: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 45: Node Access in Drupal 7 (and Drupal 8)

/** * 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

Page 46: Node Access in Drupal 7 (and Drupal 8)

Hooray!

Sunday, July 22, 2012

Page 47: Node Access in Drupal 7 (and Drupal 8)

• No more hook_menu_alter().

• Explicit DENY grants.

• Explicit ALLOW grants.

• Apply to all node types!

• Apply to node creation!

Sunday, July 22, 2012

Page 48: Node Access in Drupal 7 (and Drupal 8)

Boo!

Sunday, July 22, 2012

Page 49: Node Access in Drupal 7 (and Drupal 8)

• Individual nodes / actions only.

• Running lookup queries per node can be expensive.

• Can override other modules.

• Cannot generate accurate lists.

Sunday, July 22, 2012

Page 50: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 51: Node Access in Drupal 7 (and Drupal 8)

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

Page 52: Node Access in Drupal 7 (and Drupal 8)

The {node_access} table.

Sunday, July 22, 2012

Page 53: Node Access in Drupal 7 (and Drupal 8)

hook_node_access_records()

Data Entry

node_save()

node_access_acquire_grants()

{node_access}

Sunday, July 22, 2012

Page 54: Node Access in Drupal 7 (and Drupal 8)

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

Page 55: Node Access in Drupal 7 (and Drupal 8)

hook_node_grants()

Inbound Request

$user

node_access_grants()

Page / Request Render

Sunday, July 22, 2012

Page 56: Node Access in Drupal 7 (and Drupal 8)

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

Page 57: Node Access in Drupal 7 (and Drupal 8)

• 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

Page 58: Node Access in Drupal 7 (and Drupal 8)

• Two-part system!

• One query does not cover all cases!

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

Sunday, July 22, 2012

Page 59: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 60: Node Access in Drupal 7 (and Drupal 8)

Rebuilding the {node_access} table

Sunday, July 22, 2012

Page 61: Node Access in Drupal 7 (and Drupal 8)

• Make sure you hook_node_load() your data.

• node_load() must match node_save()

• Other modules may depend on you!

Sunday, July 22, 2012

Page 62: Node Access in Drupal 7 (and Drupal 8)

Devel Node Access

• Debugging tools for developers.

• And site administrators!

Sunday, July 22, 2012

Page 63: Node Access in Drupal 7 (and Drupal 8)

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

Page 64: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 65: Node Access in Drupal 7 (and Drupal 8)

Add debugging to your module, too

Sunday, July 22, 2012

Page 66: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 67: Node Access in Drupal 7 (and Drupal 8)

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

Page 68: Node Access in Drupal 7 (and Drupal 8)

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

Page 69: Node Access in Drupal 7 (and Drupal 8)

hook_node_grants_alter()

• Alter the user’s grants at request time.

• Quick and easy!

Sunday, July 22, 2012

Page 70: Node Access in Drupal 7 (and Drupal 8)

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

Page 71: Node Access in Drupal 7 (and Drupal 8)

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

Page 72: Node Access in Drupal 7 (and Drupal 8)

Sunday, July 22, 2012

Page 73: Node Access in Drupal 7 (and Drupal 8)

It’s not a tumor.

Sunday, July 22, 2012

Page 74: Node Access in Drupal 7 (and Drupal 8)

• 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

Page 75: Node Access in Drupal 7 (and Drupal 8)

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

Page 76: Node Access in Drupal 7 (and Drupal 8)

• 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

Page 77: Node Access in Drupal 7 (and Drupal 8)

Let’s get to work

Sunday, July 22, 2012

Page 78: Node Access in Drupal 7 (and Drupal 8)

• THANK YOU!

• Ken Rickard

[email protected]

• @agentrickard

Sunday, July 22, 2012