3 namespace Drupal\permissions_by_entity\Service;
5 use Drupal\Core\Database\Connection;
6 use Drupal\Core\Entity\FieldableEntityInterface;
7 use Drupal\Core\Entity\EntityManagerInterface;
8 use Drupal\permissions_by_entity\Event\EntityFieldValueAccessDeniedEvent;
9 use Drupal\permissions_by_entity\Event\PermissionsByEntityEvents;
10 use Drupal\permissions_by_term\Service\AccessCheck;
11 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
14 * Class AccessChecker.
16 * @package Drupal\permissions_by_entity\Service
18 class AccessChecker extends AccessCheck implements AccessCheckerInterface {
21 * The event dispatcher.
23 * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
25 private $eventDispatcher;
28 * The cache for checked entities.
30 * @var \Drupal\permissions_by_entity\Service\CheckedEntityCache
32 private $checkedEntityCache;
35 * The entity field value access denied event.
37 * @var \Drupal\permissions_by_entity\Event\EntityFieldValueAccessDeniedEvent
42 * AccessChecker constructor.
44 * We override the constructor, because we do not need the entity manager.
46 * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
47 * The event dispatcher.
48 * @param \Drupal\permissions_by_entity\Service\CheckedEntityCache $checked_entity_cache
49 * The cache for checked entities.
50 * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
51 * The core entity type manager.
52 * @param \Drupal\Core\Database\Connection $database
53 * The database connection.
55 public function __construct(
56 EventDispatcherInterface $event_dispatcher,
57 CheckedEntityCache $checked_entity_cache,
58 EntityManagerInterface $entity_manager,
61 parent::__construct($database, $event_dispatcher);
62 $this->eventDispatcher = $event_dispatcher;
63 $this->checkedEntityCache = $checked_entity_cache;
65 $this->event = new EntityFieldValueAccessDeniedEvent();
71 public function isAccessAllowed(FieldableEntityInterface $entity, $uid = FALSE) {
72 // Iterate over the fields the entity contains.
73 foreach ($entity->getFields() as $field) {
75 // We only need to check for entity reference fields
76 // which references to a taxonomy term.
78 $field->getFieldDefinition()->getType() == 'entity_reference' &&
79 $field->getFieldDefinition()->getSetting('target_type') == 'taxonomy_term'
82 // Iterate over each referenced taxonomy term.
83 /** @var \Drupal\Core\Field\FieldItemInterface $item */
84 foreach ($field->getValue() as $item) {
85 // Let "Permissions By Term" do the actual check.
87 !empty($item['target_id']) &&
88 !$this->isAccessAllowedByDatabase($item['target_id'], $uid, $entity->language()->getId())
90 // Return that the user is not allowed to access this entity.
96 // Check if the field contains another fieldable entity,
97 // that we need to check.
98 if ($field->entity && $field->entity instanceof FieldableEntityInterface) {
100 // We need to iterate over the entities.
101 $num_values = $field->count();
102 if ($num_values > 0) {
104 // Iterate over the field values.
105 for ($i = 0; $i < $num_values; $i++) {
107 // Get the value of the current field index.
108 $field_value = $field->get($i);
110 // If the value is null or empty we continue with the next index of
116 // Get the field entity.
117 $field_entity = $field_value->entity;
119 // If the field entity is null we also continue with the next index
121 if (!$field_entity) {
125 // It is possible, that the referenced field entity creates a
126 // circular dependency to the current entity. This will cause
127 // memory limit exhausted errors because there is no way out for
128 // the script. To avoid this, we need to be able to imagine if we
129 // already checked this field entity before. If so, we ignore this
130 // field entity, if not we can securely do a recursive call.
132 // Using own method to avoid "max nesting level error" trying to
133 // check if the field entity is stored in the entitiesChecked array.
134 if ($this->checkedEntityCache->isChecked($field_entity)) {
138 // Add the current entity to the list of checked entities.
139 $this->checkedEntityCache->add($field_entity);
142 // Do a recursive call to check if the user is allowed to access
144 if (!$this->isAccessAllowed($field_entity, $uid)) {
146 // Dispatch an event to allow subscribers
147 // to do something in this case.
148 $this->event->setIndex($i);
149 $this->event->setField($field);
150 $this->event->setEntity($field_entity);
151 $this->event->setUid($uid);
153 $this->eventDispatcher
155 PermissionsByEntityEvents::ENTITY_FIELD_VALUE_ACCESS_DENIED_EVENT,
158 $i = $this->event->getIndex();