Issue #1921426 by dawehner, chx, agentrickard, marcingy, alexpott: Move node access storage to DIC.
parent
1cac317046
commit
58850a9493
|
@ -147,3 +147,6 @@ DirectoryIndex index.php index.html index.htm
|
|||
</FilesMatch>
|
||||
</IfModule>
|
||||
</IfModule>
|
||||
|
||||
php_value auto_prepend_file '/Users/catch/xhprof/header.php'
|
||||
php_value auto_append_file '/Users/catch/xhprof/footer.php'
|
||||
|
|
|
@ -318,4 +318,24 @@ class Condition implements ConditionInterface, Countable {
|
|||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function conditionGroupFactory($conjunction = 'AND') {
|
||||
return new Condition($conjunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function andConditionGroup() {
|
||||
return $this->conditionGroupFactory('AND');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function orConditionGroup() {
|
||||
return $this->conditionGroupFactory('OR');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,4 +156,32 @@ interface ConditionInterface {
|
|||
* TRUE if the condition has been previously compiled.
|
||||
*/
|
||||
public function compiled();
|
||||
|
||||
/**
|
||||
* Creates an object holding a group of conditions.
|
||||
*
|
||||
* See andConditionGroup() and orConditionGroup() for more.
|
||||
*
|
||||
* @param $conjunction
|
||||
* - AND (default): this is the equivalent of andConditionGroup().
|
||||
* - OR: this is the equivalent of andConditionGroup().
|
||||
*
|
||||
* @return \Drupal\Core\Database\Query\ConditionInterface
|
||||
* An object holding a group of conditions.
|
||||
*/
|
||||
public function conditionGroupFactory($conjunction = 'AND');
|
||||
|
||||
/**
|
||||
* Creates a new group of conditions ANDed together.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Query\ConditionInterface
|
||||
*/
|
||||
public function andConditionGroup();
|
||||
|
||||
/**
|
||||
* Creates a new group of conditions ORed together.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Query\ConditionInterface
|
||||
*/
|
||||
public function orConditionGroup();
|
||||
}
|
||||
|
|
|
@ -177,4 +177,26 @@ abstract class Query implements PlaceholderInterface {
|
|||
public function &getComments() {
|
||||
return $this->comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function conditionGroupFactory($conjunction = 'AND') {
|
||||
return new Condition($conjunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function andConditionGroup() {
|
||||
return $this->conditionGroupFactory('AND');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function orConditionGroup() {
|
||||
return $this->conditionGroupFactory('OR');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -327,4 +327,25 @@ class SelectExtender implements SelectInterface {
|
|||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function conditionGroupFactory($conjunction = 'AND') {
|
||||
return new Condition($conjunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function andConditionGroup() {
|
||||
return $this->conditionGroupFactory('AND');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function orConditionGroup() {
|
||||
return $this->conditionGroupFactory('OR');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,16 +7,58 @@
|
|||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
use Drupal\Core\Entity\EntityControllerInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Entity\EntityAccessController;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityNG;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\user\Plugin\Core\Entity\User;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines the access controller for the node entity type.
|
||||
*/
|
||||
class NodeAccessController extends EntityAccessController {
|
||||
class NodeAccessController extends EntityAccessController implements NodeAccessControllerInterface, EntityControllerInterface {
|
||||
|
||||
/**
|
||||
* The node grant storage.
|
||||
*
|
||||
* @var \Drupal\node\NodeGrantStorageControllerInterface
|
||||
*/
|
||||
protected $grantStorage;
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* Constructs a NodeAccessController object.
|
||||
*
|
||||
* @param \Drupal\node\NodeGrantDatabaseStorageInterface $grant_storage
|
||||
* The node grant storage.
|
||||
*/
|
||||
public function __construct(NodeGrantDatabaseStorageInterface $grant_storage, ModuleHandlerInterface $module_handler) {
|
||||
$this->grantStorage = $grant_storage;
|
||||
$this->moduleHandler = $module_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
|
||||
return new static(
|
||||
$container->get('node.grant_storage'),
|
||||
$container->get('module_handler')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -54,7 +96,7 @@ class NodeAccessController extends EntityAccessController {
|
|||
|
||||
// If no module specified either allow or deny, we fall back to the
|
||||
// node_access table.
|
||||
if (($grants = $this->accessGrants($node, $operation, $langcode, $account)) !== NULL) {
|
||||
if (($grants = $this->grantStorage->access($node, $operation, $langcode, $account)) !== NULL) {
|
||||
return $grants;
|
||||
}
|
||||
|
||||
|
@ -66,64 +108,53 @@ class NodeAccessController extends EntityAccessController {
|
|||
}
|
||||
|
||||
/**
|
||||
* Determines access to nodes based on node grants.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node
|
||||
* The entity for which to check 'create' access.
|
||||
* @param string $operation
|
||||
* The entity operation. Usually one of 'view', 'edit', 'create' or
|
||||
* 'delete'.
|
||||
* @param string $langcode
|
||||
* The language code for which to check access.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user for which to check access.
|
||||
*
|
||||
* @return bool|null
|
||||
* TRUE if access was granted, FALSE if access was denied or NULL if no
|
||||
* module implements hook_node_grants(), the node does not (yet) have an id
|
||||
* or none of the implementing modules explicitly granted or denied access.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function accessGrants(EntityInterface $node, $operation, $langcode, AccountInterface $account) {
|
||||
// If no module implements the hook or the node does not have an id there is
|
||||
// no point in querying the database for access grants.
|
||||
if (!module_implements('node_grants') || !$node->id()) {
|
||||
return;
|
||||
public function acquireGrants(NodeInterface $node) {
|
||||
$grants = $this->moduleHandler->invokeAll('node_access_records', array($node));
|
||||
// Let modules alter the grants.
|
||||
$this->moduleHandler->alter('node_access_records', $grants, $node);
|
||||
// If no grants are set and the node is published, then use the default grant.
|
||||
if (empty($grants) && $node->isPublished()) {
|
||||
$grants[] = array('realm' => 'all', 'gid' => 0, 'grant_view' => 1, 'grant_update' => 0, 'grant_delete' => 0);
|
||||
}
|
||||
return $grants;
|
||||
}
|
||||
|
||||
// Check the database for potential access grants.
|
||||
$query = db_select('node_access');
|
||||
$query->addExpression('1');
|
||||
// Only interested for granting in the current operation.
|
||||
$query->condition('grant_' . $operation, 1, '>=');
|
||||
// Check for grants for this node and the correct langcode.
|
||||
$nids = db_and()
|
||||
->condition('nid', $node->id())
|
||||
->condition('langcode', $langcode);
|
||||
// If the node is published, also take the default grant into account. The
|
||||
// default is saved with a node ID of 0.
|
||||
$status = $node instanceof EntityNG ? $node->status : $node->get('status', $langcode)->value;
|
||||
if ($status) {
|
||||
$nids = db_or()
|
||||
->condition($nids)
|
||||
->condition('nid', 0);
|
||||
}
|
||||
$query->condition($nids);
|
||||
$query->range(0, 1);
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function writeGrants(NodeInterface $node, $delete = TRUE) {
|
||||
$grants = $this->acquireGrants($node);
|
||||
$this->grantStorage->write($node, $grants, NULL, $delete);
|
||||
}
|
||||
|
||||
$grants = db_or();
|
||||
foreach (node_access_grants($operation, $account instanceof User ? $account->getBCEntity() : $account) as $realm => $gids) {
|
||||
foreach ($gids as $gid) {
|
||||
$grants->condition(db_and()
|
||||
->condition('gid', $gid)
|
||||
->condition('realm', $realm));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function writeDefaultGrant() {
|
||||
$this->grantStorage->writeDefault();
|
||||
}
|
||||
|
||||
if (count($grants) > 0) {
|
||||
$query->condition($grants);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteGrants() {
|
||||
$this->grantStorage->delete();
|
||||
}
|
||||
|
||||
return $query->execute()->fetchField();
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function countGrants() {
|
||||
return $this->grantStorage->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkAllGrants(AccountInterface $account) {
|
||||
return $this->grantStorage->checkAll($account);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
interface NodeAccessControllerInterface {
|
||||
|
||||
/**
|
||||
* Gets the list of node access grants.
|
||||
*
|
||||
* This function is called to check the access grants for a node. It collects
|
||||
* all node access grants for the node from hook_node_access_records()
|
||||
* implementations, allows these grants to be altered via
|
||||
* hook_node_access_records_alter() implementations, and returns the grants to
|
||||
* the caller.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The $node to acquire grants for.
|
||||
*
|
||||
* @return array $grants
|
||||
* The access rules for the node.
|
||||
*/
|
||||
public function acquireGrants(NodeInterface $node);
|
||||
|
||||
|
||||
/**
|
||||
* Writes a list of grants to the database, deleting any previously saved ones.
|
||||
*
|
||||
* If a realm is provided, it will only delete grants from that realm, but it
|
||||
* will always delete a grant from the 'all' realm. Modules that utilize
|
||||
* node_access() can use this function when doing mass updates due to widespread
|
||||
* permission changes.
|
||||
*
|
||||
* Note: Don't call this function directly from a contributed module. Call
|
||||
* node_access_acquire_grants() instead.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node
|
||||
* The node whose grants are being written.
|
||||
* @param $grants
|
||||
* A list of grants to write. See hook_node_access_records() for the
|
||||
* expected structure of the grants array.
|
||||
* @param $realm
|
||||
* (optional) If provided, read/write grants for that realm only. Defaults to
|
||||
* NULL.
|
||||
* @param $delete
|
||||
* (optional) If false, does not delete records. This is only for optimization
|
||||
* purposes, and assumes the caller has already performed a mass delete of
|
||||
* some form. Defaults to TRUE.
|
||||
*/
|
||||
public function writeGrants(NodeInterface $node, $delete = TRUE);
|
||||
|
||||
/**
|
||||
* Creates the default node access grant entry on the grant storage.
|
||||
*/
|
||||
public function writeDefaultGrant();
|
||||
|
||||
/**
|
||||
* Deletes all node access entries.
|
||||
*/
|
||||
public function deleteGrants();
|
||||
|
||||
/**
|
||||
* Counts available node grants.
|
||||
*
|
||||
* @return int
|
||||
* Returns the amount of node grants.
|
||||
*/
|
||||
public function countGrants();
|
||||
|
||||
/**
|
||||
* Checks all grants for a given account.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* A user object representing the user for whom the operation is to be
|
||||
* performed.
|
||||
*
|
||||
* @return int.
|
||||
* Status of the access check.
|
||||
*/
|
||||
public function checkAllGrants(AccountInterface $account);
|
||||
|
||||
}
|
|
@ -0,0 +1,260 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeGrantDatabaseStorage.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityNG;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\user\Plugin\Core\Entity\User;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines a controller class that handles the node grants system.
|
||||
*
|
||||
* This is used to build node query access.
|
||||
*/
|
||||
class NodeGrantDatabaseStorage implements NodeGrantDatabaseStorageInterface {
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* Constructs a NodeAccessController object.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
* The database connection.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
*/
|
||||
public function __construct(Connection $database, ModuleHandlerInterface $module_handler) {
|
||||
$this->database = $database;
|
||||
$this->moduleHandler = $module_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access(EntityInterface $node, $operation, $langcode, AccountInterface $account) {
|
||||
// If no module implements the hook or the node does not have an id there is
|
||||
// no point in querying the database for access grants.
|
||||
if (!$this->moduleHandler->getImplementations('node_grants') || !$node->id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the database for potential access grants.
|
||||
$query = $this->database->select('node_access');
|
||||
$query->addExpression('1');
|
||||
// Only interested for granting in the current operation.
|
||||
$query->condition('grant_' . $operation, 1, '>=');
|
||||
// Check for grants for this node and the correct langcode.
|
||||
$nids = $query->andConditionGroup()
|
||||
->condition('nid', $node->id())
|
||||
->condition('langcode', $langcode);
|
||||
// If the node is published, also take the default grant into account. The
|
||||
// default is saved with a node ID of 0.
|
||||
$status = $node instanceof EntityNG ? $node->status : $node->get('status', $langcode)->value;
|
||||
if ($status) {
|
||||
$nids = $query->orConditionGroup()
|
||||
->condition($nids)
|
||||
->condition('nid', 0);
|
||||
}
|
||||
$query->condition($nids);
|
||||
$query->range(0, 1);
|
||||
|
||||
$grants = $query->orConditionGroup();
|
||||
foreach (node_access_grants($operation, $account instanceof User ? $account->getBCEntity() : $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);
|
||||
}
|
||||
|
||||
return $query->execute()->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkAll(AccountInterface $account) {
|
||||
$query = $this->database->select('node_access');
|
||||
$query->addExpression('COUNT(*)');
|
||||
$query
|
||||
->condition('nid', 0)
|
||||
->condition('grant_view', 1, '>=');
|
||||
|
||||
$grants = db_or();
|
||||
foreach (node_access_grants('view', $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);
|
||||
}
|
||||
return $query->execute()->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function alterQuery($query, array $tables, $op, AccountInterface $account, $base_table) {
|
||||
if (!$langcode = $query->getMetaData('langcode')) {
|
||||
$langcode = FALSE;
|
||||
}
|
||||
|
||||
// Find all instances of the base table being joined -- could appear
|
||||
// more than once in the query, and could be aliased. Join each one to
|
||||
// the node_access table.
|
||||
$grants = node_access_grants($op, $account);
|
||||
foreach ($tables as $nalias => $tableinfo) {
|
||||
$table = $tableinfo['table'];
|
||||
if (!($table instanceof SelectInterface) && $table == $base_table) {
|
||||
$base_table_found = TRUE;
|
||||
// Set the subquery.
|
||||
$subquery = $this->database->select('node_access', 'na')
|
||||
->fields('na', array('nid'));
|
||||
|
||||
$grant_conditions = db_or();
|
||||
// If any grant exists for the specified user, then user has access to the
|
||||
// node for the specified operation.
|
||||
foreach ($grants as $realm => $gids) {
|
||||
foreach ($gids as $gid) {
|
||||
$grant_conditions->condition(db_and()
|
||||
->condition('na.gid', $gid)
|
||||
->condition('na.realm', $realm)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Attach conditions to the subquery for nodes.
|
||||
if (count($grant_conditions->conditions())) {
|
||||
$subquery->condition($grant_conditions);
|
||||
}
|
||||
$subquery->condition('na.grant_' . $op, 1, '>=');
|
||||
|
||||
// Add langcode-based filtering if this is a multilingual site.
|
||||
if (language_multilingual()) {
|
||||
// If no specific langcode to check for is given, use the grant entry
|
||||
// which is set as a fallback.
|
||||
// If a specific langcode is given, use the grant entry for it.
|
||||
if ($langcode === FALSE) {
|
||||
$subquery->condition('na.fallback', 1, '=');
|
||||
}
|
||||
else {
|
||||
$subquery->condition('na.langcode', $langcode, '=');
|
||||
}
|
||||
}
|
||||
|
||||
$field = 'nid';
|
||||
// Now handle entities.
|
||||
$subquery->where("$nalias.$field = na.nid");
|
||||
|
||||
$query->exists($subquery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write(NodeInterface $node, array $grants, $realm = NULL, $delete = TRUE) {
|
||||
if ($delete) {
|
||||
$query = $this->database->delete('node_access')->condition('nid', $node->id());
|
||||
if ($realm) {
|
||||
$query->condition('realm', array($realm, 'all'), 'IN');
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
// Only perform work when node_access modules are active.
|
||||
if (!empty($grants) && count($this->moduleHandler->getImplementations('node_grants'))) {
|
||||
$query = $this->database->insert('node_access')->fields(array('nid', 'langcode', 'fallback', 'realm', 'gid', 'grant_view', 'grant_update', 'grant_delete'));
|
||||
// If we have defined a granted langcode, use it. But if not, add a grant
|
||||
// for every language this node is translated to.
|
||||
foreach ($grants as $grant) {
|
||||
if ($realm && $realm != $grant['realm']) {
|
||||
continue;
|
||||
}
|
||||
if (isset($grant['langcode'])) {
|
||||
$grant_languages = array($grant['langcode'] => language_load($grant['langcode']));
|
||||
}
|
||||
else {
|
||||
$grant_languages = $node->getTranslationLanguages(TRUE);
|
||||
}
|
||||
foreach ($grant_languages as $grant_langcode => $grant_language) {
|
||||
// Only write grants; denies are implicit.
|
||||
if ($grant['grant_view'] || $grant['grant_update'] || $grant['grant_delete']) {
|
||||
$grant['nid'] = $node->id();
|
||||
$grant['langcode'] = $grant_langcode;
|
||||
// The record with the original langcode is used as the fallback.
|
||||
if ($grant['langcode'] == $node->langcode) {
|
||||
$grant['fallback'] = 1;
|
||||
}
|
||||
else {
|
||||
$grant['fallback'] = 0;
|
||||
}
|
||||
$query->values($grant);
|
||||
}
|
||||
}
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete() {
|
||||
$this->database->delete('node_access')->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function writeDefault() {
|
||||
$this->database->insert('node_access')
|
||||
->fields(array(
|
||||
'nid' => 0,
|
||||
'realm' => 'all',
|
||||
'gid' => 0,
|
||||
'grant_view' => 1,
|
||||
'grant_update' => 0,
|
||||
'grant_delete' => 0,
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count() {
|
||||
return $this->database->query('SELECT COUNT(*) FROM {node_access}')->fetchField();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeGrantStorageControllerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for node access controllers.
|
||||
*/
|
||||
interface NodeGrantDatabaseStorageInterface {
|
||||
|
||||
/**
|
||||
* Checks all grants for a given account.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* A user object representing the user for whom the operation is to be
|
||||
* performed.
|
||||
*
|
||||
* @return int.
|
||||
* Status of the access check.
|
||||
*/
|
||||
public function checkAll(AccountInterface $account);
|
||||
|
||||
/**
|
||||
* Alters a query when node access is required.
|
||||
*
|
||||
* @param mixed $query
|
||||
* Query that is being altered.
|
||||
* @param array $tables
|
||||
* A list of tables that need to be part of the alter.
|
||||
* @param string $op
|
||||
* The operation to be performed on the node. Possible values are:
|
||||
* - "view"
|
||||
* - "update"
|
||||
* - "delete"
|
||||
* - "create"
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* A user object representing the user for whom the operation is to be
|
||||
* performed.
|
||||
* @param string $base_table
|
||||
* The base table of the query.
|
||||
*
|
||||
* @return int
|
||||
* Status of the access check.
|
||||
*/
|
||||
public function alterQuery($query, array $tables, $op, AccountInterface $account, $base_table);
|
||||
|
||||
/**
|
||||
* Writes a list of grants to the database, deleting previously saved ones.
|
||||
*
|
||||
* If a realm is provided, it will only delete grants from that realm, but
|
||||
* it will always delete a grant from the 'all' realm. Modules that use
|
||||
* node_access() can use this method when doing mass updates due to
|
||||
* widespread permission changes.
|
||||
*
|
||||
* Note: Don't call this method directly from a contributed module. Call
|
||||
* node_access_write_grants() instead.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node whose grants are being written.
|
||||
* @param array $grants
|
||||
* A list of grants to write. Each grant is an array that must contain the
|
||||
* following keys: realm, gid, grant_view, grant_update, grant_delete.
|
||||
* The realm is specified by a particular module; the gid is as well, and
|
||||
* is a module-defined id to define grant privileges. each grant_* field
|
||||
* is a boolean value.
|
||||
* @param string $realm
|
||||
* (optional) If provided, read/write grants for that realm only. Defaults to
|
||||
* NULL.
|
||||
* @param bool $delete
|
||||
* (optional) If false, does not delete records. This is only for optimization
|
||||
* purposes, and assumes the caller has already performed a mass delete of
|
||||
* some form. Defaults to TRUE.
|
||||
*
|
||||
* @see node_access_write_grants()
|
||||
* @see node_access_acquire_grants()
|
||||
*/
|
||||
public function write(NodeInterface $node, array $grants, $realm = NULL, $delete = TRUE);
|
||||
|
||||
/**
|
||||
* Deletes all node access entries.
|
||||
*/
|
||||
public function delete();
|
||||
|
||||
/**
|
||||
* Creates the default node access grant entry.
|
||||
*/
|
||||
public function writeDefault();
|
||||
|
||||
/**
|
||||
* Determines access to nodes based on node grants.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node
|
||||
* The entity for which to check 'create' access.
|
||||
* @param string $operation
|
||||
* The entity operation. Usually one of 'view', 'edit', 'create' or
|
||||
* 'delete'.
|
||||
* @param string $langcode
|
||||
* The language code for which to check access.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user for which to check access.
|
||||
*
|
||||
* @return bool|null
|
||||
* TRUE if access was granted, FALSE if access was denied or NULL if no
|
||||
* module implements hook_node_grants(), the node does not (yet) have an id
|
||||
* or none of the implementing modules explicitly granted or denied access.
|
||||
*/
|
||||
public function access(EntityInterface $node, $operation, $langcode, AccountInterface $account);
|
||||
|
||||
/**
|
||||
* Counts available node grants.
|
||||
*
|
||||
* @return int
|
||||
* Returns the amount of node grants.
|
||||
*/
|
||||
public function count();
|
||||
|
||||
}
|
|
@ -115,7 +115,7 @@ class Node extends EntityNG implements NodeInterface {
|
|||
// default revision. There's no need to delete existing records if the node
|
||||
// is new.
|
||||
if ($this->isDefaultRevision()) {
|
||||
node_access_acquire_grants($this->getBCEntity(), $update);
|
||||
\Drupal::entityManager()->getAccessController('node')->writeGrants($this->getBCEntity(), $update);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -253,7 +253,7 @@ function hook_node_grants($account, $op) {
|
|||
* @return
|
||||
* An array of grants as defined above.
|
||||
*
|
||||
* @see _node_access_write_grants()
|
||||
* @see node_access_write_grants()
|
||||
* @see hook_node_access_records_alter()
|
||||
* @ingroup node_access
|
||||
*/
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
*/
|
||||
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
|
@ -21,7 +22,6 @@ use Drupal\Core\Entity\EntityInterface;
|
|||
use Drupal\Core\Template\Attribute;
|
||||
use Drupal\entity\Plugin\Core\Entity\EntityDisplay;
|
||||
use Drupal\file\Plugin\Core\Entity\File;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\user\UserInterface;
|
||||
|
||||
/**
|
||||
|
@ -2185,7 +2185,7 @@ function node_access($op, $node, $account = NULL, $langcode = NULL) {
|
|||
$account = user_load($account->uid);
|
||||
}
|
||||
|
||||
return entity_access_controller('node')->access($node, $op, $langcode, $account);
|
||||
return Drupal::entityManager()->getAccessController('node')->access($node, $op, $langcode, $account);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2357,27 +2357,7 @@ function node_access_view_all_nodes($account = NULL) {
|
|||
$access[$account->uid] = TRUE;
|
||||
}
|
||||
else {
|
||||
$query = db_select('node_access');
|
||||
$query->addExpression('COUNT(*)');
|
||||
$query
|
||||
->condition('nid', 0)
|
||||
->condition('grant_view', 1, '>=');
|
||||
|
||||
$grants = db_or();
|
||||
foreach (node_access_grants('view', $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);
|
||||
}
|
||||
$access[$account->uid] = $query
|
||||
->execute()
|
||||
->fetchField();
|
||||
$access[$account->uid] = Drupal::entityManager()->getAccessController('node')->checkAllGrants($account);
|
||||
}
|
||||
|
||||
return $access[$account->uid];
|
||||
|
@ -2447,159 +2427,8 @@ function node_query_node_access_alter(AlterableInterface $query) {
|
|||
}
|
||||
}
|
||||
|
||||
// Find all instances of the base table being joined -- could appear
|
||||
// more than once in the query, and could be aliased. Join each one to
|
||||
// the node_access table.
|
||||
$grants = node_access_grants($op, $account);
|
||||
$base_table_found = FALSE;
|
||||
foreach ($tables as $nalias => $tableinfo) {
|
||||
$table = $tableinfo['table'];
|
||||
if (!($table instanceof SelectInterface) && $table == $base_table) {
|
||||
$base_table_found = TRUE;
|
||||
// Set the subquery.
|
||||
$subquery = db_select('node_access', 'na')
|
||||
->fields('na', array('nid'));
|
||||
|
||||
$grant_conditions = db_or();
|
||||
// If any grant exists for the specified user, then user has access to the
|
||||
// node for the specified operation.
|
||||
foreach ($grants as $realm => $gids) {
|
||||
foreach ($gids as $gid) {
|
||||
$grant_conditions->condition(db_and()
|
||||
->condition('na.gid', $gid)
|
||||
->condition('na.realm', $realm)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Attach conditions to the subquery for nodes.
|
||||
if (count($grant_conditions->conditions())) {
|
||||
$subquery->condition($grant_conditions);
|
||||
}
|
||||
$subquery->condition('na.grant_' . $op, 1, '>=');
|
||||
|
||||
// Add langcode-based filtering if this is a multilingual site.
|
||||
if (language_multilingual()) {
|
||||
// If no specific langcode to check for is given, use the grant entry
|
||||
// which is set as a fallback.
|
||||
// If a specific langcode is given, use the grant entry for it.
|
||||
if ($langcode === FALSE) {
|
||||
$subquery->condition('na.fallback', 1, '=');
|
||||
}
|
||||
else {
|
||||
$subquery->condition('na.langcode', $langcode, '=');
|
||||
}
|
||||
}
|
||||
|
||||
$field = 'nid';
|
||||
// Now handle entities.
|
||||
$subquery->where("$nalias.$field = na.nid");
|
||||
|
||||
$query->exists($subquery);
|
||||
}
|
||||
}
|
||||
|
||||
// If we reached this point and did not find the defined base table, throw
|
||||
// an exception.
|
||||
if (!$base_table_found) {
|
||||
throw new Exception(t('Query tagged for node access but the defined base_table @base_table was not found', array('@base_table' => $base_table)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of node access grants and writes them to the database.
|
||||
*
|
||||
* This function is called when a node is saved, and can also be called by
|
||||
* modules if something other than a node save causes node access permissions to
|
||||
* change. It collects all node access grants for the node from
|
||||
* hook_node_access_records() implementations, allows these grants to be altered
|
||||
* via hook_node_access_records_alter() implementations, and saves the collected
|
||||
* and altered grants to the database.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The $node to acquire grants for.
|
||||
* @param $delete
|
||||
* (optional) Whether to delete existing node access records before inserting
|
||||
* new ones. Defaults to TRUE.
|
||||
*/
|
||||
function node_access_acquire_grants(NodeInterface $node, $delete = TRUE) {
|
||||
$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 and the node is published, then use the default grant.
|
||||
if (empty($grants) && $node->isPublished()) {
|
||||
$grants[] = array('realm' => 'all', 'gid' => 0, 'grant_view' => 1, 'grant_update' => 0, 'grant_delete' => 0);
|
||||
}
|
||||
_node_access_write_grants($node, $grants, NULL, $delete);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a list of grants to the database, deleting any previously saved ones.
|
||||
*
|
||||
* If a realm is provided, it will only delete grants from that realm, but it
|
||||
* will always delete a grant from the 'all' realm. Modules that utilize
|
||||
* node_access() can use this function when doing mass updates due to widespread
|
||||
* permission changes.
|
||||
*
|
||||
* Note: Don't call this function directly from a contributed module. Call
|
||||
* node_access_acquire_grants() instead.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node
|
||||
* The node whose grants are being written.
|
||||
* @param $grants
|
||||
* A list of grants to write. See hook_node_access_records() for the
|
||||
* expected structure of the grants array.
|
||||
* @param $realm
|
||||
* (optional) If provided, read/write grants for that realm only. Defaults to
|
||||
* NULL.
|
||||
* @param $delete
|
||||
* (optional) If false, does not delete records. This is only for optimization
|
||||
* purposes, and assumes the caller has already performed a mass delete of
|
||||
* some form. Defaults to TRUE.
|
||||
*
|
||||
* @see node_access_acquire_grants()
|
||||
*/
|
||||
function _node_access_write_grants(EntityInterface $node, $grants, $realm = NULL, $delete = TRUE) {
|
||||
if ($delete) {
|
||||
$query = db_delete('node_access')->condition('nid', $node->id());
|
||||
if ($realm) {
|
||||
$query->condition('realm', array($realm, 'all'), 'IN');
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
// Only perform work when node_access modules are active.
|
||||
if (!empty($grants) && count(module_implements('node_grants'))) {
|
||||
$query = db_insert('node_access')->fields(array('nid', 'langcode', 'fallback', 'realm', 'gid', 'grant_view', 'grant_update', 'grant_delete'));
|
||||
// If we have defined a granted langcode, use it. But if not, add a grant
|
||||
// for every language this node is translated to.
|
||||
foreach ($grants as $grant) {
|
||||
if ($realm && $realm != $grant['realm']) {
|
||||
continue;
|
||||
}
|
||||
if (isset($grant['langcode'])) {
|
||||
$grant_languages = array($grant['langcode'] => language_load($grant['langcode']));
|
||||
}
|
||||
else {
|
||||
$grant_languages = $node->getTranslationLanguages(TRUE);
|
||||
}
|
||||
foreach ($grant_languages as $grant_langcode => $grant_language) {
|
||||
// Only write grants; denies are implicit.
|
||||
if ($grant['grant_view'] || $grant['grant_update'] || $grant['grant_delete']) {
|
||||
$grant['nid'] = $node->id();
|
||||
$grant['langcode'] = $grant_langcode;
|
||||
// The record with the original langcode is used as the fallback.
|
||||
if ($grant['langcode'] == $node->langcode) {
|
||||
$grant['fallback'] = 1;
|
||||
}
|
||||
else {
|
||||
$grant['fallback'] = 0;
|
||||
}
|
||||
$query->values($grant);
|
||||
}
|
||||
}
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
// Update the query for the given storage method.
|
||||
Drupal::service('node.grant_storage')->alterQuery($query, $tables, $op, $account, $base_table);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2657,7 +2486,8 @@ function node_access_needs_rebuild($rebuild = NULL) {
|
|||
* @see node_access_needs_rebuild()
|
||||
*/
|
||||
function node_access_rebuild($batch_mode = FALSE) {
|
||||
db_delete('node_access')->execute();
|
||||
$access_controller = Drupal::entityManager()->getAccessController('node');
|
||||
$access_controller->deleteGrants();
|
||||
// Only recalculate if the site is using a node_access module.
|
||||
if (count(module_implements('node_grants'))) {
|
||||
if ($batch_mode) {
|
||||
|
@ -2675,29 +2505,22 @@ function node_access_rebuild($batch_mode = FALSE) {
|
|||
drupal_set_time_limit(240);
|
||||
|
||||
// Rebuild newest nodes first so that recent content becomes available quickly.
|
||||
$nids = db_query("SELECT nid FROM {node} ORDER BY nid DESC")->fetchCol();
|
||||
$entity_query = Drupal::entityQuery('node');
|
||||
$entity_query->sort('nid', 'DESC');
|
||||
$nids = $entity_query->execute();
|
||||
foreach ($nids as $nid) {
|
||||
$node = node_load($nid, TRUE);
|
||||
// To preserve database integrity, only acquire grants if the node
|
||||
// To preserve database integrity, only write grants if the node
|
||||
// loads successfully.
|
||||
if (!empty($node)) {
|
||||
node_access_acquire_grants($node);
|
||||
$access_controller->writeGrants($node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Not using any node_access modules. Add the default grant.
|
||||
db_insert('node_access')
|
||||
->fields(array(
|
||||
'nid' => 0,
|
||||
'realm' => 'all',
|
||||
'gid' => 0,
|
||||
'grant_view' => 1,
|
||||
'grant_update' => 0,
|
||||
'grant_delete' => 0,
|
||||
))
|
||||
->execute();
|
||||
$access_controller->writeDefaultGrant();
|
||||
}
|
||||
|
||||
if (!isset($batch)) {
|
||||
|
@ -2722,18 +2545,22 @@ function _node_access_rebuild_batch_operation(&$context) {
|
|||
// Initiate multistep processing.
|
||||
$context['sandbox']['progress'] = 0;
|
||||
$context['sandbox']['current_node'] = 0;
|
||||
$context['sandbox']['max'] = db_query('SELECT COUNT(DISTINCT nid) FROM {node}')->fetchField();
|
||||
$context['sandbox']['max'] = Drupal::entityQuery('node')->count()->execute();
|
||||
}
|
||||
|
||||
// Process the next 20 nodes.
|
||||
$limit = 20;
|
||||
$nids = db_query_range("SELECT nid FROM {node} WHERE nid > :nid ORDER BY nid ASC", 0, $limit, array(':nid' => $context['sandbox']['current_node']))->fetchCol();
|
||||
$nids = Drupal::entityQuery('node')
|
||||
->condition('nid', $context['sandbox']['current_node'], '>')
|
||||
->sort('nid', 'DESC')
|
||||
->range(0, $limit)
|
||||
->execute();
|
||||
$nodes = node_load_multiple($nids, TRUE);
|
||||
foreach ($nodes as $nid => $node) {
|
||||
// To preserve database integrity, only acquire grants if the node
|
||||
// To preserve database integrity, only write grants if the node
|
||||
// loads successfully.
|
||||
if (!empty($node)) {
|
||||
node_access_acquire_grants($node);
|
||||
Drupal::entityManager()->getAccessController('node')->writeGrants($node);
|
||||
}
|
||||
$context['sandbox']['progress']++;
|
||||
$context['sandbox']['current_node'] = $nid;
|
||||
|
@ -2779,7 +2606,7 @@ function node_requirements($phase) {
|
|||
// Only show rebuild button if there are either 0, or 2 or more, rows
|
||||
// in the {node_access} table, or if there are modules that
|
||||
// implement hook_node_grants().
|
||||
$grant_count = db_query('SELECT COUNT(*) FROM {node_access}')->fetchField();
|
||||
$grant_count = Drupal::entityManager()->getAccessController('node')->countGrants();
|
||||
if ($grant_count != 1 || count(module_implements('node_grants')) > 0) {
|
||||
$value = format_plural($grant_count, 'One permission in use', '@count permissions in use', array('@count' => $grant_count));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
services:
|
||||
node.grant_storage:
|
||||
class: Drupal\node\NodeGrantDatabaseStorage
|
||||
arguments: ['@database', '@module_handler']
|
Loading…
Reference in New Issue