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


Recommended