diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 9163db0588..e31b2deae8 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -643,6 +643,54 @@ App::get('/v1/users') $cursor->setValue($cursorDocument); } + // Define attributes that have subQueryX filters + $subQueryAttributes = [ + 'authenticators' => 'subQueryAuthenticators', + 'sessions' => 'subQuerySessions', + 'tokens' => 'subQueryTokens', + 'challenges' => 'subQueryChallenges', + 'memberships' => 'subQueryMemberships', + 'targets' => 'subQueryTargets' + ]; + + // Process select queries and identify subQueryX attributes + $skipFilters = ['subQueryAuthenticators', 'subQuerySessions', 'subQueryTokens', 'subQueryChallenges', 'subQueryMemberships', 'subQueryTargets']; + $additionalSkipFilters = []; + + // Process queries to handle select queries with subQueryX attributes + $processedQueries = []; + foreach ($queries as $query) { + if ($query->getMethod() === Query::TYPE_SELECT) { + $selectedAttributes = $query->getValues(); + $filteredAttributes = []; + + foreach ($selectedAttributes as $attribute) { + if (array_key_exists($attribute, $subQueryAttributes)) { + // Add the corresponding subQuery filter to skipFilters + $additionalSkipFilters[] = $subQueryAttributes[$attribute]; + } else { + // Keep this attribute in the select query + $filteredAttributes[] = $attribute; + } + } + + // Only add the select query if there are valid attributes remaining + if (!empty($filteredAttributes)) { + $processedQueries[] = Query::select($filteredAttributes); + } + } else { + // Keep non-select queries as they are + $processedQueries[] = $query; + } + } + + // Update queries with processed queries + $queries = $processedQueries; + + // Add additional skip filters to the existing ones + $skipFilters = array_merge($skipFilters, $additionalSkipFilters); + $skipFilters = array_unique($skipFilters); + $users = []; $total = 0; @@ -655,7 +703,7 @@ App::get('/v1/users') } catch (QueryException $e) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } - }, ['subQueryAuthenticators', 'subQuerySessions', 'subQueryTokens', 'subQueryChallenges', 'subQueryMemberships']); + }, $skipFilters); $response->dynamic(new Document([ 'users' => $users, diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php index 1c5dec44dd..c99a76d31e 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php @@ -11,6 +11,7 @@ use Utopia\Database\Validator\Query\Filter; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\Query\Order; +use Appwrite\Utopia\Database\Validator\Query\Select; class Base extends Queries { @@ -82,6 +83,7 @@ class Base extends Queries new Cursor(), new Filter($attributes, APP_DATABASE_QUERY_MAX_VALUES), new Order($attributes), + new Select($attributes), ]; parent::__construct($validators); diff --git a/src/Appwrite/Utopia/Database/Validator/Query/Select.php b/src/Appwrite/Utopia/Database/Validator/Query/Select.php new file mode 100644 index 0000000000..2afe7cc5d6 --- /dev/null +++ b/src/Appwrite/Utopia/Database/Validator/Query/Select.php @@ -0,0 +1,58 @@ +schema[$attribute->getAttribute('key')] = $attribute->getAttribute('type'); + } + } + + protected function isValidAttribute(string $attribute): bool + { + if (str_starts_with($attribute, '$')) { + return true; // Allow system attributes + } + + return array_key_exists($attribute, $this->schema); + } + + public function isValid(mixed $query): bool + { + if (!$query instanceof \Utopia\Database\Query) { + return false; + } + + if ($query->getMethod() !== Query::TYPE_SELECT) { + return false; + } + + $values = $query->getValues(); + + foreach ($values as $attribute) { + if (!$this->isValidAttribute($attribute)) { + $this->message = 'Query select is not valid: Attribute "' . $attribute . '" not found.'; + return false; + } + } + + return true; + } + + public function getType(): string + { + return Query::TYPE_SELECT; + } +} \ No newline at end of file