diff --git a/app/config/collections/projects.php b/app/config/collections/projects.php index b41e8f0fd5..9568c59369 100644 --- a/app/config/collections/projects.php +++ b/app/config/collections/projects.php @@ -64,7 +64,7 @@ return [ [ '$id' => ID::custom('database'), 'type' => Database::VAR_STRING, - 'size' => 128, + 'size' => 2000, 'required' => false, 'signed' => true, 'array' => false, diff --git a/app/config/sdks.php b/app/config/sdks.php index 13fb444216..47dc8845b6 100644 --- a/app/config/sdks.php +++ b/app/config/sdks.php @@ -250,26 +250,16 @@ return [ ], ], ], - [ - 'key' => 'markdown', - 'name' => 'Markdown', - 'version' => '0.3.0', - 'url' => 'https://github.com/appwrite/sdk-for-md.git', - 'package' => 'https://www.npmjs.com/package/@appwrite.io/docs', - 'enabled' => true, - 'beta' => false, - 'dev' => false, - 'hidden' => false, - 'family' => APP_SDK_PLATFORM_CONSOLE, - 'prism' => 'markdown', - 'source' => \realpath(__DIR__ . '/../sdks/console-md'), - 'gitUrl' => 'git@github.com:appwrite/sdk-for-md.git', - 'gitRepoName' => 'sdk-for-md', - 'gitUserName' => 'appwrite', - 'gitBranch' => 'dev', - 'repoBranch' => 'main', - 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/md/CHANGELOG.md'), - ], + ], + ], + + APP_SDK_PLATFORM_STATIC => [ + 'key' => APP_SDK_PLATFORM_STATIC, + 'name' => 'Static', + 'description' => 'SDK artifacts for Appwrite integrations that do not require a generated platform API specification.', + 'enabled' => true, + 'beta' => false, + 'sdks' => [ [ 'key' => 'agent-skills', 'name' => 'AgentSkills', @@ -279,9 +269,10 @@ return [ 'beta' => false, 'dev' => false, 'hidden' => false, - 'family' => APP_SDK_PLATFORM_CONSOLE, + 'spec' => 'static', + 'family' => APP_SDK_PLATFORM_STATIC, 'prism' => 'agent-skills', - 'source' => \realpath(__DIR__ . '/../sdks/console-agent-skills'), + 'source' => \realpath(__DIR__ . '/../sdks/static-agent-skills'), 'gitUrl' => 'git@github.com:appwrite/agent-skills.git', 'gitRepoName' => 'agent-skills', 'gitUserName' => 'appwrite', @@ -298,9 +289,10 @@ return [ 'beta' => false, 'dev' => false, 'hidden' => false, - 'family' => APP_SDK_PLATFORM_CONSOLE, + 'spec' => 'static', + 'family' => APP_SDK_PLATFORM_STATIC, 'prism' => 'cursor-plugin', - 'source' => \realpath(__DIR__ . '/../sdks/console-cursor-plugin'), + 'source' => \realpath(__DIR__ . '/../sdks/static-cursor-plugin'), 'gitUrl' => 'git@github.com:appwrite/cursor-plugin.git', 'gitRepoName' => 'cursor-plugin', 'gitUserName' => 'appwrite', diff --git a/app/controllers/api/graphql.php b/app/controllers/api/graphql.php index 2d0a840bd6..937380b643 100644 --- a/app/controllers/api/graphql.php +++ b/app/controllers/api/graphql.php @@ -28,12 +28,18 @@ use Utopia\Validator\Text; Http::init() ->groups(['graphql']) ->inject('project') + ->inject('user') + ->inject('request') + ->inject('response') ->inject('authorization') - ->action(function (Document $project, Authorization $authorization) { + ->action(function (Document $project, User $user, Request $request, Response $response, Authorization $authorization) { + $response->setUser($user); + $request->setUser($user); + if ( array_key_exists('graphql', $project->getAttribute('apis', [])) && !$project->getAttribute('apis', [])['graphql'] - && !(User::isPrivileged($authorization->getRoles()) || User::isApp($authorization->getRoles())) + && !($user->isPrivileged($authorization->getRoles()) || $user->isApp($authorization->getRoles())) ) { throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED); } diff --git a/app/controllers/general.php b/app/controllers/general.php index 51cce37fee..79929816d9 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1270,7 +1270,16 @@ Http::error() * If not a publishable error, track usage stats. Publishable errors are >= 500 or those explicitly marked as publish=true in errors.php */ if (!$publish && $project->getId() !== 'console') { - if (!DBUser::isPrivileged($authorization->getRoles())) { + $errorUser = new DBUser(); + try { + $resolvedUser = $utopia->getResource('user'); + if ($resolvedUser instanceof DBUser) { + $errorUser = $resolvedUser; + } + } catch (\Throwable) { + // User resource may not be available in error context + } + if (!$errorUser->isPrivileged($authorization->getRoles())) { $bus->dispatch(new RequestCompleted( project: $project->getArrayCopy(), request: $request, diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index f98b9ed454..5166429e32 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -96,7 +96,7 @@ Http::init() ->inject('team') ->inject('apiKey') ->inject('authorization') - ->action(function (Http $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey, Authorization $authorization) { + ->action(function (Http $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, User $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey, Authorization $authorization) { $route = $utopia->getRoute(); /** @@ -419,7 +419,7 @@ Http::init() if ( array_key_exists($namespace, $project->getAttribute('services', [])) && ! $project->getAttribute('services', [])[$namespace] - && ! (User::isPrivileged($authorization->getRoles()) || User::isApp($authorization->getRoles())) + && ! ($user->isPrivileged($authorization->getRoles()) || $user->isApp($authorization->getRoles())) ) { throw new Exception(Exception::GENERAL_SERVICE_DISABLED); } @@ -483,7 +483,10 @@ Http::init() ->inject('telemetry') ->inject('platform') ->inject('authorization') - ->action(function (Http $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Context $usage, Func $queueForFunctions, Mail $queueForMails, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry, array $platform, Authorization $authorization) { + ->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Context $usage, Func $queueForFunctions, Mail $queueForMails, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry, array $platform, Authorization $authorization) { + + $response->setUser($user); + $request->setUser($user); $route = $utopia->getRoute(); $path = $route->getMatchedPath(); @@ -496,7 +499,7 @@ Http::init() if ( array_key_exists('rest', $project->getAttribute('apis', [])) && ! $project->getAttribute('apis', [])['rest'] - && ! (User::isPrivileged($authorization->getRoles()) || User::isApp($authorization->getRoles())) + && ! ($user->isPrivileged($authorization->getRoles()) || $user->isApp($authorization->getRoles())) ) { throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED); } @@ -528,8 +531,8 @@ Http::init() $closestLimit = null; $roles = $authorization->getRoles(); - $isPrivilegedUser = User::isPrivileged($roles); - $isAppUser = User::isApp($roles); + $isPrivilegedUser = $user->isPrivileged($roles); + $isAppUser = $user->isApp($roles); foreach ($timeLimitArray as $timeLimit) { foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys @@ -611,7 +614,7 @@ Http::init() if ($useCache) { $route = $utopia->match($request); $isImageTransformation = $route->getPath() === '/v1/storage/buckets/:bucketId/files/:fileId/preview'; - $isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && ! User::isPrivileged($authorization->getRoles()); + $isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && ! $user->isPrivileged($authorization->getRoles()); $key = $request->cacheIdentifier(); $cacheLog = $authorization->skip(fn () => $dbForProject->getDocument('cache', $key)); @@ -630,7 +633,7 @@ Http::init() $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); $isToken = ! $resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence(); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (! $bucket->getAttribute('enabled') && ! $isAppUser && ! $isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -663,7 +666,7 @@ Http::init() throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } // Do not update transformedAt if it's a console user - if (! User::isPrivileged($authorization->getRoles())) { + if (! $user->isPrivileged($authorization->getRoles())) { $transformedAt = $file->getAttribute('transformedAt', ''); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) { $file->setAttribute('transformedAt', DateTime::now()); @@ -697,7 +700,7 @@ Http::init() ->groups(['session']) ->inject('user') ->inject('request') - ->action(function (Document $user, Request $request) { + ->action(function (User $user, Request $request) { if (\str_contains($request->getURI(), 'oauth2')) { return; } @@ -984,7 +987,7 @@ Http::shutdown() } if ($project->getId() !== 'console') { - if (! User::isPrivileged($authorization->getRoles())) { + if (! $user->isPrivileged($authorization->getRoles())) { $bus->dispatch(new RequestCompleted( project: $project->getArrayCopy(), request: $request, diff --git a/app/controllers/shared/api/auth.php b/app/controllers/shared/api/auth.php index 6e1f9f389f..db98d97bf5 100644 --- a/app/controllers/shared/api/auth.php +++ b/app/controllers/shared/api/auth.php @@ -36,8 +36,9 @@ Http::init() ->inject('request') ->inject('project') ->inject('geodb') + ->inject('user') ->inject('authorization') - ->action(function (Http $utopia, Request $request, Document $project, Reader $geodb, Authorization $authorization) { + ->action(function (Http $utopia, Request $request, Document $project, Reader $geodb, User $user, Authorization $authorization) { $denylist = System::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', ''); if (!empty($denylist && $project->getId() === 'console')) { $countries = explode(',', $denylist); @@ -50,8 +51,8 @@ Http::init() $route = $utopia->match($request); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); - $isAppUser = User::isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); + $isAppUser = $user->isApp($authorization->getRoles()); if ($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs return; diff --git a/app/init/constants.php b/app/init/constants.php index 8fdd6d1a51..ab88be5854 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -97,6 +97,7 @@ const APP_COMPUTE_DEPLOYMENT_MAX_RETENTION = 100 * 365; // 100 years const APP_SDK_PLATFORM_SERVER = 'server'; const APP_SDK_PLATFORM_CLIENT = 'client'; const APP_SDK_PLATFORM_CONSOLE = 'console'; +const APP_SDK_PLATFORM_STATIC = 'static'; const APP_VCS_GITHUB_USERNAME = 'Appwrite'; const APP_VCS_GITHUB_EMAIL = 'team@appwrite.io'; const APP_VCS_GITHUB_URL = 'https://github.com/TeamAppwrite'; diff --git a/app/init/resources.php b/app/init/resources.php index 9883d6d644..3481e73e0b 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -432,8 +432,10 @@ Http::setResource('user', function (string $mode, Document $project, Document $c $jwtUserId = $payload['userId'] ?? ''; if (! empty($jwtUserId)) { if ($mode === APP_MODE_ADMIN) { + /** @var User $user */ $user = $dbForPlatform->getDocument('users', $jwtUserId); } else { + /** @var User $user */ $user = $dbForProject->getDocument('users', $jwtUserId); } } @@ -453,6 +455,7 @@ Http::setResource('user', function (string $mode, Document $project, Document $c throw new Exception(Exception::USER_API_KEY_AND_SESSION_SET); } + /** @var User $accountKeyUser */ $accountKeyUser = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('users', $accountKeyUserId)); if (! $accountKeyUser->isEmpty()) { $key = $accountKeyUser->find( diff --git a/app/realtime.php b/app/realtime.php index d3305ca7f8..1c453ce05b 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -518,7 +518,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $project = $consoleDatabase->getAuthorization()->skip(fn () => $consoleDatabase->getDocument('projects', $projectId)); $database = getProjectDB($project); - /** @var Appwrite\Utopia\Database\Documents\User $user */ + /** @var User $user */ $user = $database->getDocument('users', $userId); $roles = $user->getRoles($database->getAuthorization()); @@ -642,10 +642,14 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, throw new Exception(Exception::REALTIME_POLICY_VIOLATION, 'Missing or unknown project ID'); } + $timelimit = $app->getResource('timelimit'); + $user = $app->getResource('user'); /** @var User $user */ + $logUser = $user; + if ( array_key_exists('realtime', $project->getAttribute('apis', [])) && !$project->getAttribute('apis', [])['realtime'] - && !(User::isPrivileged($authorization->getRoles()) || User::isApp($authorization->getRoles())) + && !($user->isPrivileged($authorization->getRoles()) || $user->isApp($authorization->getRoles())) ) { throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED); } @@ -656,10 +660,6 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'Project is not accessible in this region. Please make sure you are using the correct endpoint'); } - $timelimit = $app->getResource('timelimit'); - $user = $app->getResource('user'); /** @var User $user */ - $logUser = $user; - /* * Abuse Check * diff --git a/composer.lock b/composer.lock index 17305e6a90..1441bf06d1 100644 --- a/composer.lock +++ b/composer.lock @@ -4002,16 +4002,16 @@ }, { "name": "utopia-php/dns", - "version": "1.6.5", + "version": "1.6.6", "source": { "type": "git", "url": "https://github.com/utopia-php/dns.git", - "reference": "574327f0f5fabefa7048030c5634cde33ad10640" + "reference": "917901ecfe5f09a540e4f689b6cbb80b9f55035d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dns/zipball/574327f0f5fabefa7048030c5634cde33ad10640", - "reference": "574327f0f5fabefa7048030c5634cde33ad10640", + "url": "https://api.github.com/repos/utopia-php/dns/zipball/917901ecfe5f09a540e4f689b6cbb80b9f55035d", + "reference": "917901ecfe5f09a540e4f689b6cbb80b9f55035d", "shasum": "" }, "require": { @@ -4053,9 +4053,9 @@ ], "support": { "issues": "https://github.com/utopia-php/dns/issues", - "source": "https://github.com/utopia-php/dns/tree/1.6.5" + "source": "https://github.com/utopia-php/dns/tree/1.6.6" }, - "time": "2026-02-19T16:06:46+00:00" + "time": "2026-03-27T11:13:50+00:00" }, { "name": "utopia-php/domains", diff --git a/docs/sdks/markdown/CHANGELOG.md b/docs/sdks/markdown/CHANGELOG.md deleted file mode 100644 index 26fcb5bbce..0000000000 --- a/docs/sdks/markdown/CHANGELOG.md +++ /dev/null @@ -1,17 +0,0 @@ -# Change Log - -## 0.3.0 - -* Add `bytesMax` and `bytesUsed` properties to Collection and Table documentation -* Add `queries` parameter to `listKeys` and `keyId` parameter to `createKey` documentation -* Add `dart-3.10` and `flutter-3.38` runtimes -* Fix Teams membership docs to use `string[]` instead of `Roles[]` - -## 0.2.0 - -* Document array-based enum parameters in Markdown examples (e.g., `permissions: BrowserPermission[]`). -* Breaking change: `Output` enum has been removed; use `ImageFormat` instead. - -## 0.1.0 - -* Initial release diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 5e5ab5eef4..37e2579644 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -114,12 +114,6 @@ parameters: count: 1 path: app/controllers/mock.php - - - message: '#^Call to an undefined method Utopia\\Database\\Document\:\:getRoles\(\)\.$#' - identifier: method.notFound - count: 1 - path: app/controllers/shared/api.php - - message: '#^Variable \$register might not be defined\.$#' identifier: variable.undefined diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php index 8d31e19753..a02eb51aba 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php @@ -87,13 +87,14 @@ class Decrement extends Action ->inject('usage') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, Event $queueForEvents, Context $usage, array $plan, Authorization $authorization): void + public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, Event $queueForEvents, Context $usage, array $plan, Authorization $authorization, User $user): void { - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php index 9de5f83154..305d9b7a8d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php @@ -87,13 +87,14 @@ class Increment extends Action ->inject('usage') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, Event $queueForEvents, Context $usage, array $plan, Authorization $authorization): void + public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, Event $queueForEvents, Context $usage, array $plan, Authorization $authorization, User $user): void { - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 08c3b047be..7f2e895228 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -139,7 +139,7 @@ class Create extends Action ->inject('eventProcessor') ->callback($this->action(...)); } - public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, Document $user, Event $queueForEvents, Context $usage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan, Authorization $authorization, EventProcessor $eventProcessor): void + public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, User $user, Event $queueForEvents, Context $usage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan, Authorization $authorization, EventProcessor $eventProcessor): void { $data = \is_string($data) ? \json_decode($data, true) @@ -183,8 +183,8 @@ class Create extends Action $documents = [$data]; } - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if ($isBulk && !$isAPIKey && !$isPrivilegedUser) { throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index 9931109c49..ecc5b152ec 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -85,6 +85,7 @@ class Delete extends Action ->inject('transactionState') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } @@ -101,12 +102,13 @@ class Delete extends Action Context $usage, TransactionState $transactionState, array $plan, - Authorization $authorization + Authorization $authorization, + User $user ): void { $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php index d84eb75a0f..b48df136ee 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php @@ -72,13 +72,14 @@ class Get extends Action ->inject('usage') ->inject('transactionState') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, Context $usage, TransactionState $transactionState, Authorization $authorization): void + public function action(string $databaseId, string $collectionId, string $documentId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, Context $usage, TransactionState $transactionState, Authorization $authorization, User $user): void { - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index f006ad7f59..27ccaafc71 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -89,10 +89,11 @@ class Update extends Action ->inject('transactionState') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, Event $queueForEvents, Context $usage, TransactionState $transactionState, array $plan, Authorization $authorization): void + public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, Event $queueForEvents, Context $usage, TransactionState $transactionState, array $plan, Authorization $authorization, User $user): void { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -102,8 +103,8 @@ class Update extends Action $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index 0dfc64f392..ef89b80e97 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -96,7 +96,7 @@ class Upsert extends Action ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Document $user, Database $dbForProject, callable $getDatabasesDB, Event $queueForEvents, Context $usage, TransactionState $transactionState, array $plan, Authorization $authorization): void + public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, User $user, Database $dbForProject, callable $getDatabasesDB, Event $queueForEvents, Context $usage, TransactionState $transactionState, array $plan, Authorization $authorization): void { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -108,8 +108,8 @@ class Upsert extends Action throw new Exception($this->getMissingPayloadException()); } - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index bc9d30c6f2..744a4fd922 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -83,10 +83,10 @@ class XList extends Action ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, bool $includeTotal, int $ttl, UtopiaResponse $response, Database $dbForProject, Document $user, callable $getDatabasesDB, Context $usage, TransactionState $transactionState, Authorization $authorization): void + public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, bool $includeTotal, int $ttl, UtopiaResponse $response, Database $dbForProject, User $user, callable $getDatabasesDB, Context $usage, TransactionState $transactionState, Authorization $authorization): void { - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php index 26457cc4a0..30c9b7cb30 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php @@ -65,17 +65,18 @@ class Create extends Action ->inject('transactionState') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } - public function action(string $transactionId, array $operations, UtopiaResponse $response, Database $dbForProject, TransactionState $transactionState, array $plan, Authorization $authorization): void + public function action(string $transactionId, array $operations, UtopiaResponse $response, Database $dbForProject, TransactionState $transactionState, array $plan, Authorization $authorization, User $user): void { if (empty($operations)) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Operations array cannot be empty'); } - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); // API keys and admins can read any transaction, regular users need permissions $transaction = ($isAPIKey || $isPrivilegedUser) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 5e88eee500..a5d96d5768 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -91,7 +91,7 @@ class Update extends Action * @param UtopiaResponse $response * @param Database $dbForProject * @param callable $getDatabasesDB - * @param Document $user + * @param User $user * @param TransactionState $transactionState * @param Delete $queueForDeletes * @param Event $queueForEvents @@ -109,7 +109,7 @@ class Update extends Action * @throws Structure * @throws \Utopia\Http\Exception */ - public function action(string $transactionId, bool $commit, bool $rollback, Document $project, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, Document $user, TransactionState $transactionState, Delete $queueForDeletes, Event $queueForEvents, Context $usage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, Authorization $authorization, EventProcessor $eventProcessor): void + public function action(string $transactionId, bool $commit, bool $rollback, Document $project, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, User $user, TransactionState $transactionState, Delete $queueForDeletes, Event $queueForEvents, Context $usage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, Authorization $authorization, EventProcessor $eventProcessor): void { if (!$commit && !$rollback) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Either commit or rollback must be true'); @@ -118,8 +118,8 @@ class Update extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Cannot commit and rollback at the same time'); } - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); $transaction = ($isAPIKey || $isPrivilegedUser) ? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Attribute/Decrement.php index de3acdc96a..6d986fc6b1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Attribute/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Attribute/Decrement.php @@ -68,6 +68,7 @@ class Decrement extends DecrementDocumentAttribute ->inject('usage') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Attribute/Increment.php index 8664bb09ec..09def76941 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Attribute/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Attribute/Increment.php @@ -68,6 +68,7 @@ class Increment extends IncrementDocumentAttribute ->inject('usage') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Delete.php index 86749f8a3d..0253c287aa 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Delete.php @@ -71,6 +71,7 @@ class Delete extends DocumentDelete ->inject('transactionState') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Get.php index 4dd1f6f6b3..47d352bf98 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Get.php @@ -59,6 +59,7 @@ class Get extends DocumentGet ->inject('usage') ->inject('transactionState') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Update.php index b5c612c155..9e79bb5464 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Collections/Documents/Update.php @@ -70,6 +70,7 @@ class Update extends DocumentUpdate ->inject('transactionState') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Transactions/Operations/Create.php index bc15d440d1..963af2f43e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/DocumentsDB/Transactions/Operations/Create.php @@ -55,6 +55,7 @@ class Create extends OperationsCreate ->inject('transactionState') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.php index 2670cc00aa..ea1bfa163d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.php @@ -70,6 +70,7 @@ class Decrement extends DecrementDocumentAttribute ->inject('usage') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php index ca6589aa3a..2f8be876d7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php @@ -70,6 +70,7 @@ class Increment extends IncrementDocumentAttribute ->inject('usage') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php index addc87f610..06aee2cb30 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php @@ -73,6 +73,7 @@ class Delete extends DocumentDelete ->inject('transactionState') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php index 48a24e9ec4..65ae238a1a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php @@ -61,6 +61,7 @@ class Get extends DocumentGet ->inject('usage') ->inject('transactionState') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php index 99599bd169..93ec5d3b58 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php @@ -71,6 +71,7 @@ class Update extends DocumentUpdate ->inject('transactionState') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php index 818ed70cea..b00b75f270 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php @@ -56,6 +56,7 @@ class Create extends OperationsCreate ->inject('transactionState') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Collections/Documents/Delete.php index eca6049970..e81e34e1e5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Collections/Documents/Delete.php @@ -71,6 +71,7 @@ class Delete extends DocumentDelete ->inject('transactionState') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Collections/Documents/Get.php index 2a7090a01e..73f7a55026 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Collections/Documents/Get.php @@ -59,6 +59,7 @@ class Get extends DocumentGet ->inject('usage') ->inject('transactionState') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Collections/Documents/Update.php index 2624cc82e4..8a8b9d89ae 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Collections/Documents/Update.php @@ -70,6 +70,7 @@ class Update extends DocumentUpdate ->inject('transactionState') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Transactions/Operations/Create.php index 830c0c3fe1..27283cda49 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Transactions/Operations/Create.php @@ -55,6 +55,7 @@ class Create extends OperationsCreate ->inject('transactionState') ->inject('plan') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php index ee33abe9e1..c6d25a15fc 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php @@ -119,7 +119,7 @@ class Create extends Base Document $project, Database $dbForProject, Database $dbForPlatform, - Document $user, + User $user, Event $queueForEvents, Context $usage, Func $queueForFunctions, @@ -171,8 +171,8 @@ class Create extends Base /* @var Document $function */ $function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::FUNCTION_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Get.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Get.php index 70912cf58c..aec9d56543 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Get.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Get.php @@ -53,6 +53,7 @@ class Get extends Base ->inject('response') ->inject('dbForProject') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } @@ -61,12 +62,13 @@ class Get extends Base string $executionId, Response $response, Database $dbForProject, - Authorization $authorization + Authorization $authorization, + User $user ) { $function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::FUNCTION_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/XList.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/XList.php index dcc3f6ee9c..b12980b222 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/XList.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/XList.php @@ -62,6 +62,7 @@ class XList extends Base ->inject('response') ->inject('dbForProject') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } @@ -71,12 +72,13 @@ class XList extends Base bool $includeTotal, Response $response, Database $dbForProject, - Authorization $authorization + Authorization $authorization, + User $user ) { $function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::FUNCTION_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php index 827dbc8dd9..c67601dae9 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php @@ -103,7 +103,7 @@ class Create extends Action Request $request, Response $response, Database $dbForProject, - Document $user, + User $user, Event $queueForEvents, string $mode, Device $deviceForFiles, @@ -112,8 +112,8 @@ class Create extends Action ) { $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Delete.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Delete.php index ca376842e2..aa7f14d2ed 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Delete.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Delete.php @@ -66,6 +66,7 @@ class Delete extends Action ->inject('deviceForFiles') ->inject('queueForDeletes') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } @@ -77,12 +78,13 @@ class Delete extends Action Event $queueForEvents, Device $deviceForFiles, DeleteEvent $queueForDeletes, - Authorization $authorization + Authorization $authorization, + User $user ) { $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Download/Get.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Download/Get.php index 042ae76565..c876004319 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Download/Get.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Download/Get.php @@ -70,6 +70,7 @@ class Get extends Action ->inject('resourceToken') ->inject('deviceForFiles') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } @@ -84,12 +85,13 @@ class Get extends Action Document $resourceToken, Device $deviceForFiles, Authorization $authorization, + User $user, ) { /* @type Document $bucket */ $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Get.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Get.php index caaab29efc..c9ce5796eb 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Get.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Get.php @@ -51,6 +51,7 @@ class Get extends Action ->inject('response') ->inject('dbForProject') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } @@ -59,12 +60,13 @@ class Get extends Action string $fileId, Response $response, Database $dbForProject, - Authorization $authorization + Authorization $authorization, + User $user ) { $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Preview/Get.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Preview/Get.php index 63a72fc683..a5e48be478 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Preview/Get.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Preview/Get.php @@ -92,6 +92,7 @@ class Get extends Action ->inject('deviceForLocal') ->inject('project') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } @@ -117,7 +118,8 @@ class Get extends Action Device $deviceForFiles, Device $deviceForLocal, Document $project, - Authorization $authorization + Authorization $authorization, + User $user ) { if (!\extension_loaded('imagick')) { @@ -127,8 +129,8 @@ class Get extends Action /* @type Document $bucket */ $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -271,7 +273,7 @@ class Get extends Action $contentType = (\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg']; //Do not update transformedAt if it's a console user - if (!User::isPrivileged($authorization->getRoles())) { + if (!$user->isPrivileged($authorization->getRoles())) { $transformedAt = $file->getAttribute('transformedAt', ''); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) { $file->setAttribute('transformedAt', DateTime::now()); diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Push/Get.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Push/Get.php index c475c53d24..5b3fd02370 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Push/Get.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Push/Get.php @@ -52,6 +52,7 @@ class Get extends Action ->inject('mode') ->inject('deviceForFiles') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } @@ -66,7 +67,8 @@ class Get extends Action Document $project, string $mode, Device $deviceForFiles, - Authorization $authorization + Authorization $authorization, + User $user ) { $decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0); @@ -88,8 +90,8 @@ class Get extends Action $disposition = $decoded['disposition'] ?? 'inline'; $dbForProject = $isInternal ? $dbForPlatform : $dbForProject; - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Update.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Update.php index 57856c1564..8e69468170 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Update.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Update.php @@ -64,6 +64,7 @@ class Update extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } @@ -75,12 +76,13 @@ class Update extends Action Response $response, Database $dbForProject, Event $queueForEvents, - Authorization $authorization + Authorization $authorization, + User $user ) { $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -108,7 +110,7 @@ class Update extends Action // Users can only manage their own roles, API keys and Admin users can manage any $roles = $authorization->getRoles(); - if (!User::isApp($roles) && !User::isPrivileged($roles) && !\is_null($permissions)) { + if (!$user->isApp($roles) && !$user->isPrivileged($roles) && !\is_null($permissions)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { $permission = Permission::parse($permission); diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/View/Get.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/View/Get.php index cba6c2fa13..b2f00da6d2 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/View/Get.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/View/Get.php @@ -71,6 +71,7 @@ class Get extends Action ->inject('resourceToken') ->inject('deviceForFiles') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } @@ -84,13 +85,14 @@ class Get extends Action string $mode, Document $resourceToken, Device $deviceForFiles, - Authorization $authorization + Authorization $authorization, + User $user ) { /* @type Document $bucket */ $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/XList.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/XList.php index 6de360ae0e..945c4bfd7c 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/XList.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/XList.php @@ -63,6 +63,7 @@ class XList extends Action ->inject('dbForProject') ->inject('mode') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } @@ -74,12 +75,13 @@ class XList extends Action Response $response, Database $dbForProject, string $mode, - Authorization $authorization + Authorization $authorization, + User $user ) { $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php b/src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php index 0632aea3dd..777184f2f2 100644 --- a/src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php +++ b/src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php @@ -98,10 +98,10 @@ class Create extends Action ->callback($this->action(...)); } - public function action(string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Authorization $authorization, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, Context $usage, array $plan, Password $proofForPassword, Token $proofForToken) + public function action(string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, User $user, Database $dbForProject, Authorization $authorization, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, Context $usage, array $plan, Password $proofForPassword, Token $proofForToken) { - $isAppUser = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAppUser = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if (empty($url)) { if (! $isAppUser && ! $isPrivilegedUser) { diff --git a/src/Appwrite/Platform/Modules/Teams/Http/Memberships/Get.php b/src/Appwrite/Platform/Modules/Teams/Http/Memberships/Get.php index 9bfbd8528e..f3fd9a4bb9 100644 --- a/src/Appwrite/Platform/Modules/Teams/Http/Memberships/Get.php +++ b/src/Appwrite/Platform/Modules/Teams/Http/Memberships/Get.php @@ -52,10 +52,11 @@ class Get extends Action ->inject('project') ->inject('dbForProject') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } - public function action(string $teamId, string $membershipId, Response $response, Document $project, Database $dbForProject, Authorization $authorization) + public function action(string $teamId, string $membershipId, Response $response, Document $project, Database $dbForProject, Authorization $authorization, User $user) { $team = $dbForProject->getDocument('teams', $teamId); @@ -76,25 +77,25 @@ class Get extends Action ]; $roles = $authorization->getRoles(); - $isPrivilegedUser = User::isPrivileged($roles); - $isAppUser = User::isApp($roles); + $isPrivilegedUser = $user->isPrivileged($roles); + $isAppUser = $user->isApp($roles); $membershipsPrivacy = array_map(function ($privacy) use ($isPrivilegedUser, $isAppUser) { return $privacy || $isPrivilegedUser || $isAppUser; }, $membershipsPrivacy); - $user = !empty(array_filter($membershipsPrivacy)) + $memberUser = !empty(array_filter($membershipsPrivacy)) ? $dbForProject->getDocument('users', $membership->getAttribute('userId')) : new Document(); if ($membershipsPrivacy['mfa']) { - $mfa = $user->getAttribute('mfa', false); + $mfa = $memberUser->getAttribute('mfa', false); if ($mfa) { - $totp = TOTP::getAuthenticatorFromUser($user); + $totp = TOTP::getAuthenticatorFromUser($memberUser); $totpEnabled = $totp && $totp->getAttribute('verified', false); - $emailEnabled = $user->getAttribute('email', false) && $user->getAttribute('emailVerification', false); - $phoneEnabled = $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false); + $emailEnabled = $memberUser->getAttribute('email', false) && $memberUser->getAttribute('emailVerification', false); + $phoneEnabled = $memberUser->getAttribute('phone', false) && $memberUser->getAttribute('phoneVerification', false); if (!$totpEnabled && !$emailEnabled && !$phoneEnabled) { $mfa = false; @@ -105,11 +106,11 @@ class Get extends Action } if ($membershipsPrivacy['userName']) { - $membership->setAttribute('userName', $user->getAttribute('name')); + $membership->setAttribute('userName', $memberUser->getAttribute('name')); } if ($membershipsPrivacy['userEmail']) { - $membership->setAttribute('userEmail', $user->getAttribute('email')); + $membership->setAttribute('userEmail', $memberUser->getAttribute('email')); } $membership->setAttribute('teamName', $team->getAttribute('name')); diff --git a/src/Appwrite/Platform/Modules/Teams/Http/Memberships/Update.php b/src/Appwrite/Platform/Modules/Teams/Http/Memberships/Update.php index a935055163..540dc8a871 100644 --- a/src/Appwrite/Platform/Modules/Teams/Http/Memberships/Update.php +++ b/src/Appwrite/Platform/Modules/Teams/Http/Memberships/Update.php @@ -66,7 +66,7 @@ class Update extends Action ->callback($this->action(...)); } - public function action(string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Authorization $authorization, Event $queueForEvents) + public function action(string $teamId, string $membershipId, array $roles, Request $request, Response $response, User $user, Document $project, Database $dbForProject, Authorization $authorization, Event $queueForEvents) { $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { @@ -83,8 +83,8 @@ class Update extends Action throw new Exception(Exception::USER_NOT_FOUND); } - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); - $isAppUser = User::isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); + $isAppUser = $user->isApp($authorization->getRoles()); $isOwner = $authorization->hasRole('team:' . $team->getId() . '/owner'); if ($project->getId() === 'console') { diff --git a/src/Appwrite/Platform/Modules/Teams/Http/Memberships/XList.php b/src/Appwrite/Platform/Modules/Teams/Http/Memberships/XList.php index ba59f48b43..364f92e1c5 100644 --- a/src/Appwrite/Platform/Modules/Teams/Http/Memberships/XList.php +++ b/src/Appwrite/Platform/Modules/Teams/Http/Memberships/XList.php @@ -61,10 +61,11 @@ class XList extends Action ->inject('project') ->inject('dbForProject') ->inject('authorization') + ->inject('user') ->callback($this->action(...)); } - public function action(string $teamId, array $queries, string $search, bool $includeTotal, Response $response, Document $project, Database $dbForProject, Authorization $authorization) + public function action(string $teamId, array $queries, string $search, bool $includeTotal, Response $response, Document $project, Database $dbForProject, Authorization $authorization, User $user) { $team = $dbForProject->getDocument('teams', $teamId); @@ -129,26 +130,26 @@ class XList extends Action ]; $roles = $authorization->getRoles(); - $isPrivilegedUser = User::isPrivileged($roles); - $isAppUser = User::isApp($roles); + $isPrivilegedUser = $user->isPrivileged($roles); + $isAppUser = $user->isApp($roles); $membershipsPrivacy = array_map(function ($privacy) use ($isPrivilegedUser, $isAppUser) { return $privacy || $isPrivilegedUser || $isAppUser; }, $membershipsPrivacy); $memberships = array_map(function ($membership) use ($dbForProject, $team, $membershipsPrivacy) { - $user = !empty(array_filter($membershipsPrivacy)) + $memberUser = !empty(array_filter($membershipsPrivacy)) ? $dbForProject->getDocument('users', $membership->getAttribute('userId')) : new Document(); if ($membershipsPrivacy['mfa']) { - $mfa = $user->getAttribute('mfa', false); + $mfa = $memberUser->getAttribute('mfa', false); if ($mfa) { - $totp = TOTP::getAuthenticatorFromUser($user); + $totp = TOTP::getAuthenticatorFromUser($memberUser); $totpEnabled = $totp && $totp->getAttribute('verified', false); - $emailEnabled = $user->getAttribute('email', false) && $user->getAttribute('emailVerification', false); - $phoneEnabled = $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false); + $emailEnabled = $memberUser->getAttribute('email', false) && $memberUser->getAttribute('emailVerification', false); + $phoneEnabled = $memberUser->getAttribute('phone', false) && $memberUser->getAttribute('phoneVerification', false); if (!$totpEnabled && !$emailEnabled && !$phoneEnabled) { $mfa = false; @@ -159,11 +160,11 @@ class XList extends Action } if ($membershipsPrivacy['userName']) { - $membership->setAttribute('userName', $user->getAttribute('name')); + $membership->setAttribute('userName', $memberUser->getAttribute('name')); } if ($membershipsPrivacy['userEmail']) { - $membership->setAttribute('userEmail', $user->getAttribute('email')); + $membership->setAttribute('userEmail', $memberUser->getAttribute('email')); } $membership->setAttribute('teamName', $team->getAttribute('name')); diff --git a/src/Appwrite/Platform/Modules/Teams/Http/Teams/Create.php b/src/Appwrite/Platform/Modules/Teams/Http/Teams/Create.php index ae20017e76..0d20a58b6b 100644 --- a/src/Appwrite/Platform/Modules/Teams/Http/Teams/Create.php +++ b/src/Appwrite/Platform/Modules/Teams/Http/Teams/Create.php @@ -68,10 +68,10 @@ class Create extends Action ->callback($this->action(...)); } - public function action(string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Authorization $authorization, Event $queueForEvents) + public function action(string $teamId, string $name, array $roles, Response $response, User $user, Database $dbForProject, Authorization $authorization, Event $queueForEvents) { - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); - $isAppUser = User::isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); + $isAppUser = $user->isApp($authorization->getRoles()); $teamId = $teamId == 'unique()' ? ID::unique() : $teamId; diff --git a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Action.php b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Action.php index 5f1bd55788..934074d3c2 100644 --- a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Action.php +++ b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Action.php @@ -11,12 +11,12 @@ use Utopia\Platform\Action as UtopiaAction; class Action extends UtopiaAction { - protected function getFileAndBucket(Database $dbForProject, Authorization $authorization, string $bucketId, string $fileId): array + protected function getFileAndBucket(Database $dbForProject, Authorization $authorization, User $user, string $bucketId, string $fileId): array { $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = User::isApp($authorization->getRoles()); - $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAPIKey = $user->isApp($authorization->getRoles()); + $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php index 10257d3603..1c2bd692ef 100644 --- a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php +++ b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php @@ -8,6 +8,7 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response; use Utopia\Auth\Proofs\Token; use Utopia\Database\Database; @@ -64,19 +65,20 @@ class Create extends Action ->param('fileId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'File unique ID.', false, ['dbForProject']) ->param('expire', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Token expiry date', true) ->inject('response') + ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') ->inject('authorization') ->callback($this->action(...)); } - public function action(string $bucketId, string $fileId, ?string $expire, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void + public function action(string $bucketId, string $fileId, ?string $expire, Response $response, User $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void { /** * @var Document $bucket * @var Document $file */ - ['bucket' => $bucket, 'file' => $file] = $this->getFileAndBucket($dbForProject, $authorization, $bucketId, $fileId); + ['bucket' => $bucket, 'file' => $file] = $this->getFileAndBucket($dbForProject, $authorization, $user, $bucketId, $fileId); $fileSecurity = $bucket->getAttribute('fileSecurity', false); $bucketPermission = $authorization->isValid(new Input(Database::PERMISSION_UPDATE, $bucket->getUpdate())); diff --git a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/XList.php b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/XList.php index 3d7be9bf81..5a93a06f26 100644 --- a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/XList.php +++ b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/XList.php @@ -7,6 +7,7 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Database\Validator\Queries\FileTokens; use Appwrite\Utopia\Response; use Utopia\Database\Database; @@ -57,14 +58,15 @@ class XList extends Action ->param('queries', [], new FileTokens(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', FileTokens::ALLOWED_ATTRIBUTES), true) ->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true) ->inject('response') + ->inject('user') ->inject('dbForProject') ->inject('authorization') ->callback($this->action(...)); } - public function action(string $bucketId, string $fileId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Authorization $authorization) + public function action(string $bucketId, string $fileId, array $queries, bool $includeTotal, Response $response, User $user, Database $dbForProject, Authorization $authorization) { - ['bucket' => $bucket, 'file' => $file] = $this->getFileAndBucket($dbForProject, $authorization, $bucketId, $fileId); + ['bucket' => $bucket, 'file' => $file] = $this->getFileAndBucket($dbForProject, $authorization, $user, $bucketId, $fileId); $queries = Query::parseQueries($queries); $queries[] = Query::equal('resourceType', [TOKENS_RESOURCE_TYPE_FILES]); diff --git a/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/Detections/Create.php b/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/Detections/Create.php index bada6d98bb..5dd5c6dcfa 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/Detections/Create.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/Detections/Create.php @@ -82,12 +82,11 @@ class Create extends Action responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, - model: Response::MODEL_DETECTION_RUNTIME, + model: [ + Response::MODEL_DETECTION_RUNTIME, + Response::MODEL_DETECTION_FRAMEWORK, + ], ), - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_DETECTION_FRAMEWORK, - ) ] )) ->param('installationId', '', new Text(256), 'Installation Id') diff --git a/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/XList.php b/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/XList.php index ca2c812901..d5b2b48175 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/XList.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/XList.php @@ -86,12 +86,11 @@ class XList extends Action responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, - model: Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST, + model: [ + Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST, + Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST, + ], ), - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST, - ) ] )) ->param('installationId', '', new Text(256), 'Installation Id') diff --git a/src/Appwrite/Platform/Tasks/SDKs.php b/src/Appwrite/Platform/Tasks/SDKs.php index a7a5f0278f..a36959af33 100644 --- a/src/Appwrite/Platform/Tasks/SDKs.php +++ b/src/Appwrite/Platform/Tasks/SDKs.php @@ -14,7 +14,6 @@ use Appwrite\SDK\Language\Flutter; use Appwrite\SDK\Language\Go; use Appwrite\SDK\Language\GraphQL; use Appwrite\SDK\Language\Kotlin; -use Appwrite\SDK\Language\Markdown; use Appwrite\SDK\Language\Node; use Appwrite\SDK\Language\PHP; use Appwrite\SDK\Language\Python; @@ -25,6 +24,7 @@ use Appwrite\SDK\Language\Rust; use Appwrite\SDK\Language\Swift; use Appwrite\SDK\Language\Web; use Appwrite\SDK\SDK; +use Appwrite\Spec\StaticSpec; use Appwrite\Spec\Swagger2; use CzProject\GitPhp\Git; use Utopia\Agents\Adapters\OpenAI; @@ -50,7 +50,10 @@ class SDKs extends Action public static function getPlatforms(): array { - return Specs::getPlatforms(); + return [ + ...Specs::getPlatforms(), + APP_SDK_PLATFORM_STATIC, + ]; } protected function getSdkConfigPath(): string @@ -99,11 +102,14 @@ class SDKs extends Action } else { $sdks = explode(',', $sdks); } - $version ??= Console::confirm('Choose an Appwrite version'); $createRelease = ($release === 'yes'); $commitRelease = ($commit === 'yes'); + if ($createRelease && $examplesOnly) { + throw new \Exception('Cannot use --release=yes with --mode=examples'); + } + if (! $createRelease && ! $examplesOnly) { $git ??= Console::confirm('Should we use git push? (yes/no)'); $git = ($git === 'yes'); @@ -115,30 +121,34 @@ class SDKs extends Action $prUrls = []; } - if (! \in_array($version, [ - '0.6.x', - '0.7.x', - '0.8.x', - '0.9.x', - '0.10.x', - '0.11.x', - '0.12.x', - '0.13.x', - '0.14.x', - '0.15.x', - '1.0.x', - '1.1.x', - '1.2.x', - '1.3.x', - '1.4.x', - '1.5.x', - '1.6.x', - '1.7.x', - '1.8.x', - '1.9.x', - 'latest', - ])) { - throw new \Exception('Unknown version given'); + if (! $createRelease) { + $version ??= Console::confirm('Choose an Appwrite version'); + + if (! \in_array($version, [ + '0.6.x', + '0.7.x', + '0.8.x', + '0.9.x', + '0.10.x', + '0.11.x', + '0.12.x', + '0.13.x', + '0.14.x', + '0.15.x', + '1.0.x', + '1.1.x', + '1.2.x', + '1.3.x', + '1.4.x', + '1.5.x', + '1.6.x', + '1.7.x', + '1.8.x', + '1.9.x', + 'latest', + ])) { + throw new \Exception('Unknown version given'); + } } $selectedPlatforms = ($selectedPlatform === '*' || $selectedPlatform === null) ? null : \array_map('trim', \explode(',', $selectedPlatform)); @@ -170,16 +180,140 @@ class SDKs extends Action } Console::log(''); - Console::info("━━━ {$language['name']} SDK ({$platform['name']}, {$version}) ━━━"); - Console::log(' Fetching API spec...'); - $specPath = __DIR__ . '/../../../../app/config/specs/swagger2-' . $version . '-' . $language['family'] . '.json'; + if ($createRelease && ! $examplesOnly) { + Console::info("━━━ {$language['name']} SDK ({$platform['name']}, {$language['version']}) ━━━"); + $changelog = $language['changelog'] ?? ''; + $changelog = ($changelog) ? \file_get_contents($changelog) : '# Change Log'; - if (!file_exists($specPath)) { - throw new \Exception('Spec file not found: ' . $specPath . '. Please run "docker compose exec appwrite specs --version=' . $version . '" first to generate the specs.'); + $repoName = $language['gitUserName'] . '/' . $language['gitRepoName']; + $releaseVersion = $language['version']; + $releaseNotes = $this->extractReleaseNotes($changelog, $releaseVersion); + + if (empty($releaseNotes)) { + $releaseNotes = "Release version {$releaseVersion}"; + } + + $releaseTitle = $releaseVersion; + $releaseTarget = $language['repoBranch'] ?? 'main'; + + if ($repoName === '/') { + Console::warning(' Not a releasable SDK, skipping'); + + continue; + } + + // Check if release already exists + $checkReleaseCommand = 'gh release view ' . \escapeshellarg($releaseVersion) . ' --repo ' . \escapeshellarg($repoName) . ' --json url --jq ".url" 2>/dev/null'; + $existingReleaseUrl = trim(\shell_exec($checkReleaseCommand) ?? ''); + + if (! empty($existingReleaseUrl)) { + Console::warning(" Release {$releaseVersion} already exists, skipping"); + Console::log(" {$existingReleaseUrl}"); + + continue; + } + + // Check if the latest commit on the target branch already has a release + $latestCommitCommand = 'gh api repos/' . $repoName . '/commits/' . $releaseTarget . ' --jq ".sha" 2>/dev/null'; + $latestCommitSha = trim(\shell_exec($latestCommitCommand) ?? ''); + + if (! empty($latestCommitSha)) { + $latestReleaseTagCommand = 'gh api repos/' . $repoName . '/releases --jq ".[0] | .tag_name" 2>/dev/null'; + $latestReleaseTag = trim(\shell_exec($latestReleaseTagCommand) ?? ''); + + if (! empty($latestReleaseTag)) { + $tagCommitCommand = 'gh api repos/' . $repoName . '/git/ref/tags/' . $latestReleaseTag . ' --jq ".object.sha" 2>/dev/null'; + $tagCommitSha = trim(\shell_exec($tagCommitCommand) ?? ''); + + if (! empty($tagCommitSha) && $latestCommitSha === $tagCommitSha) { + Console::warning(" Latest commit already released ({$latestReleaseTag}), skipping"); + + continue; + } + } + } + + $previousVersion = ''; + $tagListCommand = 'gh release list --repo ' . \escapeshellarg($repoName) . ' --limit 1 --json tagName --jq ".[0].tagName" 2>&1'; + $previousVersion = trim(\shell_exec($tagListCommand) ?? ''); + + $formattedNotes = "## What's Changed\n\n"; + $formattedNotes .= $releaseNotes . "\n\n"; + + if (! empty($previousVersion)) { + $formattedNotes .= '**Full Changelog**: https://github.com/' . $repoName . '/compare/' . $previousVersion . '...' . $releaseVersion; + } else { + $formattedNotes .= '**Full Changelog**: https://github.com/' . $repoName . '/releases/tag/' . $releaseVersion; + } + + if (! $commitRelease) { + Console::info(' [DRY RUN] Would create release:'); + Console::log(" Repository: {$repoName}"); + Console::log(" Version: {$releaseVersion}"); + Console::log(" Title: {$releaseTitle}"); + Console::log(" Target Branch: {$releaseTarget}"); + Console::log(' Previous Version: ' . ($previousVersion ?: 'N/A')); + Console::log(' Release Notes:'); + Console::log(' ' . str_replace("\n", "\n ", $formattedNotes)); + } else { + Console::log(" Creating release {$releaseVersion}..."); + + $tempNotesFile = \tempnam(\sys_get_temp_dir(), 'release_notes_'); + \file_put_contents($tempNotesFile, $formattedNotes); + + $releaseCommand = 'gh release create ' . \escapeshellarg($releaseVersion) . ' \ + --repo ' . \escapeshellarg($repoName) . ' \ + --title ' . \escapeshellarg($releaseTitle) . ' \ + --notes-file ' . \escapeshellarg($tempNotesFile) . ' \ + --target ' . \escapeshellarg($releaseTarget) . ' \ + 2>&1'; + + $releaseOutput = []; + $releaseReturnCode = 0; + \exec($releaseCommand, $releaseOutput, $releaseReturnCode); + + \unlink($tempNotesFile); + + if ($releaseReturnCode === 0) { + // Extract release URL from output + $releaseUrl = ''; + foreach ($releaseOutput as $line) { + if (strpos($line, 'https://github.com/') !== false) { + $releaseUrl = trim($line); + break; + } + } + + Console::success(" Release {$releaseVersion} created"); + if (! empty($releaseUrl)) { + Console::log(" {$releaseUrl}"); + } + } else { + $errorMessage = implode("\n", $releaseOutput); + Console::error(" Failed to create release: " . $errorMessage); + } + } + + continue; } - $spec = file_get_contents($specPath); + Console::info("━━━ {$language['name']} SDK ({$platform['name']}, {$version}) ━━━"); + $specFormat = $language['spec'] ?? 'swagger2'; + $spec = null; + if ($specFormat === 'static') { + Console::log(' Using static SDK spec...'); + } else { + Console::log(' Fetching API spec...'); + + $specPath = __DIR__ . '/../../../../app/config/specs/swagger2-' . $version . '-' . $language['family'] . '.json'; + + if (!file_exists($specPath)) { + throw new \Exception('Spec file not found: ' . $specPath . '. Please run "docker compose exec appwrite specs --version=' . $version . '" first to generate the specs.'); + } + + $spec = file_get_contents($specPath); + } $cover = 'https://github.com/appwrite/appwrite/raw/main/public/images/github.png'; $result = \realpath(__DIR__ . '/../../../../app') . '/sdks/' . $key . '-' . $language['key']; @@ -311,10 +445,6 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND case 'rest': $config = new REST(); break; - case 'markdown': - $config = new Markdown(); - $config->setNPMPackage('@appwrite.io/docs'); - break; case 'agent-skills': $config = new AgentSkills(); break; @@ -325,124 +455,22 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND throw new \Exception('Language "' . $language['key'] . '" not supported'); } - if ($createRelease && ! $examplesOnly) { - $repoName = $language['gitUserName'] . '/' . $language['gitRepoName']; - $releaseVersion = $language['version']; - $releaseNotes = $this->extractReleaseNotes($changelog, $releaseVersion); - - if (empty($releaseNotes)) { - $releaseNotes = "Release version {$releaseVersion}"; - } - - $releaseTitle = $releaseVersion; - $releaseTarget = $language['repoBranch'] ?? 'main'; - - if ($repoName === '/') { - Console::warning(' Not a releasable SDK, skipping'); - - continue; - } - - // Check if release already exists - $checkReleaseCommand = 'gh release view ' . \escapeshellarg($releaseVersion) . ' --repo ' . \escapeshellarg($repoName) . ' --json url --jq ".url" 2>/dev/null'; - $existingReleaseUrl = trim(\shell_exec($checkReleaseCommand) ?? ''); - - if (! empty($existingReleaseUrl)) { - Console::warning(" Release {$releaseVersion} already exists, skipping"); - Console::log(" {$existingReleaseUrl}"); - - continue; - } - - // Check if the latest commit on the target branch already has a release - $latestCommitCommand = 'gh api repos/' . $repoName . '/commits/' . $releaseTarget . ' --jq ".sha" 2>/dev/null'; - $latestCommitSha = trim(\shell_exec($latestCommitCommand) ?? ''); - - if (! empty($latestCommitSha)) { - $latestReleaseTagCommand = 'gh api repos/' . $repoName . '/releases --jq ".[0] | .tag_name" 2>/dev/null'; - $latestReleaseTag = trim(\shell_exec($latestReleaseTagCommand) ?? ''); - - if (! empty($latestReleaseTag)) { - $tagCommitCommand = 'gh api repos/' . $repoName . '/git/ref/tags/' . $latestReleaseTag . ' --jq ".object.sha" 2>/dev/null'; - $tagCommitSha = trim(\shell_exec($tagCommitCommand) ?? ''); - - if (! empty($tagCommitSha) && $latestCommitSha === $tagCommitSha) { - Console::warning(" Latest commit already released ({$latestReleaseTag}), skipping"); - - continue; - } - } - } - - $previousVersion = ''; - $tagListCommand = 'gh release list --repo ' . \escapeshellarg($repoName) . ' --limit 1 --json tagName --jq ".[0].tagName" 2>&1'; - $previousVersion = trim(\shell_exec($tagListCommand) ?? ''); - - $formattedNotes = "## What's Changed\n\n"; - $formattedNotes .= $releaseNotes . "\n\n"; - - if (! empty($previousVersion)) { - $formattedNotes .= '**Full Changelog**: https://github.com/' . $repoName . '/compare/' . $previousVersion . '...' . $releaseVersion; - } else { - $formattedNotes .= '**Full Changelog**: https://github.com/' . $repoName . '/releases/tag/' . $releaseVersion; - } - - if (! $commitRelease) { - Console::info(' [DRY RUN] Would create release:'); - Console::log(" Repository: {$repoName}"); - Console::log(" Version: {$releaseVersion}"); - Console::log(" Title: {$releaseTitle}"); - Console::log(" Target Branch: {$releaseTarget}"); - Console::log(' Previous Version: ' . ($previousVersion ?: 'N/A')); - Console::log(' Release Notes:'); - Console::log(' ' . str_replace("\n", "\n ", $formattedNotes)); - } else { - Console::log(" Creating release {$releaseVersion}..."); - - $tempNotesFile = \tempnam(\sys_get_temp_dir(), 'release_notes_'); - \file_put_contents($tempNotesFile, $formattedNotes); - - $releaseCommand = 'gh release create ' . \escapeshellarg($releaseVersion) . ' \ - --repo ' . \escapeshellarg($repoName) . ' \ - --title ' . \escapeshellarg($releaseTitle) . ' \ - --notes-file ' . \escapeshellarg($tempNotesFile) . ' \ - --target ' . \escapeshellarg($releaseTarget) . ' \ - 2>&1'; - - $releaseOutput = []; - $releaseReturnCode = 0; - \exec($releaseCommand, $releaseOutput, $releaseReturnCode); - - \unlink($tempNotesFile); - - if ($releaseReturnCode === 0) { - // Extract release URL from output - $releaseUrl = ''; - foreach ($releaseOutput as $line) { - if (strpos($line, 'https://github.com/') !== false) { - $releaseUrl = trim($line); - break; - } - } - - Console::success(" Release {$releaseVersion} created"); - if (! empty($releaseUrl)) { - Console::log(" {$releaseUrl}"); - } - } else { - $errorMessage = implode("\n", $releaseOutput); - Console::error(" Failed to create release: " . $errorMessage); - } - } - - continue; - } - Console::log($examplesOnly ? ' Generating examples...' : ' Generating SDK...'); - $sdk = new SDK($config, new Swagger2($spec)); + $sdk = new SDK( + $config, + $specFormat === 'static' + ? new StaticSpec( + title: 'Appwrite', + description: 'Appwrite backend as a service', + version: $version, + licenseName: 'BSD-3-Clause', + licenseURL: 'https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE', + ) + : new Swagger2($spec) + ); $sdk ->setName($language['name']) @@ -483,6 +511,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND try { $sdk->generate($result); + Console::success($examplesOnly + ? " Examples generated at {$result}" + : " SDK generated at {$result}"); } catch (\Throwable $exception) { Console::error($exception->getMessage()); } diff --git a/src/Appwrite/Platform/Tasks/Specs.php b/src/Appwrite/Platform/Tasks/Specs.php index 606c03bf10..6453977a39 100644 --- a/src/Appwrite/Platform/Tasks/Specs.php +++ b/src/Appwrite/Platform/Tasks/Specs.php @@ -347,6 +347,13 @@ class Specs extends Action $keys = $this->getKeys(); $generatedFiles = []; + $endpoint = System::getEnv('_APP_HOME', '[HOSTNAME]'); + $email = System::getEnv('_APP_SYSTEM_TEAM_EMAIL', APP_EMAIL_TEAM); + $specsDir = __DIR__ . '/../../../../app/config/specs'; + + if (!is_dir($specsDir) && !@mkdir($specsDir, 0755, true) && !is_dir($specsDir)) { + throw new Exception('Failed to create specs directory: ' . $specsDir); + } foreach ($platforms as $platform) { $routes = []; @@ -443,8 +450,6 @@ class Specs extends Action foreach (['swagger2', 'open-api3'] as $format) { $formatInstance = $this->getFormatInstance($format, $arguments); $specs = new Specification($formatInstance); - $endpoint = System::getEnv('_APP_HOME', '[HOSTNAME]'); - $email = System::getEnv('_APP_SYSTEM_TEAM_EMAIL', APP_EMAIL_TEAM); $formatInstance ->setParam('name', APP_NAME) @@ -463,36 +468,30 @@ class Specs extends Action ->setParam('docs.description', 'Full API docs, specs and tutorials') ->setParam('docs.url', $endpoint . '/docs'); - $specsDir = __DIR__ . '/../../../../app/config/specs'; + $path = $mocks + ? $specsDir . '/' . $format . '-mocks-' . $platform . '.json' + : $specsDir . '/' . $format . '-' . $version . '-' . $platform . '.json'; - if (!is_dir($specsDir)) { - if (!mkdir($specsDir, 0755, true)) { - throw new Exception('Failed to create specs directory: ' . $specsDir); - } + $parsedSpecs = $specs->parse(); + $encodedSpecs = \json_encode($parsedSpecs, JSON_PRETTY_PRINT); + + unset($parsedSpecs); + + if ($encodedSpecs === false) { + throw new Exception('Failed to encode ' . ($mocks ? 'mocks ' : '') . 'spec file: ' . \json_last_error_msg()); } - if ($mocks) { - $path = $specsDir . '/' . $format . '-mocks-' . $platform . '.json'; - - if (!file_put_contents($path, json_encode($specs->parse(), JSON_PRETTY_PRINT))) { - throw new Exception('Failed to save mocks spec file: ' . $path); - } - - $generatedFiles[] = realpath($path); - Console::success('Saved mocks spec file: ' . realpath($path)); - - continue; - } - - $path = $specsDir . '/' . $format . '-' . $version . '-' . $platform . '.json'; - - if (!file_put_contents($path, json_encode($specs->parse(), JSON_PRETTY_PRINT))) { - throw new Exception('Failed to save spec file: ' . $path); + if (\file_put_contents($path, $encodedSpecs) === false) { + throw new Exception('Failed to save ' . ($mocks ? 'mocks ' : '') . 'spec file: ' . $path); } $generatedFiles[] = realpath($path); - Console::success('Saved spec file: ' . realpath($path)); + Console::success('Saved ' . ($mocks ? 'mocks ' : '') . 'spec file: ' . realpath($path)); + + unset($encodedSpecs, $specs, $formatInstance); } + + unset($arguments, $models, $routes, $services); } if ($git === 'yes') { diff --git a/src/Appwrite/Utopia/Database/Documents/User.php b/src/Appwrite/Utopia/Database/Documents/User.php index bef73b31d9..50e66dac38 100644 --- a/src/Appwrite/Utopia/Database/Documents/User.php +++ b/src/Appwrite/Utopia/Database/Documents/User.php @@ -102,7 +102,7 @@ class User extends Document * * @return bool */ - public static function isPrivileged(array $roles): bool + public function isPrivileged(array $roles): bool { if ( in_array(self::ROLE_OWNER, $roles) || @@ -122,7 +122,7 @@ class User extends Document * * @return bool */ - public static function isApp(array $roles): bool + public function isApp(array $roles): bool { if (in_array(self::ROLE_APPS, $roles)) { return true; @@ -175,7 +175,5 @@ class User extends Document } return false; - - return false; } } diff --git a/src/Appwrite/Utopia/Request.php b/src/Appwrite/Utopia/Request.php index a5b3d038bb..9428ff9d88 100644 --- a/src/Appwrite/Utopia/Request.php +++ b/src/Appwrite/Utopia/Request.php @@ -215,7 +215,7 @@ class Request extends UtopiaRequest $forwardedUserAgent = $this->getHeader('x-forwarded-user-agent'); if (!empty($forwardedUserAgent)) { $roles = $this->authorization->getRoles(); - $isAppUser = User::isApp($roles); + $isAppUser = $this->user?->isApp($roles) ?? false; if ($isAppUser) { return $forwardedUserAgent; @@ -239,9 +239,15 @@ class Request extends UtopiaRequest } private ?Authorization $authorization = null; + private ?User $user = null; public function setAuthorization(Authorization $authorization): void { $this->authorization = $authorization; } + + public function setUser(User $user): void + { + $this->user = $user; + } } diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index c2fc520da3..99170a58c9 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -505,8 +505,9 @@ class Response extends SwooleResponse if ($rule['sensitive']) { $roles = $this->authorization->getRoles(); - $isPrivilegedUser = DBUser::isPrivileged($roles); - $isAppUser = DBUser::isApp($roles); + $user = $this->user ?? new DBUser(); + $isPrivilegedUser = $user->isPrivileged($roles); + $isAppUser = $user->isApp($roles); if ((!$isPrivilegedUser && !$isAppUser) && !self::$showSensitive) { $data->setAttribute($key, ''); @@ -674,9 +675,15 @@ class Response extends SwooleResponse } private ?Authorization $authorization = null; + private ?DBUser $user = null; public function setAuthorization(Authorization $authorization): void { $this->authorization = $authorization; } + + public function setUser(DBUser $user): void + { + $this->user = $user; + } } diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 3b44d5c1e3..628928914f 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -3631,7 +3631,7 @@ trait DatabasesBase ]); $adapter = getenv('_APP_DB_ADAPTER'); - if ($adapter === 'mongodb') { + if ($adapter === 'mongodb' || !$this->getSupportForAttributes()) { $this->assertEquals(400, $response['headers']['status-code']); } else { $this->assertEquals(200, $response['headers']['status-code']); diff --git a/tests/unit/Utopia/Database/Documents/UserTest.php b/tests/unit/Utopia/Database/Documents/UserTest.php index 4094b43246..b3638e7d3a 100644 --- a/tests/unit/Utopia/Database/Documents/UserTest.php +++ b/tests/unit/Utopia/Database/Documents/UserTest.php @@ -171,36 +171,40 @@ class UserTest extends TestCase public function testIsPrivilegedUser(): void { - $this->assertEquals(false, User::isPrivileged([])); - $this->assertEquals(false, User::isPrivileged([Role::guests()->toString()])); - $this->assertEquals(false, User::isPrivileged([Role::users()->toString()])); - $this->assertEquals(true, User::isPrivileged([User::ROLE_ADMIN])); - $this->assertEquals(true, User::isPrivileged([User::ROLE_DEVELOPER])); - $this->assertEquals(true, User::isPrivileged([User::ROLE_OWNER])); - $this->assertEquals(false, User::isPrivileged([User::ROLE_APPS])); - $this->assertEquals(false, User::isPrivileged([User::ROLE_SYSTEM])); + $user = new User(); - $this->assertEquals(false, User::isPrivileged([User::ROLE_APPS, User::ROLE_APPS])); - $this->assertEquals(false, User::isPrivileged([User::ROLE_APPS, Role::guests()->toString()])); - $this->assertEquals(true, User::isPrivileged([User::ROLE_OWNER, Role::guests()->toString()])); - $this->assertEquals(true, User::isPrivileged([User::ROLE_OWNER, User::ROLE_ADMIN, User::ROLE_DEVELOPER])); + $this->assertEquals(false, $user->isPrivileged([])); + $this->assertEquals(false, $user->isPrivileged([Role::guests()->toString()])); + $this->assertEquals(false, $user->isPrivileged([Role::users()->toString()])); + $this->assertEquals(true, $user->isPrivileged([User::ROLE_ADMIN])); + $this->assertEquals(true, $user->isPrivileged([User::ROLE_DEVELOPER])); + $this->assertEquals(true, $user->isPrivileged([User::ROLE_OWNER])); + $this->assertEquals(false, $user->isPrivileged([User::ROLE_APPS])); + $this->assertEquals(false, $user->isPrivileged([User::ROLE_SYSTEM])); + + $this->assertEquals(false, $user->isPrivileged([User::ROLE_APPS, User::ROLE_APPS])); + $this->assertEquals(false, $user->isPrivileged([User::ROLE_APPS, Role::guests()->toString()])); + $this->assertEquals(true, $user->isPrivileged([User::ROLE_OWNER, Role::guests()->toString()])); + $this->assertEquals(true, $user->isPrivileged([User::ROLE_OWNER, User::ROLE_ADMIN, User::ROLE_DEVELOPER])); } public function testIsAppUser(): void { - $this->assertEquals(false, User::isApp([])); - $this->assertEquals(false, User::isApp([Role::guests()->toString()])); - $this->assertEquals(false, User::isApp([Role::users()->toString()])); - $this->assertEquals(false, User::isApp([User::ROLE_ADMIN])); - $this->assertEquals(false, User::isApp([User::ROLE_DEVELOPER])); - $this->assertEquals(false, User::isApp([User::ROLE_OWNER])); - $this->assertEquals(true, User::isApp([User::ROLE_APPS])); - $this->assertEquals(false, User::isApp([User::ROLE_SYSTEM])); + $user = new User(); - $this->assertEquals(true, User::isApp([User::ROLE_APPS, User::ROLE_APPS])); - $this->assertEquals(true, User::isApp([User::ROLE_APPS, Role::guests()->toString()])); - $this->assertEquals(false, User::isApp([User::ROLE_OWNER, Role::guests()->toString()])); - $this->assertEquals(false, User::isApp([User::ROLE_OWNER, User::ROLE_ADMIN, User::ROLE_DEVELOPER])); + $this->assertEquals(false, $user->isApp([])); + $this->assertEquals(false, $user->isApp([Role::guests()->toString()])); + $this->assertEquals(false, $user->isApp([Role::users()->toString()])); + $this->assertEquals(false, $user->isApp([User::ROLE_ADMIN])); + $this->assertEquals(false, $user->isApp([User::ROLE_DEVELOPER])); + $this->assertEquals(false, $user->isApp([User::ROLE_OWNER])); + $this->assertEquals(true, $user->isApp([User::ROLE_APPS])); + $this->assertEquals(false, $user->isApp([User::ROLE_SYSTEM])); + + $this->assertEquals(true, $user->isApp([User::ROLE_APPS, User::ROLE_APPS])); + $this->assertEquals(true, $user->isApp([User::ROLE_APPS, Role::guests()->toString()])); + $this->assertEquals(false, $user->isApp([User::ROLE_OWNER, Role::guests()->toString()])); + $this->assertEquals(false, $user->isApp([User::ROLE_OWNER, User::ROLE_ADMIN, User::ROLE_DEVELOPER])); } public function testGuestRoles(): void