View source
<?php
namespace Drupal\search_api\Plugin\search_api\processor;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\Session\UserSession;
use Drupal\search_api\Datasource\DatasourceInterface;
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 Drupal\user\RoleInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class RoleAccess extends ProcessorPluginBase {
use LoggerTrait;
const ROLE_ACCESS_FIELD = 'search_api_role_access';
protected $currentUser;
protected static $lastUsedUid = PHP_INT_MAX;
protected static $roleDummyAccounts = [];
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$processor = parent::create($container, $configuration, $plugin_id, $plugin_definition);
$processor
->setCurrentUser($container
->get('current_user'));
$processor
->setLogger($container
->get('logger.channel.search_api'));
return $processor;
}
public function getCurrentUser() {
return $this->currentUser ?: \Drupal::currentUser();
}
public function setCurrentUser(AccountProxyInterface $current_user) {
$this->currentUser = $current_user;
return $this;
}
public function getPropertyDefinitions(DatasourceInterface $datasource = NULL) {
$properties = [];
if (!$datasource) {
$definition = [
'label' => $this
->t('Role-based access information'),
'description' => $this
->t('Data needed to apply role-based item access'),
'type' => 'string',
'processor_id' => $this
->getPluginId(),
'hidden' => TRUE,
'is_list' => TRUE,
];
$properties[static::ROLE_ACCESS_FIELD] = new ProcessorProperty($definition);
}
return $properties;
}
public function addFieldValues(ItemInterface $item) {
$role_has_access = function (RoleInterface $role) use ($item) {
$transient_account = $this
->createTransientAccountWithRole($role);
return $item
->getDatasource()
->getItemAccessResult($item
->getOriginalObject(), $transient_account)
->isAllowed();
};
$allowed_roles = array_filter(user_roles(), $role_has_access);
$allowed_roles = array_map(function (RoleInterface $role) {
return $role
->id();
}, $allowed_roles);
$fields = $item
->getFields();
$fields = $this
->getFieldsHelper()
->filterForPropertyPath($fields, NULL, static::ROLE_ACCESS_FIELD);
foreach ($fields as $field) {
$field
->setValues($allowed_roles);
}
}
protected function createTransientAccountWithRole(RoleInterface $role) : AccountInterface {
$role_id = $role
->id();
if (empty(static::$roleDummyAccounts[$role_id])) {
if ($role_id === AccountInterface::ANONYMOUS_ROLE) {
$uid = 0;
}
else {
$uid = --static::$lastUsedUid;
}
static::$roleDummyAccounts[$role_id] = new UserSession([
'roles' => [
$role_id,
],
'uid' => $uid,
]);
}
return static::$roleDummyAccounts[$role_id];
}
public function preIndexSave() {
$this
->ensureField(NULL, static::ROLE_ACCESS_FIELD, 'string')
->setHidden();
}
public function preprocessSearchQuery(QueryInterface $query) {
if ($query
->getOption('search_api_bypass_access')) {
return;
}
$account = $query
->getOption('search_api_access_account', $this
->getCurrentUser());
if (is_numeric($account)) {
$account = User::load($account);
}
$role_field = $this
->findField(NULL, static::ROLE_ACCESS_FIELD, 'string');
if ($role_field) {
$query
->addCondition($role_field
->getFieldIdentifier(), $account
->getRoles(), 'IN');
}
else {
$query
->abort();
$this
->getLogger()
->warning('Role-based access checks could not be added to a search query on index %index since the required field is not available. Please re-save the index.', [
'%index' => $query
->getIndex()
->label(),
]);
}
}
}