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

src/AccessPolicySelection.php
<?php

namespace Drupal\access_policy;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;

/**
 * Handles the selection of access policies.
 */
class AccessPolicySelection implements AccessPolicySelectionInterface {

  use StringTranslationTrait;

  /**
   * Invalid set key.
   *
   * This can happen if an entity is assigned two or more policies that are
   * not part of a specific set.
   */
  const NULL_SET = 'null_set';

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

  /**
   * The access policy settings.
   *
   * @var \Drupal\access_policy\EntityTypeSettingsInterface
   */
  protected $entityTypeSettings;

  /**
   * The access policy discovery service.
   *
   * @var \Drupal\access_policy\AccessPolicyDiscoveryInterface
   */
  protected $accessPolicyDiscovery;

  /**
   * The selection strategy plugin manager.
   *
   * @var \Drupal\access_policy\SelectionStrategyPluginManager
   */
  protected $selectionStrategyManager;

  /**
   * The selection strategy keyed by entity type id.
   *
   * @var \Drupal\access_policy\Plugin\access_policy\SelectionStrategy\SelectionStrategyInterface[]
   */
  protected $selectionStrategy;

  /**
   * Constructs a new AccessPolicySelection service.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\access_policy\EntityTypeSettingsInterface $settings
   *   The access policy settings.
   * @param \Drupal\access_policy\AccessPolicyDiscoveryInterface $access_policy_discovery
   *   The access policy discovery service.
   * @param \Drupal\access_policy\SelectionStrategyPluginManager $selection_strategy_manager
   *   The selection strategy plugin manager.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeSettingsInterface $settings, AccessPolicyDiscoveryInterface $access_policy_discovery, SelectionStrategyPluginManager $selection_strategy_manager) {
    $this->entityTypeManager = $entity_type_manager;
    $this->entityTypeSettings = $settings;
    $this->accessPolicyDiscovery = $access_policy_discovery;
    $this->selectionStrategyManager = $selection_strategy_manager;
  }

  /**
   * {@inheritdoc}
   */
  public function validateAssignAccessPolicy(EntityInterface $entity, $account) {

    if (!$this->hasAllowedAccessPolicies($entity, $account)) {
      return FALSE;
    }

    if (!$this->hasPermissionToCurrentPolicy($entity, $account)) {
      return FALSE;
    }

    return TRUE;
  }

  /**
   * Determine whether there are any allowed access policies.
   *
   * @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 assign any policies; FALSE otherwise.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function hasAllowedAccessPolicies(EntityInterface $entity, AccountInterface $account) {
    $allowed_policies = $this->accessPolicyDiscovery->getAllowedAccessPolicies($entity, $account);
    if (empty($allowed_policies)) {
      return FALSE;
    }

    return TRUE;

  }

  /**
   * Determine whether the user has access to the current assigned policy.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The current user.
   *
   * @return bool
   *   TRUE if the user has access to the current policy; FALSE otherwise.
   */
  protected function hasPermissionToCurrentPolicy(EntityInterface $entity, AccountInterface $account) {
    // Compare allowed policies with the current policy. If not found, then do
    // not grant access.
    $policies = $this->getCurrentPolicy($entity);
    foreach ($policies as $policy) {
      if (!$this->hasPermission($policy, $account)) {
        return FALSE;
      }
    }

    return TRUE;
  }

  /**
   * Get the current policy on the entity.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The current entity.
   *
   * @return \Drupal\Core\Entity\EntityInterface[]
   *   Array of access policy entities.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function getCurrentPolicy(EntityInterface $entity) {
    // Compare allowed policies with the current policy. If not found, then do
    // not grant access.
    $current_policies = $entity->get('access_policy')->getValue();
    $current_policies = array_filter($current_policies, function ($item) {
      return (!empty($item['target_id']));
    });

    $policy_ids = array_map(function ($item) {
      return $item['target_id'];
    }, $current_policies);

    if (empty($policy_ids)) {
      return [];
    }

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

  /**
   * {@inheritdoc}
   */
  public function getAllowedAccessPolicies(EntityInterface $entity, AccountInterface $account) {
    return $this->accessPolicyDiscovery->getAllowedAccessPolicies($entity, $account);
  }

  /**
   * {@inheritdoc}
   */
  public function getApplicablePolicies(EntityInterface $entity, AccountInterface $account) {
    return $this->accessPolicyDiscovery->getApplicablePolicies($entity, $account);
  }

  /**
   * Get the access policy discovery service.
   *
   * @return \Drupal\access_policy\AccessPolicyDiscoveryInterface
   *   The access policy discovery service.
   */
  public function getDiscovery() {
    return $this->accessPolicyDiscovery;
  }

  /**
   * Resets the discovery cache.
   */
  public function resetCache() {
    $this->accessPolicyDiscovery->resetCache();
  }

  /**
   * {@inheritdoc}
   */
  public function getDefaultPolicy(EntityInterface $entity, AccountInterface $account) {
    // First check to see if the strategy has overridden this value.
    $strategy = $this->getSelectionStrategy($entity->getEntityTypeId());
    $assignment = $strategy->getOption('dynamic_assignment');
    if ($assignment == 'on_create' || $assignment == 'on_save' || $assignment == 'on_change') {
      return $this->getApplicablePolicies($entity, $account);
    }

    return FALSE;
  }

  /**
   * Determine whether the access policy should be updated.
   *
   * The access policy should only be updated if it's new or if the fields
   * observed by selection rules have changed.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The current entity.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The current user.
   *
   * @return bool
   *   TRUE if the access policy should be updated; FALSE otherwise.
   */
  public function shouldUpdateAccessPolicy(EntityInterface $entity, AccountInterface $account) {
    if ($entity->isNew()) {
      return TRUE;
    }

    // Don't allow any changes to happen if the user does not have permission
    // to the current policy.
    if (!$this->hasPermissionToCurrentPolicy($entity, $account)) {
      return FALSE;
    }

    $strategy = $this->getSelectionStrategy($entity->getEntityTypeId());
    if ($strategy->getOption('dynamic_assignment') == 'on_change') {
      $fields = $this->getSelectionRuleFields($entity, $account);
      if (!empty($fields)) {
        // If the allowed policies have selection rules then check to see if the
        // fields changed. But only if dynamic assignment is set to on_change.
        if ($this->hasEntityChangedBetweenRevisions($entity, $fields)) {
          return TRUE;
        }

        // Finally, check to see if there are any applicable access policies
        // but for some reason they are missing.
        return $this->hasApplicableButMissingAccessPolicy($entity, $account);
      }
    }

    return TRUE;
  }

  /**
   * Get the applicable fields for selection rules associated with policies.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The current entity.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The current user.
   *
   * @return array
   *   Array of selection rule fields.
   */
  protected function getSelectionRuleFields(EntityInterface $entity, AccountInterface $account) {
    // Check only the access policies that the user has access too.
    $access_policies = $this->getAllowedAccessPolicies($entity, $account);
    $fields = [];
    // Get all the fields that are observed by selection rules.
    foreach ($access_policies as $policy) {
      $selection_rules = $policy->getHandlers('selection_rule');
      foreach ($selection_rules as $selection_rule) {
        if (isset($selection_rule['field'])) {
          $fields[] = $selection_rule['field'];
        }
      }
    }

    return $fields;
  }

  /**
   * Determine whether it's missing an access policy.
   *
   * This checks to see if it's missing an access policy even though there are
   * applicable policies available.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The current entity.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The current user.
   *
   * @return bool
   *   TRUE if it's missing an access policy; FALSE otherwise.
   */
  protected function hasApplicableButMissingAccessPolicy(EntityInterface $entity, AccountInterface $account) {
    $policies = $entity->get('access_policy')->referencedEntities();
    if (!empty($policies)) {
      return FALSE;
    }

    $applicable = $this->getApplicablePolicies($entity, $account);
    if (!empty($applicable)) {
      return TRUE;
    }

    return FALSE;
  }

  /**
   * Determine whether the entity has changed between revisions.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The current entity.
   * @param array $fields
   *   The array of fields to check.
   *
   * @return bool
   *   TRUE if the entity has changed; FALSE otherwise.
   */
  protected function hasEntityChangedBetweenRevisions(EntityInterface $entity, array $fields) {
    // Determine whether the entity has changed. If it has then we need to
    // execute the selection rules again.
    // Get the previous revision.
    $original = $this->getLatestRevision($entity);
    foreach ($fields as $field) {
      if ($entity->hasField($field)) {
        $original_field = $original->get($field);
        if (!$entity->get($field)->equals($original_field)) {
          return TRUE;
        }
      }
    }

    return FALSE;
  }

  /**
   * Get the latest revision.
   *
   * When doing field comparisons, we have to check the latest revision. This is
   * because $entity->original always contains the current revision and can
   * lead to unpredictable results when trying to determine if a new policy
   * should be assigned.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The current entity.
   */
  protected function getLatestRevision(EntityInterface $entity) {
    /** @var \Drupal\Core\Entity\RevisionableStorageInterface $storage */
    $storage = $this->entityTypeManager->getStorage($entity->getEntityTypeId());
    $vid = $storage->getLatestRevisionId($entity->id());
    return $storage->loadRevision($vid);
  }

  /**
   * 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;
  }

  /**
   * Get the selection strategy.
   *
   * @param string $entity_type
   *   The entity type.
   *
   * @return \Drupal\access_policy\Plugin\access_policy\SelectionStrategy\SelectionStrategyInterface
   *   The selection strategy plugin.
   */
  public function getSelectionStrategy($entity_type) {
    if (!isset($this->selectionStrategy[$entity_type])) {
      $strategy = $this->getSetting($entity_type, 'selection_strategy');
      $settings = $this->getSetting($entity_type, 'selection_strategy_settings');
      $plugin = $this->selectionStrategyManager->createInstance($strategy, $settings);

      $entity_type_obj = $this->entityTypeManager->getDefinition($entity_type);
      $plugin->setEntityType($entity_type_obj);
      $this->selectionStrategy[$entity_type] = $plugin;
    }
    return $this->selectionStrategy[$entity_type];
  }

  /**
   * Determine whether empty access policies are allowed.
   *
   * @param string $entity_type
   *   The entity type.
   *
   * @return bool
   *   TRUE if empty policies are allowed; FALSE otherwise.
   */
  public function emptyPoliciesAllowed($entity_type) {
    $strategy = $this->getSelectionStrategy($entity_type);
    if ($strategy->getOption('allow_empty')) {
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Determine whether the policy should be updated on entity update.
   *
   * @param string $entity_type
   *   The entity type.
   *
   * @return bool
   *   TRUE if the policy should be updated; FALSE otherwise.
   */
  public function assignPolicyOnUpdate($entity_type) {
    $strategy = $this->getSelectionStrategy($entity_type);
    $dynamic_assignment = $strategy->getOption('dynamic_assignment');
    if ($dynamic_assignment == 'on_save' || $dynamic_assignment == 'on_change') {
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Determine whether the selection page is enabled.
   *
   * @param string $entity_type
   *   The entity type.
   *
   * @return bool
   *   TRUE if the selection page is enabled.
   */
  public function isSelectionPageEnabled($entity_type) {
    $strategy = $this->getSelectionStrategy($entity_type);
    if ($strategy->getOption('enable_selection_page')) {
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Determine whether the operations link is enabled.
   *
   * Note that this can only return true if the selection page is also enabled.
   *
   * @param string $entity_type
   *   The entity type.
   *
   * @return bool
   *   TRUE if the operations link is enabled.
   */
  public function isOperationsLinkEnabled($entity_type) {
    $strategy = $this->getSelectionStrategy($entity_type);
    $selection_page_enabled = $strategy->getOption('enable_selection_page');
    $operation_link_enabled = $strategy->getOption('show_operations_link');
    if ($selection_page_enabled && $operation_link_enabled) {
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Determine whether the assignment field is enabled.
   *
   * @param string $entity_type
   *   The entity type.
   *
   * @return bool
   *   TRUE if the policy should be updated; FALSE otherwise.
   */
  public function isPolicyFieldEnabled($entity_type) {
    $strategy = $this->getSelectionStrategy($entity_type);
    if ($strategy->getOption('enable_policy_field') == TRUE) {
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Get the selection setting value.
   *
   * @param string $entity_type
   *   The entity type.
   * @param string $key
   *   The strategy setting name.
   *
   * @return mixed
   *   The strategy setting value.
   */
  protected function getSetting($entity_type, $key) {
    return $this->getConfig($entity_type)->get($key);
  }

  /**
   * {@inheritdoc}
   */
  public function getSelectionSetFromEntity(EntityInterface $entity) {
    $value = $entity->get('access_policy')->getValue();
    $policy_ids = array_map(function ($item) {
      return $item['target_id'];
    }, $value);
    $access_policies = $this->entityTypeManager->getStorage('access_policy')->loadMultiple($policy_ids);

    $assigned_sets = [];
    foreach ($access_policies as $policy) {
      $set = $policy->getSelectionSet();
      $assigned_sets = array_merge($assigned_sets, $set);
    }

    // No selection sets are assigned.
    if (empty($assigned_sets)) {
      return FALSE;
    }

    // If the count matches the same number of policies that that is the set
    // that policies are from.
    $total_policies = count($access_policies);
    $count_values = array_count_values($assigned_sets);
    $policy = array_search($total_policies, $count_values);

    // If no policy was found then return null set.
    if ($policy === FALSE) {
      return self::NULL_SET;
    }

    return $policy;
  }

  /**
   * {@inheritdoc}
   */
  public function getPoliciesInSelectionSet($entity_type, $name) {
    $policies = $this->entityTypeManager->getStorage('access_policy')->loadByProperties([
      'target_entity_type_id' => $entity_type,
    ]);
    return array_filter($policies, function ($policy) use ($name) {
      $selection_sets = $policy->getSelectionSet();
      if (in_array($name, $selection_sets)) {
        return TRUE;
      }
    });
  }

  /**
   * {@inheritdoc}
   */
  public function getSelectionSets($entity_type) {
    return $this->getConfig($entity_type)->get('selection_sets');
  }

  /**
   * {@inheritdoc}
   */
  public function getSelectionSet($entity_type, $name) {
    $sets = $this->getConfig($entity_type)->get('selection_sets');
    if (isset($sets[$name])) {
      return $sets[$name];
    }

    return FALSE;
  }

  /**
   * Get the access policy entity type settings.
   *
   * @return \Drupal\access_policy\EntityTypeSettingsInterface
   *   A entity type settings.
   */
  private function getConfig($entity_type) {
    return $this->entityTypeSettings->load($entity_type);
  }

}

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

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