3 namespace Drupal\permissions_by_entity\Service;
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;
16 * Class AccessChecker.
18 * @package Drupal\permissions_by_entity\Service
20 class AccessChecker extends AccessCheck implements AccessCheckerInterface {
23 * The event dispatcher.
25 * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
27 private $eventDispatcher;
30 * The cache for checked entities.
32 * @var \Drupal\permissions_by_entity\Service\CheckedEntityCache
34 private $checkedEntityCache;
37 * The entity field value access denied event.
39 * @var \Drupal\permissions_by_entity\Event\EntityFieldValueAccessDeniedEvent
44 * AccessChecker constructor.
46 * We override the constructor, because we do not need the entity manager.
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.
57 public function __construct(
58 EventDispatcherInterface $event_dispatcher,
59 CheckedEntityCache $checked_entity_cache,
60 EntityManagerInterface $entity_manager,
63 parent::__construct($database, $event_dispatcher);
64 $this->eventDispatcher = $event_dispatcher;
65 $this->checkedEntityCache = $checked_entity_cache;
67 $this->event = new EntityFieldValueAccessDeniedEvent();
73 public function isAccessAllowed(ContentEntityInterface $entity, $uid = FALSE) {
74 // Iterate over the fields the entity contains.
75 foreach ($entity->getFields() as $field) {
77 // We only need to check for entity reference fields
78 // which references to a taxonomy term.
80 $field->getFieldDefinition()->getType() == 'entity_reference' &&
81 $field->getFieldDefinition()->getSetting('target_type') == 'taxonomy_term'
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.
89 !empty($item['target_id']) &&
90 !$this->isAccessAllowedByDatabase($item['target_id'], $uid)
92 // Return that the user is not allowed to access this entity.
98 // Check if the field contains another content entity,
99 // that we need to check.
100 if ($field->entity && $field->entity instanceof ContentEntityInterface) {
102 // We need to iterate over the entities.
103 $num_values = $field->count();
104 if ($num_values > 0) {
106 // Iterate over the field values.
107 for ($i = 0; $i < $num_values; $i++) {
109 // Get the value of the current field index.
110 $field_value = $field->get($i);
112 // If the value is null or empty we continue with the next index of
118 // Get the field entity.
119 $field_entity = $field_value->entity;
121 // If the field entity is null we also continue with the next index
123 if (!$field_entity) {
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.
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)) {
140 // Add the current entity to the list of checked entities.
141 $this->checkedEntityCache->add($field_entity);
144 // Do a recursive call to check if the user is allowed to access
146 if (!$this->isAccessAllowed($field_entity, $uid)) {
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);
155 $this->eventDispatcher
157 PermissionsByEntityEvents::ENTITY_FIELD_VALUE_ACCESS_DENIED_EVENT,
160 $i = $this->event->getIndex();