access_policy-1.0.x-dev/src/AccessPolicyDiscovery.php

src/AccessPolicyDiscovery.php
<?php

namespace Drupal\access_policy;

use Drupal\access_policy\Validation\ExecutionContext;
use Drupal\access_policy\Validation\ExecutionContextInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;

/**
 * Handles the discovery of access policies for a given user and entity.
 */
class AccessPolicyDiscovery implements AccessPolicyDiscoveryInterface {

  use StringTranslationTrait;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * Access policy information service.
   *
   * @var \Drupal\access_policy\AccessPolicyInformation
   */
  protected $accessPolicyInformation;

  /**
   * The selection rule plugin manager.
   *
   * @var \Drupal\access_policy\AccessPolicyHandlerManager
   */
  protected $selectionRuleManager;

  /**
   * Array of allowed access policies, keyed by entity uuid.
   *
   * @var array
   */
  protected $allowedPolicies = [];

  /**
   * Array of applicable access policies, keyed by entity uuid.
   *
   * @var array
   */
  protected $applicablePolicies = [];

  /**
   * Constructs a new AccessPolicySelection service.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\access_policy\AccessPolicyInformation $access_policy_information
   *   The access policy information service.
   * @param \Drupal\access_policy\AccessPolicyHandlerManager $selection_rule_manager
   *   The selection rule manager.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, AccessPolicyInformation $access_policy_information, AccessPolicyHandlerManager $selection_rule_manager) {
    $this->entityTypeManager = $entity_type_manager;
    $this->accessPolicyInformation = $access_policy_information;
    $this->selectionRuleManager = $selection_rule_manager;
  }

  /**
   * {@inheritdoc}
   */
  public function getAllowedAccessPolicies(EntityInterface $entity, AccountInterface $account) {
    if (isset($this->allowedPolicies[$entity->uuid()])) {
      return $this->allowedPolicies[$entity->uuid()];
    }

    $this->allowedPolicies[$entity->uuid()] = [];
    $policies = $this->getAccessPoliciesSorted($entity->getEntityType());
    foreach ($policies as $policy) {
      if ($this->isAllowed($policy, $entity, $account)) {
        $this->allowedPolicies[$entity->uuid()][] = $policy;
      }
    }

    return $this->allowedPolicies[$entity->uuid()];
  }

  /**
   * {@inheritdoc}
   */
  public function getApplicablePolicies(EntityInterface $entity, AccountInterface $account) {
    if (isset($this->applicablePolicies[$entity->uuid()])) {
      return $this->applicablePolicies[$entity->uuid()];
    }

    $this->applicablePolicies[$entity->uuid()] = [];
    $policies = $this->getAccessPoliciesSorted($entity->getEntityType());
    if (!empty($policies)) {
      $candidates = [];
      foreach ($policies as $policy) {
        $is_allowed = $this->isAllowed($policy, $entity, $account);
        if ($is_allowed) {
          $selection_rules_valid = $this->selectionRulesValid($policy, $entity, $account);
          if ($selection_rules_valid) {
            // If this is the first policy and this is unique then return that
            // policy and exit.
            if (empty($candidates) && !$policy->isMultiple()) {
              $this->applicablePolicies[$entity->uuid()] = $policy;
              return [$this->applicablePolicies[$entity->uuid()]];
            }

            // Group the policies by selection set if applicable.
            $selection_sets = $policy->getSelectionSet() ?? ['_empty'];
            foreach ($selection_sets as $selection_set) {
              $candidates[$selection_set][] = $policy;
            }
          }
        }
      }

      // Always return the first set of candidates.
      return $this->applicablePolicies[$entity->uuid()] = reset($candidates);
    }

    return FALSE;
  }

  /**
   * Determine whether a user is allowed to assign an access policy.
   *
   * This checks whether the user has the permission or if the policy is
   * applicable.
   *
   * @param \Drupal\Core\Entity\EntityInterface $policy
   *   The access policy entity.
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The current entity.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The current user.
   *
   * @return bool
   *   TRUE if the user can use the policy; FALSE otherwise.
   */
  protected function isAllowed(EntityInterface $policy, EntityInterface $entity, AccountInterface $account) {
    if ($this->isApplicable($policy, $entity) && $this->hasPermission($policy, $account)) {
      return TRUE;
    }

    return FALSE;
  }

  /**
   * Determine whether a user has the permission to assign an access policy.
   *
   * @param \Drupal\Core\Entity\EntityInterface $policy
   *   The access policy entity.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The current user.
   *
   * @return bool
   *   TRUE if the user can use the policy; FALSE otherwise.
   */
  protected function hasPermission(EntityInterface $policy, AccountInterface $account) {
    if (!$account->hasPermission('assign ' . $policy->id() . ' access policy')) {
      return FALSE;
    }
    return TRUE;
  }

  /**
   * Determine whether a policy is applicable for an entity.
   *
   * @param \Drupal\Core\Entity\EntityInterface $policy
   *   The access policy entity.
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The current entity.
   *
   * @return bool
   *   TRUE if the policy is applicable; FALSE otherwise.
   *
   * @throws \Drupal\Component\Plugin\Exception\PluginException
   */
  protected function isApplicable(EntityInterface $policy, EntityInterface $entity) {
    // If the policy has selection rules, but they're not applicable to this
    // entity, then this policy is not allowed to be assigned.
    $has_handlers = !empty($policy->getHandlers('selection_rule'));
    $is_applicable = !empty($this->selectionRuleManager->getApplicableHandlers($policy, $entity));
    if ($has_handlers && !$is_applicable) {
      return FALSE;
    }

    return TRUE;
  }

  /**
   * Determine whether the selection rules are valid.
   *
   * @param \Drupal\Core\Entity\EntityInterface $policy
   *   The access policy.
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The current entity.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The current user.
   *
   * @return bool
   *   TRUE if the selection rule are valid; FALSE otherwise.
   */
  protected function selectionRulesValid(EntityInterface $policy, EntityInterface $entity, AccountInterface $account) {
    $selection_rules = $this->selectionRuleManager->getHandlersFromPolicy($policy);
    if (!empty($selection_rules)) {
      $context = ExecutionContext::create('assign access policy', [
        'entity' => $entity,
        'user' => $account,
      ]);
      return $this->validateSelectionRules($selection_rules, $context, $policy->getSelectionRuleOperator());
    }

    return TRUE;
  }

  /**
   * Validate the selection rules.
   *
   * @param array $selection_rules
   *   Array of selection rules.
   * @param \Drupal\access_policy\Validation\ExecutionContextInterface $context
   *   The execution context.
   * @param string $operator
   *   The selection rule operator.
   *
   * @return bool
   *   TRUE if the selection rule passes; FALSE otherwise.
   */
  protected function validateSelectionRules(array $selection_rules, ExecutionContextInterface $context, $operator = 'AND') {
    $access = TRUE;
    foreach ($selection_rules as $plugin) {
      $plugin->setContext($context);
      // Compute the final value that will be validated against.
      $plugin->getValue();

      $entity = $context->get('entity');
      $account = $context->get('user');
      // @todo deprecate passing arguments to validate().
      $access = $plugin->validate($entity, $account);

      switch ($operator) {
        // If all rules are required then fail immediately if any return false.
        case 'AND':
          if (!$access) {
            return FALSE;
          }
          break;

        // If any rules are required then pass immediately if any return true.
        case 'OR':
          if ($access) {
            return TRUE;
          }
          break;
      }
    }

    return $access;
  }

  /**
   * Get access policies sorted by ascending weight.
   *
   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
   *   The entity type.
   *
   * @return array
   *   Array of sorted access policies.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  private function getAccessPoliciesSorted(EntityTypeInterface $entity_type) {
    $policies = $this->accessPolicyInformation->getEnabledForEntitiesOfEntityType($entity_type);
    $sorted = [];
    if (!empty($policies)) {
      foreach ($policies as $policy) {
        $sorted[$policy->id()] = $policy->getWeight();
      }
      asort($sorted);

      return $this->entityTypeManager->getStorage('access_policy')
        ->loadMultiple(array_keys($sorted));
    }

    return [];
  }

  /**
   * Reset the access policy discovery cache.
   */
  public function resetCache() {
    $this->allowedPolicies = NULL;
    $this->applicablePolicies = NULL;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc