checkItem($item, $params); } /** * @throws BadRequest * @throws Forbidden */ private function checkItem(Item $item, Params $params): void { $type = $item->getType(); $attribute = $item->getAttribute(); $value = $item->getValue(); $forbidComplexExpressions = $params->forbidComplexExpressions(); $checkWherePermission = $params->applyPermissionCheck(); if ($forbidComplexExpressions) { if (in_array($type, $this->subQueryTypeList)) { throw new Forbidden("Sub-queries are forbidden in where."); } } if ($attribute && $forbidComplexExpressions) { if (QueryUtil::isComplexExpression($attribute)) { throw new Forbidden("Complex expressions are forbidden in where."); } } if ($attribute) { $argumentList = QueryComposer::getAllAttributesFromComplexExpression($attribute); foreach ($argumentList as $argument) { $this->checkAttributeExistence($argument, $type); if ($checkWherePermission) { $this->checkAttributePermission($argument, $type); } } } if (in_array($type, $this->nestingTypeList) && is_array($value)) { foreach ($value as $subItem) { $this->checkItem(Item::fromRaw($subItem), $params); } } } /** * @throws BadRequest */ private function checkAttributeExistence(string $attribute, string $type): void { if (str_contains($attribute, '.')) { // @todo Check existence of foreign attributes. return; } if (in_array($type, $this->linkTypeList)) { if (!$this->getSeed()->hasRelation($attribute)) { throw new BadRequest("Not existing relation '{$attribute}' in where."); } return; } if (!$this->getSeed()->hasAttribute($attribute)) { throw new BadRequest("Not existing attribute '{$attribute}' in where."); } } /** * @throws Forbidden */ private function checkAttributePermission(string $attribute, string $type): void { $entityType = $this->entityType; if (str_contains($attribute, '.')) { list($link, $attribute) = explode('.', $attribute); if (!$this->getSeed()->hasRelation($link)) { // TODO allow alias throw new Forbidden("Bad relation '{$link}' in where."); } $foreignEntityType = $this->getRelationParam($this->getSeed(), $link, 'entity'); if (!$foreignEntityType) { throw new Forbidden("Bad relation '{$link}' in where."); } if ( !$this->acl->checkScope($foreignEntityType) || in_array($link, $this->acl->getScopeForbiddenLinkList($entityType)) ) { throw new Forbidden("Forbidden relation '{$link}' in where."); } if (in_array($attribute, $this->acl->getScopeForbiddenAttributeList($foreignEntityType))) { throw new Forbidden("Forbidden attribute '{$link}.{$attribute}' in where."); } return; } if (in_array($type, $this->linkTypeList)) { $link = $attribute; if (!$this->getSeed()->hasRelation($link)) { throw new Forbidden("Bad relation '{$link}' in where."); } $foreignEntityType = $this->getRelationParam($this->getSeed(), $link, 'entity'); if (!$foreignEntityType) { throw new Forbidden("Bad relation '{$link}' in where."); } if ( in_array($link, $this->acl->getScopeForbiddenFieldList($entityType)) || !$this->acl->checkScope($foreignEntityType) || in_array($link, $this->acl->getScopeForbiddenLinkList($entityType)) ) { throw new Forbidden("Forbidden relation '{$link}' in where."); } return; } if (in_array($attribute, $this->acl->getScopeForbiddenAttributeList($entityType))) { throw new Forbidden("Forbidden attribute '{$attribute}' in where."); } } private function getSeed(): Entity { return $this->seed ?? $this->entityManager->getNewEntity($this->entityType); } private function getRelationParam(Entity $entity, string $relation, string $param): mixed { if ($entity instanceof BaseEntity) { return $entity->getRelationParam($relation, $param); } $entityDefs = $this->entityManager ->getDefs() ->getEntity($entity->getEntityType()); if (!$entityDefs->hasRelation($relation)) { return null; } return $entityDefs->getRelation($relation)->getParam($param); } }