Security update for Core, with self-updated composer
[yaffs-website] / web / core / modules / block / src / BlockAccessControlHandler.php
1 <?php
2
3 namespace Drupal\block;
4
5 use Drupal\Component\Plugin\Exception\ContextException;
6 use Drupal\Core\Access\AccessResult;
7 use Drupal\Core\Cache\Cache;
8 use Drupal\Core\Cache\CacheableDependencyInterface;
9 use Drupal\Core\Condition\ConditionAccessResolverTrait;
10 use Drupal\Core\Entity\EntityAccessControlHandler;
11 use Drupal\Core\Entity\EntityHandlerInterface;
12 use Drupal\Core\Entity\EntityInterface;
13 use Drupal\Core\Entity\EntityTypeInterface;
14 use Drupal\Core\Plugin\Context\ContextHandlerInterface;
15 use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
16 use Drupal\Core\Plugin\ContextAwarePluginInterface;
17 use Drupal\Core\Session\AccountInterface;
18 use Symfony\Component\DependencyInjection\ContainerInterface;
19
20 /**
21  * Defines the access control handler for the block entity type.
22  *
23  * @see \Drupal\block\Entity\Block
24  */
25 class BlockAccessControlHandler extends EntityAccessControlHandler implements EntityHandlerInterface {
26
27   use ConditionAccessResolverTrait;
28
29   /**
30    * The plugin context handler.
31    *
32    * @var \Drupal\Core\Plugin\Context\ContextHandlerInterface
33    */
34   protected $contextHandler;
35
36   /**
37    * The context manager service.
38    *
39    * @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface
40    */
41   protected $contextRepository;
42
43   /**
44    * {@inheritdoc}
45    */
46   public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
47     return new static(
48       $entity_type,
49       $container->get('context.handler'),
50       $container->get('context.repository')
51     );
52   }
53
54   /**
55    * Constructs the block access control handler instance
56    *
57    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
58    *   The entity type definition.
59    * @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
60    *   The ContextHandler for applying contexts to conditions properly.
61    * @param \Drupal\Core\Plugin\Context\ContextRepositoryInterface $context_repository
62    *   The lazy context repository service.
63    */
64   public function __construct(EntityTypeInterface $entity_type, ContextHandlerInterface $context_handler, ContextRepositoryInterface $context_repository) {
65     parent::__construct($entity_type);
66     $this->contextHandler = $context_handler;
67     $this->contextRepository = $context_repository;
68   }
69
70
71   /**
72    * {@inheritdoc}
73    */
74   protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
75     /** @var \Drupal\block\BlockInterface $entity */
76     if ($operation != 'view') {
77       return parent::checkAccess($entity, $operation, $account);
78     }
79
80     // Don't grant access to disabled blocks.
81     if (!$entity->status()) {
82       return AccessResult::forbidden()->addCacheableDependency($entity);
83     }
84     else {
85       $conditions = [];
86       $missing_context = FALSE;
87       foreach ($entity->getVisibilityConditions() as $condition_id => $condition) {
88         if ($condition instanceof ContextAwarePluginInterface) {
89           try {
90             $contexts = $this->contextRepository->getRuntimeContexts(array_values($condition->getContextMapping()));
91             $this->contextHandler->applyContextMapping($condition, $contexts);
92           }
93           catch (ContextException $e) {
94             $missing_context = TRUE;
95           }
96         }
97         $conditions[$condition_id] = $condition;
98       }
99
100       if ($missing_context) {
101         // If any context is missing then we might be missing cacheable
102         // metadata, and don't know based on what conditions the block is
103         // accessible or not. For example, blocks that have a node type
104         // condition will have a missing context on any non-node route like the
105         // frontpage.
106         // @todo Avoid setting max-age 0 for some or all cases, for example by
107         //   treating available contexts without value differently in
108         //   https://www.drupal.org/node/2521956.
109         $access = AccessResult::forbidden()->setCacheMaxAge(0);
110       }
111       elseif ($this->resolveConditions($conditions, 'and') !== FALSE) {
112         // Delegate to the plugin.
113         $block_plugin = $entity->getPlugin();
114         try {
115           if ($block_plugin instanceof ContextAwarePluginInterface) {
116             $contexts = $this->contextRepository->getRuntimeContexts(array_values($block_plugin->getContextMapping()));
117             $this->contextHandler->applyContextMapping($block_plugin, $contexts);
118           }
119           $access = $block_plugin->access($account, TRUE);
120         }
121         catch (ContextException $e) {
122           // Setting access to forbidden if any context is missing for the same
123           // reasons as with conditions (described in the comment above).
124           // @todo Avoid setting max-age 0 for some or all cases, for example by
125           //   treating available contexts without value differently in
126           //   https://www.drupal.org/node/2521956.
127           $access = AccessResult::forbidden()->setCacheMaxAge(0);
128         }
129       }
130       else {
131         $access = AccessResult::forbidden();
132       }
133
134       $this->mergeCacheabilityFromConditions($access, $conditions);
135
136       // Ensure that access is evaluated again when the block changes.
137       return $access->addCacheableDependency($entity);
138     }
139   }
140
141   /**
142    * Merges cacheable metadata from conditions onto the access result object.
143    *
144    * @param \Drupal\Core\Access\AccessResult $access
145    *   The access result object.
146    * @param \Drupal\Core\Condition\ConditionInterface[] $conditions
147    *   List of visibility conditions.
148    */
149   protected function mergeCacheabilityFromConditions(AccessResult $access, array $conditions) {
150     foreach ($conditions as $condition) {
151       if ($condition instanceof CacheableDependencyInterface) {
152         $access->addCacheTags($condition->getCacheTags());
153         $access->addCacheContexts($condition->getCacheContexts());
154         $access->setCacheMaxAge(Cache::mergeMaxAges($access->getCacheMaxAge(), $condition->getCacheMaxAge()));
155       }
156     }
157   }
158
159 }