SlideShare a Scribd company logo
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
What is Node Access?
          Drupal’s system for controlling the
          access to nodes (content) for:
                        • View
                        • Edit
                        • Delete
                        • and now...Create

Sunday, July 22, 2012
Drupal 7 Goodness

          • Create permissions!
          • Alter hooks!
          • ‘Bypass node access’
          • Access control modules
          • Node Access API modules

Sunday, July 22, 2012
Sunday, July 22, 2012
chx   moshe   Crell   Dave    some
                                              Cohen    guy




Sunday, July 22, 2012
Sunday, July 22, 2012
Node Access is multipurpose


        • Filter listing queries.
        • Assert access rights on CRUD.
        • API for managing access rights.



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
With proper access control



                                            Recent
                               Blue
                                            Posts




Sunday, July 22, 2012
Without proper access control




                                 Blue




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
Boolean assertions



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




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));
                    }
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
Drupal 8 Changes

          • Language-sensitive
          • Abstract to all entities
          • Remove hook_node_access()?
          • Better list queries in core?
          • Support AND and OR logic?

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
Key issues

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

More Related Content

KEY
Drupalcamp gent - Node access
PDF
Symfony2 from the Trenches
PPTX
Build your own entity with Drupal
PDF
Doctrine MongoDB Object Document Mapper
PDF
Symfony Day 2010 Doctrine MongoDB ODM
PDF
Zend Framework 1 + Doctrine 2
PDF
ZendCon2010 Doctrine MongoDB ODM
PDF
Command-Oriented Architecture
Drupalcamp gent - Node access
Symfony2 from the Trenches
Build your own entity with Drupal
Doctrine MongoDB Object Document Mapper
Symfony Day 2010 Doctrine MongoDB ODM
Zend Framework 1 + Doctrine 2
ZendCon2010 Doctrine MongoDB ODM
Command-Oriented Architecture

What's hot (20)

PPT
Drupal csu-open atriumname
PPTX
ONE MORE TIME ABOUT CODE STANDARDS AND BEST PRACTICES
PDF
iBATIS
PDF
Top Ten Reasons to Use EntityFieldQuery in Drupal
PDF
Your Entity, Your Code
PDF
Dependency Injection in Laravel
PPTX
Final tagless and cats mtl
PDF
Drupal Entities - Emerging Patterns of Usage
PPTX
Magento Dependency Injection
PDF
jQuery secrets
ODP
Modularized Persistence - B Zsoldos
PDF
"Android Data Binding в массы" Михаил Анохин
PDF
Михаил Анохин "Data binding 2.0"
PDF
Virtual Madness @ Etsy
PPT
Hi5 Opensocial Code Lab Presentation
PDF
SQLAlchemy Seminar
PDF
Mpg Dec07 Gian Lorenzetto
PDF
Your code sucks, let's fix it - DPC UnCon
PDF
Desarrollo de módulos en Drupal e integración con dispositivos móviles
PDF
Object::Franger: Wear a Raincoat in your Code
Drupal csu-open atriumname
ONE MORE TIME ABOUT CODE STANDARDS AND BEST PRACTICES
iBATIS
Top Ten Reasons to Use EntityFieldQuery in Drupal
Your Entity, Your Code
Dependency Injection in Laravel
Final tagless and cats mtl
Drupal Entities - Emerging Patterns of Usage
Magento Dependency Injection
jQuery secrets
Modularized Persistence - B Zsoldos
"Android Data Binding в массы" Михаил Анохин
Михаил Анохин "Data binding 2.0"
Virtual Madness @ Etsy
Hi5 Opensocial Code Lab Presentation
SQLAlchemy Seminar
Mpg Dec07 Gian Lorenzetto
Your code sucks, let's fix it - DPC UnCon
Desarrollo de módulos en Drupal e integración con dispositivos móviles
Object::Franger: Wear a Raincoat in your Code
Ad

Similar to Node Access in Drupal 7 (and Drupal 8) (20)

PDF
Drupal node access system & AUL 7.x.-2.x
PDF
SfCon: Test Driven Development
PPT
Introduction to j query
PDF
jQuery UI Widgets, Drag and Drop, Drupal 7 Javascript
PDF
Como escalar aplicações PHP
PDF
Redis on Rails (RedDotRubyConf 2012)
PDF
Drupal 7: What's In It For You?
PDF
Symfony2 and MongoDB
PDF
Aegir
PDF
jQuery (MeshU)
PDF
Angular.js - JS Camp UKraine 2013
KEY
The Open & Social Web - Kings of Code 2009
PDF
jQuery (BostonPHP)
PDF
Domain Driven Design Javaday Roma2007
PDF
PHP Loves MongoDB - Dublin MUG (by Hannes)
PPT
Headless drupal + react js Oleksandr Linyvyi
PDF
Caching techniques in python, europython2010
PPTX
Mock Objects from Concept to Code
PDF
Architecting large Node.js applications
PPTX
20120722 word press
Drupal node access system & AUL 7.x.-2.x
SfCon: Test Driven Development
Introduction to j query
jQuery UI Widgets, Drag and Drop, Drupal 7 Javascript
Como escalar aplicações PHP
Redis on Rails (RedDotRubyConf 2012)
Drupal 7: What's In It For You?
Symfony2 and MongoDB
Aegir
jQuery (MeshU)
Angular.js - JS Camp UKraine 2013
The Open & Social Web - Kings of Code 2009
jQuery (BostonPHP)
Domain Driven Design Javaday Roma2007
PHP Loves MongoDB - Dublin MUG (by Hannes)
Headless drupal + react js Oleksandr Linyvyi
Caching techniques in python, europython2010
Mock Objects from Concept to Code
Architecting large Node.js applications
20120722 word press
Ad

More from nyccamp (19)

PDF
Drupal As A Jigsaw
PDF
A/B Testing and Optimizely Module
PPTX
Behat - human-readable automated testing
PPT
ALL YOUR BASE (THEMES) ARE BELONG TO US
PDF
Drupal Commerce - The Product vs Display Conundrum and How to Explain it to a...
PDF
Promotions Vouchers and Offers in Drupal Commerce
PDF
Workbench: Managing Content Management
PPT
Deployment Strategies: Managing Code, Content, and Configurations
PDF
Drupal Aware Design: Good Techniques for Better Themes
PDF
Drupal and Higher Education
PDF
A New Theme Layer for Drupal 8
PDF
Mobile and Responsive Design with Sass
PDF
Drupal and Apache Solr Search Go Together Like Pizza and Beer for Your Site
PDF
Building Social Networks
PPT
The State of Drupal 8
PDF
Building Social Networks
PDF
Move Into Drupal Using The Migrate Module
PDF
Hack Into Drupal Sites (or, How to Secure Your Drupal Site)
PDF
Drulenium - Testing Made Easy
Drupal As A Jigsaw
A/B Testing and Optimizely Module
Behat - human-readable automated testing
ALL YOUR BASE (THEMES) ARE BELONG TO US
Drupal Commerce - The Product vs Display Conundrum and How to Explain it to a...
Promotions Vouchers and Offers in Drupal Commerce
Workbench: Managing Content Management
Deployment Strategies: Managing Code, Content, and Configurations
Drupal Aware Design: Good Techniques for Better Themes
Drupal and Higher Education
A New Theme Layer for Drupal 8
Mobile and Responsive Design with Sass
Drupal and Apache Solr Search Go Together Like Pizza and Beer for Your Site
Building Social Networks
The State of Drupal 8
Building Social Networks
Move Into Drupal Using The Migrate Module
Hack Into Drupal Sites (or, How to Secure Your Drupal Site)
Drulenium - Testing Made Easy

Recently uploaded (20)

PDF
Empathic Computing: Creating Shared Understanding
PDF
Encapsulation_ Review paper, used for researhc scholars
PPTX
sap open course for s4hana steps from ECC to s4
PDF
A comparative analysis of optical character recognition models for extracting...
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PPTX
Big Data Technologies - Introduction.pptx
PPT
Teaching material agriculture food technology
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PDF
gpt5_lecture_notes_comprehensive_20250812015547.pdf
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Spectral efficient network and resource selection model in 5G networks
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
Electronic commerce courselecture one. Pdf
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
Empathic Computing: Creating Shared Understanding
Encapsulation_ Review paper, used for researhc scholars
sap open course for s4hana steps from ECC to s4
A comparative analysis of optical character recognition models for extracting...
The Rise and Fall of 3GPP – Time for a Sabbatical?
Big Data Technologies - Introduction.pptx
Teaching material agriculture food technology
“AI and Expert System Decision Support & Business Intelligence Systems”
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
The AUB Centre for AI in Media Proposal.docx
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
MIND Revenue Release Quarter 2 2025 Press Release
gpt5_lecture_notes_comprehensive_20250812015547.pdf
Building Integrated photovoltaic BIPV_UPV.pdf
Spectral efficient network and resource selection model in 5G networks
Advanced methodologies resolving dimensionality complications for autism neur...
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Electronic commerce courselecture one. Pdf
Mobile App Security Testing_ A Comprehensive Guide.pdf

Node Access in Drupal 7 (and Drupal 8)

  • 1. Node Access in Drupal 7 (and Drupal 8) Ken Rickard NYCamp 2012 Sunday, July 22, 2012
  • 2. • 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
  • 3. • Ken Rickard • Drupal since 2004 • Domain Access • rickard@Palantir.net • @agentrickard Sunday, July 22, 2012
  • 5. Drupal 7 Module Development Sunday, July 22, 2012
  • 7. What is Node Access? Sunday, July 22, 2012
  • 9. • Powerful • Unintelligible • Unpredictable • Multi-dimensional Sunday, July 22, 2012
  • 10. What is Node Access? Drupal’s system for controlling the access to nodes (content) for: • View • Edit • Delete • and now...Create Sunday, July 22, 2012
  • 11. Drupal 7 Goodness • Create permissions! • Alter hooks! • ‘Bypass node access’ • Access control modules • Node Access API modules Sunday, July 22, 2012
  • 13. chx moshe Crell Dave some Cohen guy Sunday, July 22, 2012
  • 15. Node Access is multipurpose • Filter listing queries. • Assert access rights on CRUD. • API for managing access rights. Sunday, July 22, 2012
  • 17. • Drupal 6: hook_db_rewrite_sql() • Drupal 7: ‘node access’ query flag. Sunday, July 22, 2012
  • 18. 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
  • 19. 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
  • 20. Failure to tag your query is a security violation. Sunday, July 22, 2012
  • 25. Blue Red Green Grey Our Clients Sunday, July 22, 2012
  • 26. Blue Red Green Grey Organic Groups Sunday, July 22, 2012
  • 27. With proper access control Recent Blue Posts Sunday, July 22, 2012
  • 28. Without proper access control Blue Sunday, July 22, 2012
  • 30. • 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
  • 31. Boolean assertions if (user_access(‘administer nodes’)) { return t(“I’ll be back.”); } Sunday, July 22, 2012
  • 32. Conditional access check hook_node_grants($account, $op) hook_node_access($node, $op, $account) Sunday, July 22, 2012
  • 34. • Those two functions look similar. • But they aren’t. Sunday, July 22, 2012
  • 35. 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
  • 36. // $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
  • 37. // 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
  • 38. 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
  • 39. 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
  • 41. 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
  • 42. Access control modules • Act on Create, View, Update & Delete. • No tables*. • No queries*. • Just business logic. • DENY, ALLOW, IGNORE Sunday, July 22, 2012
  • 43. The operation condition matters! ‘create’ ‘delete’ ‘update’ ‘view’ Sunday, July 22, 2012
  • 45. /** * 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
  • 47. • No more hook_menu_alter(). • Explicit DENY grants. • Explicit ALLOW grants. • Apply to all node types! • Apply to node creation! Sunday, July 22, 2012
  • 49. • Individual nodes / actions only. • Running lookup queries per node can be expensive. • Can override other modules. • Cannot generate accurate lists. Sunday, July 22, 2012
  • 51. 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
  • 53. hook_node_access_records() Data Entry node_save() node_access_acquire_grants() {node_access} Sunday, July 22, 2012
  • 54. 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
  • 55. hook_node_grants() Inbound Request $user node_access_grants() Page / Request Render Sunday, July 22, 2012
  • 56. 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
  • 57. • 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
  • 58. • Two-part system! • One query does not cover all cases! ‘create’ ‘delete’ ‘update’ ‘view’ Sunday, July 22, 2012
  • 60. Rebuilding the {node_access} table Sunday, July 22, 2012
  • 61. • Make sure you hook_node_load() your data. • node_load() must match node_save() • Other modules may depend on you! Sunday, July 22, 2012
  • 62. Devel Node Access • Debugging tools for developers. • And site administrators! Sunday, July 22, 2012
  • 63. 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)); } Sunday, July 22, 2012
  • 65. Add debugging to your module, too Sunday, July 22, 2012
  • 67. 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
  • 68. 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
  • 69. hook_node_grants_alter() • Alter the user’s grants at request time. • Quick and easy! Sunday, July 22, 2012
  • 70. 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
  • 71. 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
  • 73. It’s not a tumor. Sunday, July 22, 2012
  • 74. Drupal 8 Changes • Language-sensitive • Abstract to all entities • Remove hook_node_access()? • Better list queries in core? • Support AND and OR logic? Sunday, July 22, 2012
  • 75. 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
  • 76. Key issues • 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 Sunday, July 22, 2012
  • 77. Let’s get to work Sunday, July 22, 2012
  • 78. • THANK YOU! • Ken Rickard • rickard@Palantir.net • @agentrickard Sunday, July 22, 2012