View source
<?php
namespace Drupal\search_api\Plugin\search_api\processor;
use Drupal\comment\CommentInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\Session\AnonymousUserSession;
use Drupal\Core\TypedData\ComplexDataInterface;
use Drupal\node\NodeInterface;
use Drupal\search_api\Datasource\DatasourceInterface;
use Drupal\search_api\IndexInterface;
use Drupal\search_api\Item\ItemInterface;
use Drupal\search_api\LoggerTrait;
use Drupal\search_api\Processor\ProcessorPluginBase;
use Drupal\search_api\Processor\ProcessorProperty;
use Drupal\search_api\Query\QueryInterface;
use Drupal\user\Entity\User;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ContentAccess extends ProcessorPluginBase {
use LoggerTrait;
protected $database;
protected $currentUser;
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$processor = parent::create($container, $configuration, $plugin_id, $plugin_definition);
$processor
->setLogger($container
->get('logger.channel.search_api'));
$processor
->setDatabase($container
->get('database'));
$processor
->setCurrentUser($container
->get('current_user'));
return $processor;
}
public function getDatabase() {
return $this->database ?: \Drupal::database();
}
public function setDatabase(Connection $database) {
$this->database = $database;
return $this;
}
public function getCurrentUser() {
return $this->currentUser ?: \Drupal::currentUser();
}
public function setCurrentUser(AccountProxyInterface $current_user) {
$this->currentUser = $current_user;
return $this;
}
public static function supportsIndex(IndexInterface $index) {
foreach ($index
->getDatasources() as $datasource) {
if (in_array($datasource
->getEntityTypeId(), [
'node',
'comment',
])) {
return TRUE;
}
}
return FALSE;
}
public function getPropertyDefinitions(DatasourceInterface $datasource = NULL) {
$properties = [];
if (!$datasource) {
$definition = [
'label' => $this
->t('Node access information'),
'description' => $this
->t('Data needed to apply node access.'),
'type' => 'string',
'processor_id' => $this
->getPluginId(),
'hidden' => TRUE,
'is_list' => TRUE,
];
$properties['search_api_node_grants'] = new ProcessorProperty($definition);
}
return $properties;
}
public function addFieldValues(ItemInterface $item) {
static $anonymous_user;
if (!isset($anonymous_user)) {
$anonymous_user = new AnonymousUserSession();
}
$entity_type_id = $item
->getDatasource()
->getEntityTypeId();
if (!in_array($entity_type_id, [
'node',
'comment',
])) {
return;
}
$node = $this
->getNode($item
->getOriginalObject());
if (!$node) {
return;
}
$fields = $item
->getFields();
$fields = $this
->getFieldsHelper()
->filterForPropertyPath($fields, NULL, 'search_api_node_grants');
foreach ($fields as $field) {
$sql = 'SELECT gid, realm FROM {node_access} WHERE (nid = 0 OR nid = :nid) AND grant_view = 1';
$args = [
':nid' => $node
->id(),
];
$grant_records = $this
->getDatabase()
->query($sql, $args)
->fetchAll();
if ($grant_records) {
foreach ($grant_records as $grant) {
$field
->addValue("node_access_{$grant->realm}:{$grant->gid}");
}
}
else {
$field
->addValue('node_access__all');
}
}
}
public function preIndexSave() {
foreach ($this->index
->getDatasources() as $datasource_id => $datasource) {
$entity_type = $datasource
->getEntityTypeId();
if (in_array($entity_type, [
'node',
'comment',
])) {
$this
->ensureField($datasource_id, 'status', 'boolean');
if ($entity_type == 'node') {
$this
->ensureField($datasource_id, 'uid', 'integer');
}
}
}
$field = $this
->ensureField(NULL, 'search_api_node_grants', 'string');
$field
->setHidden();
}
protected function getNode(ComplexDataInterface $item) {
$item = $item
->getValue();
if ($item instanceof CommentInterface) {
$item = $item
->getCommentedEntity();
}
if ($item instanceof NodeInterface) {
return $item;
}
return NULL;
}
public function preprocessSearchQuery(QueryInterface $query) {
if (!$query
->getOption('search_api_bypass_access')) {
$account = $query
->getOption('search_api_access_account', $this
->getCurrentUser());
if (is_numeric($account)) {
$account = User::load($account);
}
if ($account instanceof AccountInterface) {
$this
->addNodeAccess($query, $account);
}
else {
$account = $query
->getOption('search_api_access_account', $this
->getCurrentUser());
if ($account instanceof AccountInterface) {
$account = $account
->id();
}
if (!is_scalar($account)) {
$account = var_export($account, TRUE);
}
$this
->getLogger()
->warning('An illegal user UID was given for node access: @uid.', [
'@uid' => $account,
]);
}
}
}
protected function addNodeAccess(QueryInterface $query, AccountInterface $account) {
if ($account
->hasPermission('bypass node access')) {
return;
}
$affected_datasources = [];
$unaffected_datasources = [];
foreach ($this->index
->getDatasources() as $datasource_id => $datasource) {
$entity_type = $datasource
->getEntityTypeId();
if (in_array($entity_type, [
'node',
'comment',
])) {
$affected_datasources[$entity_type][] = $datasource_id;
}
else {
$unaffected_datasources[] = $datasource_id;
}
}
if ($unaffected_datasources) {
$outer_conditions = $query
->createConditionGroup('OR', [
'content_access',
]);
$query
->addConditionGroup($outer_conditions);
foreach ($unaffected_datasources as $datasource_id) {
$outer_conditions
->addCondition('search_api_datasource', $datasource_id);
}
$access_conditions = $query
->createConditionGroup('AND');
$outer_conditions
->addConditionGroup($access_conditions);
}
else {
$access_conditions = $query;
}
if (!$account
->hasPermission('access content')) {
unset($affected_datasources['node']);
}
if (!$account
->hasPermission('access comments')) {
unset($affected_datasources['comment']);
}
if (!$affected_datasources) {
if (!$unaffected_datasources) {
$query
->abort($this
->t('You have no access to any results in this search.'));
}
return;
}
$unpublished_own = $account
->hasPermission('view own unpublished content');
$enabled_conditions = $query
->createConditionGroup('OR', [
'content_access_enabled',
]);
foreach ($affected_datasources as $entity_type => $datasources) {
foreach ($datasources as $datasource_id) {
$status_field = $this
->findField($datasource_id, 'status', 'boolean');
if ($status_field) {
$enabled_conditions
->addCondition($status_field
->getFieldIdentifier(), TRUE);
}
if ($entity_type == 'node' && $unpublished_own) {
$author_field = $this
->findField($datasource_id, 'uid', 'integer');
if ($author_field) {
$enabled_conditions
->addCondition($author_field
->getFieldIdentifier(), $account
->id());
}
}
}
}
$access_conditions
->addConditionGroup($enabled_conditions);
$node_grants_field = $this
->findField(NULL, 'search_api_node_grants', 'string');
if (!$node_grants_field) {
return;
}
$node_grants_field_id = $node_grants_field
->getFieldIdentifier();
$grants_conditions = $query
->createConditionGroup('OR', [
'content_access_grants',
]);
$grants = node_access_grants('view', $account);
foreach ($grants as $realm => $gids) {
foreach ($gids as $gid) {
$grants_conditions
->addCondition($node_grants_field_id, "node_access_{$realm}:{$gid}");
}
}
$grants_conditions
->addCondition($node_grants_field_id, 'node_access__all');
$access_conditions
->addConditionGroup($grants_conditions);
}
}