Security update for permissions_by_term
[yaffs-website] / web / modules / contrib / permissions_by_term / modules / permissions_by_entity / src / Service / AccessChecker.php
1 <?php
2
3 namespace Drupal\permissions_by_entity\Service;
4
5 use Drupal\Core\Database\Connection;
6 use Drupal\Core\Database\Database;
7 use Drupal\Core\Entity\ContentEntityInterface;
8 use Drupal\Core\Entity\EntityManagerInterface;
9 use Drupal\Core\Entity\EntityTypeManagerInterface;
10 use Drupal\permissions_by_entity\Event\EntityFieldValueAccessDeniedEvent;
11 use Drupal\permissions_by_entity\Event\PermissionsByEntityEvents;
12 use Drupal\permissions_by_term\Service\AccessCheck;
13 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
14
15 /**
16  * Class AccessChecker.
17  *
18  * @package Drupal\permissions_by_entity\Service
19  */
20 class AccessChecker extends AccessCheck implements AccessCheckerInterface {
21
22   /**
23    * The event dispatcher.
24    *
25    * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
26    */
27   private $eventDispatcher;
28
29   /**
30    * The cache for checked entities.
31    *
32    * @var \Drupal\permissions_by_entity\Service\CheckedEntityCache
33    */
34   private $checkedEntityCache;
35
36   /**
37    * The entity field value access denied event.
38    *
39    * @var \Drupal\permissions_by_entity\Event\EntityFieldValueAccessDeniedEvent
40    */
41   private $event;
42
43   /**
44    * AccessChecker constructor.
45    *
46    * We override the constructor, because we do not need the entity manager.
47    *
48    * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
49    *   The event dispatcher.
50    * @param \Drupal\permissions_by_entity\Service\CheckedEntityCache $checked_entity_cache
51    *   The cache for checked entities.
52    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
53    *   The core entity type manager.
54    * @param \Drupal\Core\Database\Connection $database
55    *   The database connection.
56    */
57   public function __construct(
58     EventDispatcherInterface $event_dispatcher,
59     CheckedEntityCache $checked_entity_cache,
60     EntityManagerInterface $entity_manager,
61     Connection $database
62   ) {
63     parent::__construct($database, $event_dispatcher);
64     $this->eventDispatcher = $event_dispatcher;
65     $this->checkedEntityCache = $checked_entity_cache;
66
67     $this->event = new EntityFieldValueAccessDeniedEvent();
68   }
69
70   /**
71    * {@inheritdoc}
72    */
73   public function isAccessAllowed(ContentEntityInterface $entity, $uid = FALSE) {
74     // Iterate over the fields the entity contains.
75     foreach ($entity->getFields() as $field) {
76
77       // We only need to check for entity reference fields
78       // which references to a taxonomy term.
79       if (
80         $field->getFieldDefinition()->getType() == 'entity_reference' &&
81         $field->getFieldDefinition()->getSetting('target_type') == 'taxonomy_term'
82       ) {
83
84         // Iterate over each referenced taxonomy term.
85         /** @var \Drupal\Core\Field\FieldItemInterface $item */
86         foreach ($field->getValue() as $item) {
87           // Let "Permissions By Term" do the actual check.
88           if (
89             !empty($item['target_id']) &&
90             !$this->isAccessAllowedByDatabase($item['target_id'], $uid)
91           ) {
92             // Return that the user is not allowed to access this entity.
93             return FALSE;
94           }
95         }
96       }
97
98       // Check if the field contains another content entity,
99       // that we need to check.
100       if ($field->entity && $field->entity instanceof ContentEntityInterface) {
101
102         // We need to iterate over the entities.
103         $num_values = $field->count();
104         if ($num_values > 0) {
105
106           // Iterate over the field values.
107           for ($i = 0; $i < $num_values; $i++) {
108
109             // Get the value of the current field index.
110             $field_value = $field->get($i);
111
112             // If the value is null or empty we continue with the next index of
113             // the loop.
114             if (!$field_value) {
115               continue;
116             }
117
118             // Get the field entity.
119             $field_entity = $field_value->entity;
120
121             // If the field entity is null we also continue with the next index
122             // of the loop.
123             if (!$field_entity) {
124               continue;
125             }
126
127             // It is possible, that the referenced field entity creates a
128             // circular dependency to the current entity. This will cause
129             // memory limit exhausted errors because there is no way out for
130             // the script. To avoid this, we need to be able to imagine if we
131             // already checked this field entity before. If so, we ignore this
132             // field entity, if not we can securely do a recursive call.
133             //
134             // Using own method to avoid "max nesting level error" trying to
135             // check if the field entity is stored in the entitiesChecked array.
136             if ($this->checkedEntityCache->isChecked($field_entity)) {
137               continue;
138             }
139             else {
140               // Add the current entity to the list of checked entities.
141               $this->checkedEntityCache->add($field_entity);
142             }
143
144             // Do a recursive call to check if the user is allowed to access
145             // this entity.
146             if (!$this->isAccessAllowed($field_entity, $uid)) {
147
148               // Dispatch an event to allow subscribers
149               // to do something in this case.
150               $this->event->setIndex($i);
151               $this->event->setField($field);
152               $this->event->setEntity($field_entity);
153               $this->event->setUid($uid);
154
155               $this->eventDispatcher
156                 ->dispatch(
157                   PermissionsByEntityEvents::ENTITY_FIELD_VALUE_ACCESS_DENIED_EVENT,
158                   $this->event
159                 );
160               $i = $this->event->getIndex();
161             }
162           }
163         }
164       }
165     }
166
167     return TRUE;
168   }
169
170 }