Filters with ACL.

@param AdminInterface      $admin
@param ProxyQueryInterface $query
@param string              $context

@throws \RuntimeException
170530
public function configureQuery(AdminInterface $admin, ProxyQueryInterface $query, $context = 'list')
    {
        // Don't filter for admins and for not ACL enabled classes and for command cli
        if (
            !$admin->isAclEnabled()
            || !$this->tokenStorage->getToken()
            || $admin->isGranted(sprintf($admin->getSecurityHandler()->getBaseRole($admin), 'ADMIN'))
        ) {
            return;
        }

        // Retrieve current logged user SecurityIdentity
        $user = $this->tokenStorage->getToken()->getUser();
        $userSecurityIdentity = UserSecurityIdentity::fromAccount($user);

        // Retrieve current logged user roles
        $userRoles = $user->getRoles();

        // Find child roles
        $roles = array();
        foreach ($userRoles as $userRole) {
            $roles[] = ($userRole instanceof RoleInterface) ? $userRole : new Role($userRole);
        }

        $reachableRoles = $this->roleHierarchy->getReachableRoles($roles);

        // Get identity ACL user identifier
        $identifiers[] = sprintf('%s-%s', $userSecurityIdentity->getClass(), $userSecurityIdentity->getUsername());

        // Get identities ACL roles identifiers
        foreach ($reachableRoles as $reachableRole) {
            $role = $reachableRole->getRole();
            if (!in_array($role, $identifiers)) {
                $identifiers[] = $role;
            }
        }

        $identityStmt = $this->databaseConnection->executeQuery(
            'SELECT id FROM acl_security_identities WHERE identifier IN (?)',
            array($identifiers),
            array(Connection::PARAM_STR_ARRAY)
        );

        $identityIds = array();
        foreach ($identityStmt->fetchAll() as $row) {
            $identityIds[] = $row['id'];
        }

        // Get class ACL identifier
        $classType = $admin->getClass();
        $classStmt = $this->databaseConnection->prepare('SELECT id FROM acl_classes WHERE class_type = :classType');
        $classStmt->bindValue('classType', $classType);
        $classStmt->execute();

        $classId = $classStmt->fetchColumn();

        if (!empty($identityIds) && $classId) {
            $entriesStmt = $this->databaseConnection->executeQuery(
                'SELECT DISTINCT object_identifier FROM acl_entries AS ae JOIN acl_object_identities AS aoi ON ae.object_identity_id = aoi.id WHERE ae.class_id = ? AND ae.security_identity_id IN (?) AND (? = ae.mask & ? OR ? = ae.mask & ? OR ? = ae.mask & ? OR ? = ae.mask & ?)',
                array(
                    $classId,
                    $identityIds,
                    MaskBuilder::MASK_VIEW,
                    MaskBuilder::MASK_VIEW,
                    MaskBuilder::MASK_OPERATOR,
                    MaskBuilder::MASK_OPERATOR,
                    MaskBuilder::MASK_MASTER,
                    MaskBuilder::MASK_MASTER,
                    MaskBuilder::MASK_OWNER,
                    MaskBuilder::MASK_OWNER,
                ),
                array(
                    \PDO::PARAM_INT,
                    Connection::PARAM_INT_ARRAY,
                    \PDO::PARAM_INT,
                    \PDO::PARAM_INT,
                    \PDO::PARAM_INT,
                    \PDO::PARAM_INT,
                    \PDO::PARAM_INT,
                    \PDO::PARAM_INT,
                    \PDO::PARAM_INT,
                    \PDO::PARAM_INT,
                )
            );

            $ids = array();
            foreach ($entriesStmt->fetchAll() as $row) {
                $ids[] = $row['object_identifier'];
            }

            if (count($ids)) {
                $query
                    ->andWhere('o IN (:ids)')
                    ->setParameter('ids', $ids)
                ;

                return;
            }
        }

        // Display an empty list
        $query->andWhere('1 = 2');
    }
401718
protected function get_role_users_for_expired_context(expired_context $expiredctx, \context $context) : \stdClass {
        $expiredroles = $expiredctx->get('expiredroles');
        $expiredroleusers = [];
        if (!empty($expiredroles)) {
            // Find the list of expired role users.
            $expiredroleuserassignments = get_role_users($expiredroles, $context, true, 'ra.id, u.id AS userid', 'ra.id');
            $expiredroleusers = array_map(function($assignment) {
                return $assignment->userid;
            }, $expiredroleuserassignments);
        }
        $expiredroleusers = array_unique($expiredroleusers);

        $unexpiredroles = $expiredctx->get('unexpiredroles');
        $unexpiredroleusers = [];
        if (!empty($unexpiredroles)) {
            // Find the list of unexpired role users.
            $unexpiredroleuserassignments = get_role_users($unexpiredroles, $context, true, 'ra.id, u.id AS userid', 'ra.id');
            $unexpiredroleusers = array_map(function($assignment) {
                return $assignment->userid;
            }, $unexpiredroleuserassignments);
        }
        $unexpiredroleusers = array_unique($unexpiredroleusers);

        if (!$expiredctx->get('defaultexpired')) {
            $tofilter = get_users_roles($context, $expiredroleusers);
            $tofilter = array_filter($tofilter, function($userroles) use ($expiredroles) {
                // Each iteration contains the list of role assignment for a specific user.
                // All roles that the user holds must match those in the list of expired roles.
                foreach ($userroles as $ra) {
                    if (false === array_search($ra->roleid, $expiredroles)) {
                        // This role was not found in the list of assignments.
                        return true;
                    }
                }

                return false;
            });
            $unexpiredroleusers = array_merge($unexpiredroleusers, array_keys($tofilter));
        }

        return (object) [
            'expired' => $expiredroleusers,
            'unexpired' => $unexpiredroleusers,
        ];
    }
901890
public function visit($query, $node, string $context)
    {
        if ($node instanceof $this->node) {
            $bindings = [];
            $sql = [];

            $child0 = $node->getChildByIndex(0);

            if ($child0 instanceof Nodes\KeyNode) {
                $context === Nodes\OrNode::class && $query->orWhereNull($this->parseKey($child0->getValue()));
                $context === Nodes\AndNode::class && $query->whereNull($this->parseKey($child0->getValue()));
            }
        }
    }
1142953
protected function initializeFiltering(ContextInterface $context)
    {
        $activeFilters = $context->getActiveFilters();

        foreach ($activeFilters as $activeFilter) {
            $filterName = $activeFilter->getFilterName();
            if ($this->table->hasFilter($filterName)) {
                $this->table->getFilter($filterName)->applyFilter($this, $activeFilter);
            }
        }
    }
879769
protected function _beforeRepositoryFetch(AnCommandContext $context)
    {
        if (!get_viewer()->admin()) {
            $query = $context->query;
            $query->where('IF(@col(enabled)=FALSE,0,1)');
        }
    }
398267
public function batchActionSnapshot($query)
    {
        if (false === $this->get('sonata.page.admin.snapshot')->isGranted('CREATE')) {
            throw new AccessDeniedException();
        }

        foreach ($query->execute() as $page) {
            $this->get('sonata.notification.backend')
                ->createAndPublish('sonata.page.create_snapshot', [
                    'pageId' => $page->getId(),
                ]);
        }

        return new RedirectResponse($this->admin->generateUrl('list', $this->admin->getFilterParameters()));
    }
372240
public function getCollection(AdminInterface $admin, array $filters = [])
    {
        $adminConfiguration = $admin->getConfiguration();
        $actionConfiguration = $admin->getAction()->getConfiguration();

        // Create a query builder for the configured entity class
        $queryBuilder = $this
            ->getRepository($adminConfiguration->getParameter('entity'))
            ->createQueryBuilder('entity')
        ;

        // Dispatch an event to allow filter alteration on the query builder
        $event = new ORMFilterEvent($queryBuilder, $admin, $filters);
        $this->eventDispatcher->dispatch(Events::DOCTRINE_ORM_FILTER, $event);

        if ('pagerfanta' === $actionConfiguration->getParameter('pager')) {
            $pageParameter = $actionConfiguration->getParameter('page_parameter');
            $request = $this->requestStack->getCurrentRequest();
            $page = (int) $request->get($pageParameter, 1);

            $adapter = new DoctrineORMAdapter($queryBuilder);
            $pager = new Pagerfanta($adapter);
            $pager->setCurrentPage($page);
            $pager->setMaxPerPage($actionConfiguration->getParameter('max_per_page'));
            $entities = $pager;
        } else {
            $entities = $queryBuilder->getQuery()->getResult();
        }

        return $entities;
    }
959631
public function makeQueryFiltered($query)
    {
        $settingTarget = $this->getSettingTarget();
        if ($settingTarget) {
            $query = $query->where([
                ['settable_id', '=', $settingTarget->getKeyName()],
                ['settable_type', '=', $settingTarget->getMorphClass()]
            ]);
        } else {
            $query = $query->whereNull('settable_id')->whereNull('settable_type');
        }
        return $query;
    }
567269
public function runDirectives(QueryInterface $query, array $skipDirective = [])
    {
        foreach ($this->directives as $key => $value) {
            if (in_array($key, $skipDirective, true)) {
                continue;
            }
            if (!$this->hasDirectiveHandler($key)) {
                continue;
            }
            if (is_callable($this->getDirectiveHandler($key))) {
                call_user_func($this->getDirectiveHandler($key), $query, $value, $this->directives);
            }
        }
    }
1139201
public function scopeFromYearsAgo(Builder $query, $ago, $endDateTime = null)
    {
        return $this->scopeFromDateTimeRange(DateTime::getYearsAgo($ago, $endDateTime));
    }
284488
protected function processQuery(Query $query, $context): Query
    {
        $queryProperties = [];

        // Table
        $queryProperties['table'] = $this->processTable($query->table, $context);

        // Select
        foreach ($query->select as $alias => $select) {
            $queryProperties['select'][$alias] = $this->processSelect($select, $context);
        }

        // Insert
        foreach ($query->insert as $index => $insert) {
            $queryProperties['insert'][$index] = $this->processInsert($insert, $context);
        }

        // Update
        foreach ($query->update as $column => $update) {
            $column = $this->processColumnName($column, $context);
            $queryProperties['update'][$column] = $this->processValueOrSubQuery($update, $context);
        }

        // Join
        foreach ($query->join as $index => $join) {
            $queryProperties['join'][$index] = $this->processJoin($join, $context);
        }

        // Where
        foreach ($query->where as $index => $criterion) {
            $queryProperties['where'][$index] = $this->processCriterion($criterion, $context);
        }

        // Order
        foreach ($query->order as $index => $order) {
            $queryProperties['order'][$index] = $this->processOrder($order, $context);
        }

        // Offset
        $queryProperties['offset'] = $this->processValueOrSubQuery($query->offset, $context);

        // Limit
        $queryProperties['limit'] = $this->processValueOrSubQuery($query->limit, $context);

        // Is any property changed?
        $isChanged = false;
        foreach ($queryProperties as $property => $value) {
            if ($value !== $query->$property) {
                $isChanged = true;
                break;
            }
        }

        if ($isChanged) {
            $query = clone $query;
            foreach ($queryProperties as $property => $value) {
                $query->$property = $value;
            }
            return $query;
        } else {
            return $query;
        }
    }
1260737
public function bind(AbstractAdmin $admin, Request $request)
    {
        if ($this->request) {
            throw new \RuntimeException('A datagrid can only be bound once to a request');
        }

        // configure our datagrid
        $resolver = new OptionsResolver();
        $this->configureOptions($resolver);
        $this->options = $resolver->resolve($admin->getDatagridOptions());

        $this->admin = $admin;
        $this->request = $request;

        // attach our fields, filters and actions
        $this->admin->buildDatagrid($this);

        $this->applyFilters();
    }
1203307
private function addRuleCondition(QueryBuilder $query, EntityDefinition $definition, Context $context): void
    {
        if ($definition->isBlacklistAware() && $context->getRuleIds()) {
            $wheres = [];

            $accessor = $this->getFieldAccessor('blacklistIds', $definition, $definition->getEntityName(), $context);

            foreach ($context->getRuleIds() as $ruleId) {
                if (!Uuid::isValid($ruleId)) {
                    throw new InvalidUuidException($ruleId);
                }
                $wheres[] = sprintf(
                    'JSON_CONTAINS(IFNULL(' . $accessor . ', JSON_ARRAY()), JSON_ARRAY(:%s))',
                    'contextRule' . $ruleId
                );
                $query->setParameter('contextRule' . $ruleId, $ruleId);
            }

            $query->andWhere(implode(' + ', $wheres) . ' = 0');
        }

        if (!$definition->isWhitelistAware()) {
            return;
        }

        $accessor = $this->getFieldAccessor('whitelistIds', $definition, $definition->getEntityName(), $context);

        $wheres = [];
        foreach ($context->getRuleIds() as $id) {
            if (!Uuid::isValid($id)) {
                throw new InvalidUuidException($id);
            }
            $wheres[] = sprintf(
                'JSON_CONTAINS(IFNULL(' . $accessor . ', JSON_ARRAY()), JSON_ARRAY(:%s))',
                'contextRule' . $id
            );
            $query->setParameter('contextRule' . $id, $id);
        }

        $conditions = [
            '(JSON_DEPTH(' . $accessor . ') is null)',
            '(JSON_DEPTH(' . $accessor . ') = 1)',
        ];

        if (!empty($wheres)) {
            $conditions[] = implode(' + ', $wheres) . ' >= 1';
        }

        $query->andWhere('(' . implode(' OR ', $conditions) . ')');
    }
1064928
protected function _authorizeRemoveAdmin(AnCommandContext $context)
    {
        if ($this->_entity->isAdministrable()) {
            return ($this->_entity->administratorIds->count() >= 2);
        }

        return false;
    }
615077
protected function queryAddAccessPolicyCondition($access_policy_name, $query)
	{
		// Allow by default
		if (empty($access_policy_name)) {
			return;
		}

		if (!isset($this->access_policies[$access_policy_name])) {
			throw new \InvalidArgumentException('Unknown policy: '.$access_policy_name);
		}
		$access_policy = $this->access_policies[$access_policy_name];

		//debug_dump($access_policy, 'POLICY: '.$access_policy_name.' @ '.get_class($this));

		if ($this->auth->isAllMighty()) {
			// FIXME: Remove hardcoded role name
			return;
		}

		switch ($access_policy['type']) {

			// anyone: Completely open for anyone
			case 'anyone':
				return;

			// anonymous: Only anonymous users allowed (not logged in)
			case 'anonymous':
				$user_id = $this->auth->getUserId();
				$query->where($user_id === null ? 'TRUE' : 'FALSE');
				return;

			// user: All logged-in users allowed
			case 'user':
				$user_id = $this->auth->getUserId();
				$query->where($user_id !== null ? 'TRUE' : 'FALSE');
				return;

			// owner: Owner must match current user
			case 'owner':
				$user_id = $this->auth->getUserId();
				$owner_property = $query->quoteIdent($access_policy['owner_property']);
				$table = $query->quoteIdent($this->table);
				if (isset($access_policy['session_state'])) {
					if ($this->auth->getSessionMachine()->state != $access_policy['session_state']) {
						$query->where('FALSE');
						return;
					}
				}
				if ($user_id === null) {
					$query->where("$table.$owner_property IS NULL");
					return;
				} else {
					$query->where("$table.$owner_property = ?", $user_id);
					return;
				}

			// role: Current user must have specified role ($ref is ignored)
			case 'role':
				$query->where($this->auth->hasUserRoles($access_policy['required_role']) ? 'TRUE' : 'FALSE');
				return;

			// These are done by SQL select.
			case 'condition':
				$this->queryAddSimpleAccessPolicyCondition($access_policy_name, $access_policy, $query, 'where');
				return;

			// These are done by SQL select.
			case 'user_relation':
				$this->queryAddUserRelationAccessPolicyCondition($access_policy_name, $access_policy, $query, 'where');
				return;

			// unknown policies are considered unsafe
			default:
				$query->where('FALSE');
				return;
		}

		// This should not happen.
		throw new \RuntimeException('Policy '.$policy.' did not decide.');
	}
407846
private function retrieveFilterFieldDescription(
        AdminInterface $admin,
        string $field
    ): FieldDescriptionInterface {
        $admin->getFilterFieldDescriptions();

        $fieldDescription = $admin->getFilterFieldDescription($field);

        if (!$fieldDescription) {
            throw new \RuntimeException(sprintf('The field "%s" does not exist.', $field));
        }

        if (null === $fieldDescription->getTargetEntity()) {
            throw new \RuntimeException(sprintf('No associated entity with field "%s".', $field));
        }

        return $fieldDescription;
    }
669275
public function visit($query, NodeContract $node, string $context)
    {
        if ($node instanceof Nodes\KeyNode) {
            $key = $node->getValue();

            $keys = explode('.', $key);

            if (count($keys) === 1) {
                $keys = [$this->getBaseTable(), $keys[0]];
            }

            $key = implode('.', $keys);

            $node->setValue($key);

            if ($this->getKeys()[0] === '*') {
                return;
            }

            if (!in_array($key, $this->getKeys())) {
                throw new QuerySyntaxException();
            }
        }

        foreach ($node->getChildren() as $child) {
            $this->visit($query, $child, $context);
        }
    }