From 5c2828bc78aa6e2c5cb483089227f0cf4cc1d083 Mon Sep 17 00:00:00 2001 From: shimon Date: Wed, 29 Oct 2025 20:21:41 +0200 Subject: [PATCH] Refactor authorization handling across multiple modules to use a single instance of the Authorization class. --- app/cli.php | 3 +- app/controllers/api/account.php | 140 +++++++------ app/controllers/api/avatars.php | 30 +-- app/controllers/api/messaging.php | 62 +++--- app/controllers/api/migrations.php | 20 +- app/controllers/api/project.php | 10 +- app/controllers/api/storage.php | 186 ++++++++++-------- app/controllers/api/teams.php | 64 +++--- app/controllers/api/users.php | 6 +- app/controllers/api/vcs.php | 61 +++--- .../Modules/Console/Http/Resources/Get.php | 7 +- .../Collections/Attributes/Delete.php | 6 +- .../Databases/Collections/Attributes/Get.php | 6 +- .../Attributes/Relationship/Create.php | 6 +- .../Collections/Attributes/XList.php | 6 +- .../Http/Databases/Collections/Create.php | 6 +- .../Http/Databases/Collections/Delete.php | 6 +- .../Collections/Documents/Action.php | 4 +- .../Documents/Attribute/Decrement.php | 14 +- .../Documents/Attribute/Increment.php | 14 +- .../Collections/Documents/Create.php | 30 +-- .../Collections/Documents/Delete.php | 17 +- .../Databases/Collections/Documents/Get.php | 12 +- .../Collections/Documents/Logs/XList.php | 6 +- .../Collections/Documents/Update.php | 24 +-- .../Collections/Documents/Upsert.php | 24 +-- .../Databases/Collections/Documents/XList.php | 14 +- .../Http/Databases/Collections/Get.php | 6 +- .../Databases/Collections/Indexes/Create.php | 6 +- .../Databases/Collections/Indexes/Delete.php | 6 +- .../Databases/Collections/Indexes/Get.php | 6 +- .../Databases/Collections/Indexes/XList.php | 8 +- .../Http/Databases/Collections/Logs/XList.php | 6 +- .../Http/Databases/Collections/Update.php | 6 +- .../Http/Databases/Collections/Usage/Get.php | 6 +- .../Http/Databases/Collections/XList.php | 6 +- .../Http/Databases/Transactions/Create.php | 6 +- .../Transactions/Operations/Create.php | 26 +-- .../Http/Databases/Transactions/Update.php | 33 ++-- .../Databases/Http/Databases/Usage/Get.php | 6 +- .../Databases/Http/Databases/Usage/XList.php | 6 +- .../Functions/Http/Executions/Create.php | 23 ++- .../Functions/Http/Executions/Delete.php | 7 +- .../Modules/Functions/Http/Executions/Get.php | 11 +- .../Functions/Http/Executions/XList.php | 11 +- .../Functions/Http/Functions/Create.php | 9 +- .../Functions/Http/Functions/Delete.php | 7 +- .../Http/Functions/Deployment/Update.php | 11 +- .../Functions/Http/Functions/Update.php | 7 +- .../Modules/Functions/Http/Usage/Get.php | 6 +- .../Modules/Functions/Http/Usage/XList.php | 6 +- .../Functions/Http/Variables/Create.php | 7 +- .../Functions/Http/Variables/Delete.php | 7 +- .../Functions/Http/Variables/Update.php | 7 +- .../Modules/Sites/Http/Deployments/Create.php | 9 +- .../Http/Deployments/Duplicate/Create.php | 7 +- .../Http/Deployments/Template/Create.php | 7 +- .../Sites/Http/Sites/Deployment/Update.php | 9 +- .../Platform/Modules/Sites/Http/Usage/Get.php | 7 +- .../Modules/Sites/Http/Usage/XList.php | 6 +- .../Http/Tokens/Buckets/Files/Create.php | 8 +- src/Appwrite/Platform/Tasks/Migrate.php | 5 +- .../Platform/Tasks/StatsResources.php | 10 +- 63 files changed, 647 insertions(+), 457 deletions(-) diff --git a/app/cli.php b/app/cli.php index a70a718948..d2e335b795 100644 --- a/app/cli.php +++ b/app/cli.php @@ -149,7 +149,7 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform $adapter = new DatabasePool($pools->get($dsn->getHost())); $database = new Database($adapter, $cache); - $database->setAuthorization($authorization); + $databases[$dsn->getHost()] = $database; $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); @@ -166,6 +166,7 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform } $database + ->setAuthorization($authorization) ->setMetadata('host', \gethostname()) ->setMetadata('project', $project->getId()); diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index da7e4c6ff1..ca6ac52cd4 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -51,6 +51,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\Query\Limit; @@ -169,10 +170,10 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc ; -$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails) { +$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) { /** @var Utopia\Database\Document $user */ - $userFromRequest = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('users', $userId)); + $userFromRequest = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId)); if ($userFromRequest->isEmpty()) { throw new Exception(Exception::USER_INVALID_TOKEN); @@ -218,7 +219,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res $detector->getDevice() )); - $dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $session = $dbForProject->createDocument('sessions', $session ->setAttribute('$permissions', [ @@ -227,7 +228,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res Permission::delete(Role::user($user->getId())), ])); - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId())); + $authorization->skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId())); $dbForProject->purgeCachedDocument('users', $user->getId()); // Magic URL + Email OTP @@ -323,8 +324,9 @@ App::post('/v1/account') ->inject('user') ->inject('project') ->inject('dbForProject') + ->inject('authorization') ->inject('hooks') - ->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Hooks $hooks) { + ->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Authorization $authorization, Hooks $hooks) { $email = \strtolower($email); if ('console' === $project->getId()) { @@ -402,9 +404,9 @@ App::post('/v1/account') 'accessedAt' => DateTime::now(), ]); $user->removeAttribute('$sequence'); - $user = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('users', $user)); + $user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); try { - $target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('targets', new Document([ + $target = $authorization->skip(fn () => $dbForProject->createDocument('targets', new Document([ '$permissions' => [ Permission::read(Role::user($user->getId())), Permission::update(Role::user($user->getId())), @@ -430,9 +432,9 @@ App::post('/v1/account') throw new Exception(Exception::USER_ALREADY_EXISTS); } - $dbForProject->getAuthorization()->removeRole(Role::guests()->toString()); - $dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString()); - $dbForProject->getAuthorization()->addRole(Role::users()->toString()); + $authorization->removeRole(Role::guests()->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::users()->toString()); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -893,7 +895,8 @@ App::post('/v1/account/sessions/email') ->inject('queueForEvents') ->inject('queueForMails') ->inject('hooks') - ->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks) { + ->inject('authorization') + ->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks, Authorization $authorization) { $email = \strtolower($email); $protocol = $request->getProtocol(); @@ -936,7 +939,7 @@ App::post('/v1/account/sessions/email') $detector->getDevice() )); - $dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); // Re-hash if not using recommended algo if ($user->getAttribute('hash') !== Auth::DEFAULT_ALGO) { @@ -1028,7 +1031,8 @@ App::post('/v1/account/sessions/anonymous') ->inject('dbForProject') ->inject('geodb') ->inject('queueForEvents') - ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents) { + ->inject('authorization') + ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $authorization) { $protocol = $request->getProtocol(); if ('console' === $project->getId()) { @@ -1073,7 +1077,7 @@ App::post('/v1/account/sessions/anonymous') 'accessedAt' => DateTime::now(), ]); $user->removeAttribute('$sequence'); - $dbForProject->getAuthorization()-> skip(fn () => $dbForProject->createDocument('users', $user)); + $authorization-> skip(fn () => $dbForProject->createDocument('users', $user)); // Create session token $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; @@ -1099,7 +1103,7 @@ App::post('/v1/account/sessions/anonymous') $detector->getDevice() )); - $dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $session = $dbForProject->createDocument('sessions', $session->setAttribute('$permissions', [ Permission::read(Role::user($user->getId())), @@ -1172,6 +1176,7 @@ App::post('/v1/account/sessions/token') ->inject('geodb') ->inject('queueForEvents') ->inject('queueForMails') + ->inject('authorization') ->action($createSession); App::get('/v1/account/sessions/oauth2/:provider') @@ -1364,7 +1369,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->inject('dbForProject') ->inject('geodb') ->inject('queueForEvents') - ->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, array $platforms, Document $devKey, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) { + ->inject('authorization') + ->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, array $platforms, Document $devKey, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $authorization) use ($oauthDefaultSuccess) { $protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https'; $port = $request->getPort(); $callbackBase = $protocol . '://' . $request->getHostname(); @@ -1605,7 +1611,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'accessedAt' => DateTime::now(), ]); $user->removeAttribute('$sequence'); - $userDoc = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('users', $user)); + $userDoc = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); $dbForProject->createDocument('targets', new Document([ '$permissions' => [ Permission::read(Role::user($user->getId())), @@ -1624,8 +1630,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') } } - $dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString()); - $dbForProject->getAuthorization()->addRole(Role::users()->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::users()->toString()); if (false === $user->getAttribute('status')) { // Account is blocked $failureRedirect(Exception::USER_BLOCKED); // User is in status blocked @@ -1684,7 +1690,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $dbForProject->updateDocument('users', $user->getId(), $user); - $dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $state['success'] = URLParser::parse($state['success']); $query = URLParser::parseQuery($state['success']['query']); @@ -1706,7 +1712,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'ip' => $request->getIP(), ]); - $dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $token = $dbForProject->createDocument('tokens', $token ->setAttribute('$permissions', [ @@ -1933,7 +1939,8 @@ App::post('/v1/account/tokens/magic-url') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') - ->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { + ->inject('authorization') + ->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) { if (empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); } @@ -1995,7 +2002,7 @@ App::post('/v1/account/tokens/magic-url') ]); $user->removeAttribute('$sequence'); - $user = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('users', $user)); + $user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); } $tokenSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_MAGIC_URL); @@ -2012,7 +2019,7 @@ App::post('/v1/account/tokens/magic-url') 'ip' => $request->getIP(), ]); - $dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $token = $dbForProject->createDocument('tokens', $token ->setAttribute('$permissions', [ @@ -2185,7 +2192,8 @@ App::post('/v1/account/tokens/email') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') - ->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { + ->inject('authorization') + ->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) { if (empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); } @@ -2243,9 +2251,9 @@ App::post('/v1/account/tokens/email') ]); $user->removeAttribute('$sequence'); - $user = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('users', $user)); + $user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); try { - $target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('targets', new Document([ + $target = $authorization->skip(fn () => $dbForProject->createDocument('targets', new Document([ '$permissions' => [ Permission::read(Role::user($user->getId())), Permission::update(Role::user($user->getId())), @@ -2283,7 +2291,7 @@ App::post('/v1/account/tokens/email') 'ip' => $request->getIP(), ]); - $dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $token = $dbForProject->createDocument('tokens', $token ->setAttribute('$permissions', [ @@ -2464,6 +2472,7 @@ App::put('/v1/account/sessions/magic-url') ->inject('geodb') ->inject('queueForEvents') ->inject('queueForMails') + ->inject('authorization') ->action($createSession); App::put('/v1/account/sessions/phone') @@ -2505,6 +2514,7 @@ App::put('/v1/account/sessions/phone') ->inject('geodb') ->inject('queueForEvents') ->inject('queueForMails') + ->inject('authorization') ->action($createSession); App::post('/v1/account/tokens/phone') @@ -2545,7 +2555,8 @@ App::post('/v1/account/tokens/phone') ->inject('timelimit') ->inject('queueForStatsUsage') ->inject('plan') - ->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) { + ->inject('authorization') + ->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Authorization $authorization) { if (empty(System::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } @@ -2590,9 +2601,9 @@ App::post('/v1/account/tokens/phone') ]); $user->removeAttribute('$sequence'); - $user = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('users', $user)); + $user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); try { - $target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('targets', new Document([ + $target = $authorization->skip(fn () => $dbForProject->createDocument('targets', new Document([ '$permissions' => [ Permission::read(Role::user($user->getId())), Permission::update(Role::user($user->getId())), @@ -2638,7 +2649,7 @@ App::post('/v1/account/tokens/phone') 'ip' => $request->getIP(), ]); - $dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $token = $dbForProject->createDocument('tokens', $token ->setAttribute('$permissions', [ @@ -3023,7 +3034,8 @@ App::patch('/v1/account/email') ->inject('queueForEvents') ->inject('project') ->inject('hooks') - ->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks) { + ->inject('authorization') + ->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $authorization) { // passwordUpdate will be empty if the user has never set a password $passwordUpdate = $user->getAttribute('passwordUpdate'); @@ -3062,7 +3074,7 @@ App::patch('/v1/account/email') ->setAttribute('passwordUpdate', DateTime::now()); } - $target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->findOne('targets', [ + $target = $authorization->skip(fn () => $dbForProject->findOne('targets', [ Query::equal('identifier', [$email]), ])); @@ -3078,7 +3090,7 @@ App::patch('/v1/account/email') $oldTarget = $user->find('identifier', $oldEmail, 'targets'); if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) { - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email))); + $authorization->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email))); } $dbForProject->purgeCachedDocument('users', $user->getId()); } catch (Duplicate) { @@ -3120,7 +3132,8 @@ App::patch('/v1/account/phone') ->inject('queueForEvents') ->inject('project') ->inject('hooks') - ->action(function (string $phone, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks) { + ->inject('authorization') + ->action(function (string $phone, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $authorization) { // passwordUpdate will be empty if the user has never set a password $passwordUpdate = $user->getAttribute('passwordUpdate'); @@ -3133,7 +3146,7 @@ App::patch('/v1/account/phone') $hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]); - $target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->findOne('targets', [ + $target = $authorization->skip(fn () => $dbForProject->findOne('targets', [ Query::equal('identifier', [$phone]), ])); @@ -3164,7 +3177,7 @@ App::patch('/v1/account/phone') $oldTarget = $user->find('identifier', $oldPhone, 'targets'); if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) { - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone))); + $authorization->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone))); } $dbForProject->purgeCachedDocument('users', $user->getId()); } catch (Duplicate $th) { @@ -3298,7 +3311,8 @@ App::post('/v1/account/recovery') ->inject('locale') ->inject('queueForMails') ->inject('queueForEvents') - ->action(function (string $email, string $url, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents) { + ->inject('authorization') + ->action(function (string $email, string $url, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents, Authorization $authorization) { if (empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); @@ -3334,7 +3348,7 @@ App::post('/v1/account/recovery') 'ip' => $request->getIP(), ]); - $dbForProject->getAuthorization()->addRole(Role::user($profile->getId())->toString()); + $authorization->addRole(Role::user($profile->getId())->toString()); $recovery = $dbForProject->createDocument('tokens', $recovery ->setAttribute('$permissions', [ @@ -3476,7 +3490,8 @@ App::put('/v1/account/recovery') ->inject('project') ->inject('queueForEvents') ->inject('hooks') - ->action(function (string $userId, string $secret, string $password, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks) { + ->inject('authorization') + ->action(function (string $userId, string $secret, string $password, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks, Authorization $authorization) { $profile = $dbForProject->getDocument('users', $userId); if ($profile->isEmpty()) { @@ -3490,7 +3505,7 @@ App::put('/v1/account/recovery') throw new Exception(Exception::USER_INVALID_TOKEN); } - $dbForProject->getAuthorization()->addRole(Role::user($profile->getId())->toString()); + $authorization->addRole(Role::user($profile->getId())->toString()); $newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS); @@ -3588,7 +3603,8 @@ App::post('/v1/account/verifications/email') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') - ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { + ->inject('authorization') + ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) { if (empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); @@ -3617,7 +3633,7 @@ App::post('/v1/account/verifications/email') 'ip' => $request->getIP(), ]); - $dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $verification = $dbForProject->createDocument('tokens', $verification ->setAttribute('$permissions', [ @@ -3800,9 +3816,10 @@ App::put('/v1/account/verifications/email') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { + ->inject('authorization') + ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { - $profile = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('users', $userId)); + $profile = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId)); if ($profile->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); @@ -3815,7 +3832,7 @@ App::put('/v1/account/verifications/email') throw new Exception(Exception::USER_INVALID_TOKEN); } - $dbForProject->getAuthorization()->addRole(Role::user($profile->getId())->toString()); + $authorization->addRole(Role::user($profile->getId())->toString()); $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true)); @@ -3874,7 +3891,8 @@ App::post('/v1/account/verifications/phone') ->inject('timelimit') ->inject('queueForStatsUsage') ->inject('plan') - ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) { + ->inject('authorization') + ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Authorization $authorization) { if (empty(System::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } @@ -3913,7 +3931,7 @@ App::post('/v1/account/verifications/phone') 'ip' => $request->getIP(), ]); - $dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $verification = $dbForProject->createDocument('tokens', $verification ->setAttribute('$permissions', [ @@ -4018,9 +4036,10 @@ App::put('/v1/account/verifications/phone') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { + ->inject('authorization') + ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { - $profile = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('users', $userId)); + $profile = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId)); if ($profile->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); @@ -4032,7 +4051,7 @@ App::put('/v1/account/verifications/phone') throw new Exception(Exception::USER_INVALID_TOKEN); } - $dbForProject->getAuthorization()->addRole(Role::user($profile->getId())->toString()); + $authorization->addRole(Role::user($profile->getId())->toString()); $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('phoneVerification', true)); @@ -5020,12 +5039,13 @@ App::post('/v1/account/targets/push') ->inject('request') ->inject('response') ->inject('dbForProject') - ->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) { + ->inject('authorization') + ->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) { $targetId = $targetId == 'unique()' ? ID::unique() : $targetId; - $provider = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('providers', $providerId)); + $provider = $authorization->skip(fn () => $dbForProject->getDocument('providers', $providerId)); - $target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('targets', $targetId)); + $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId)); if (!$target->isEmpty()) { throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS); @@ -5100,9 +5120,10 @@ App::put('/v1/account/targets/:targetId/push') ->inject('request') ->inject('response') ->inject('dbForProject') - ->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) { + ->inject('authorization') + ->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) { - $target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('targets', $targetId)); + $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId)); if ($target->isEmpty()) { throw new Exception(Exception::USER_TARGET_NOT_FOUND); @@ -5165,8 +5186,9 @@ App::delete('/v1/account/targets/:targetId/push') ->inject('request') ->inject('response') ->inject('dbForProject') - ->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject) { - $target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('targets', $targetId)); + ->inject('authorization') + ->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) { + $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId)); if ($target->isEmpty()) { throw new Exception(Exception::USER_TARGET_NOT_FOUND); diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 4952a763fc..0751f0a2a6 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -16,6 +16,7 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Domains\Domain; use Utopia\Fetch\Client; @@ -66,9 +67,9 @@ $avatarCallback = function (string $type, string $code, int $width, int $height, unset($image); }; -$getUserGitHub = function (string $userId, Document $project, Database $dbForProject, Database $dbForPlatform, ?Logger $logger) { +$getUserGitHub = function (string $userId, Document $project, Database $dbForProject, Database $dbForPlatform, Authorization $authorization, ?Logger $logger) { try { - $user = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('users', $userId)); + $user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId)); $sessions = $user->getAttribute('sessions', []); @@ -119,7 +120,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro ->setAttribute('providerRefreshToken', $refreshToken) ->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$oauth2->getAccessTokenExpiry(''))); - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession)); + $authorization->skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession)); $dbForProject->purgeCachedDocument('users', $user->getId()); } catch (Throwable $err) { @@ -127,7 +128,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro do { $previousAccessToken = $gitHubSession->getAttribute('providerAccessToken'); - $user = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('users', $userId)); + $user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId)); $sessions = $user->getAttribute('sessions', []); $gitHubSession = new Document(); @@ -656,8 +657,9 @@ App::get('/v1/cards/cloud') ->inject('contributors') ->inject('employees') ->inject('logger') - ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) { - $user = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('users', $userId)); + ->inject('authorization') + ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization) use ($getUserGitHub) { + $user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { throw new Exception(Exception::USER_NOT_FOUND); @@ -668,7 +670,7 @@ App::get('/v1/cards/cloud') $email = $user->getAttribute('email', ''); $createdAt = new \DateTime($user->getCreatedAt()); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger, $authorization); $githubName = $gitHub['name'] ?? ''; $githubId = $gitHub['id'] ?? ''; @@ -863,8 +865,9 @@ App::get('/v1/cards/cloud-back') ->inject('contributors') ->inject('employees') ->inject('logger') - ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) { - $user = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('users', $userId)); + ->inject('authorization') + ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization) use ($getUserGitHub) { + $user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { throw new Exception(Exception::USER_NOT_FOUND); @@ -874,7 +877,7 @@ App::get('/v1/cards/cloud-back') $userId = $user->getId(); $email = $user->getAttribute('email', ''); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger, $authorization); $githubId = $gitHub['id'] ?? ''; $isHero = \array_key_exists($email, $heroes); @@ -941,8 +944,9 @@ App::get('/v1/cards/cloud-og') ->inject('contributors') ->inject('employees') ->inject('logger') - ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) { - $user = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('users', $userId)); + ->inject('authorization') + ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization) use ($getUserGitHub) { + $user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { throw new Exception(Exception::USER_NOT_FOUND); @@ -957,7 +961,7 @@ App::get('/v1/cards/cloud-og') $email = $user->getAttribute('email', ''); $createdAt = new \DateTime($user->getCreatedAt()); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger, $authorization); $githubName = $gitHub['name'] ?? ''; $githubId = $gitHub['id'] ?? ''; diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 551be5c19c..5caae49864 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -35,6 +35,7 @@ use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Authorization\Input; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Queries; @@ -1068,8 +1069,9 @@ App::get('/v1/messaging/providers') ->param('queries', [], new Providers(), '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(', ', Providers::ALLOWED_ATTRIBUTES), true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('dbForProject') + ->inject('authorization') ->inject('response') - ->action(function (array $queries, string $search, Database $dbForProject, Response $response) { + ->action(function (array $queries, string $search, Database $dbForProject, Authorization $authorization, Response $response) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -1095,7 +1097,7 @@ App::get('/v1/messaging/providers') } $providerId = $cursor->getValue(); - $cursorDocument = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('providers', $providerId)); + $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('providers', $providerId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Provider '{$providerId}' for the 'cursor' value not found."); @@ -2473,8 +2475,9 @@ App::get('/v1/messaging/topics') ->param('queries', [], new Topics(), '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(', ', Topics::ALLOWED_ATTRIBUTES), true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('dbForProject') + ->inject('authorization') ->inject('response') - ->action(function (array $queries, string $search, Database $dbForProject, Response $response) { + ->action(function (array $queries, string $search, Database $dbForProject, Authorization $authorization, Response $response) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -2500,7 +2503,7 @@ App::get('/v1/messaging/topics') } $topicId = $cursor->getValue(); - $cursorDocument = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('topics', $topicId)); + $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Topic '{$topicId}' for the 'cursor' value not found."); @@ -2775,26 +2778,27 @@ App::post('/v1/messaging/topics/:topicId/subscribers') ->param('targetId', '', new UID(), 'Target ID. The target ID to link to the specified Topic ID.') ->inject('queueForEvents') ->inject('dbForProject') + ->inject('authorization') ->inject('response') - ->action(function (string $subscriberId, string $topicId, string $targetId, Event $queueForEvents, Database $dbForProject, Response $response) { + ->action(function (string $subscriberId, string $topicId, string $targetId, Event $queueForEvents, Database $dbForProject, Authorization $authorization, Response $response) { $subscriberId = $subscriberId == 'unique()' ? ID::unique() : $subscriberId; - $topic = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('topics', $topicId)); + $topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { throw new Exception(Exception::TOPIC_NOT_FOUND); } - if (!$dbForProject->getAuthorization()->isValid(new Input('subscribe', $topic->getAttribute('subscribe')))) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + if (!$authorization->isValid(new Input('subscribe', $topic->getAttribute('subscribe')))) { + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } - $target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('targets', $targetId)); + $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId)); if ($target->isEmpty()) { throw new Exception(Exception::USER_TARGET_NOT_FOUND); } - $user = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); + $user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); $subscriber = new Document([ '$id' => $subscriberId, @@ -2827,7 +2831,7 @@ App::post('/v1/messaging/topics/:topicId/subscribers') default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE), }; - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->increaseDocumentAttribute( + $authorization->skip(fn () => $dbForProject->increaseDocumentAttribute( 'topics', $topicId, $totalAttribute, @@ -2871,8 +2875,9 @@ App::get('/v1/messaging/topics/:topicId/subscribers') ->param('queries', [], new Subscribers(), '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(', ', Providers::ALLOWED_ATTRIBUTES), true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('dbForProject') + ->inject('authorization') ->inject('response') - ->action(function (string $topicId, array $queries, string $search, Database $dbForProject, Response $response) { + ->action(function (string $topicId, array $queries, string $search, Database $dbForProject, Authorization $authorization, Response $response) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -2883,7 +2888,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers') $queries[] = Query::search('search', $search); } - $topic = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('topics', $topicId)); + $topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { throw new Exception(Exception::TOPIC_NOT_FOUND); @@ -2906,7 +2911,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers') } $subscriberId = $cursor->getValue(); - $cursorDocument = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId)); + $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Subscriber '{$subscriberId}' for the 'cursor' value not found."); @@ -2920,10 +2925,10 @@ App::get('/v1/messaging/topics/:topicId/subscribers') throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } - $subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject) { - return function () use ($subscriber, $dbForProject) { - $target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); - $user = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); + $subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject, $authorization) { + return function () use ($subscriber, $dbForProject, $authorization) { + $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); + $user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); return $subscriber ->setAttribute('target', $target) @@ -3057,9 +3062,10 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->param('topicId', '', new UID(), 'Topic ID. The topic ID subscribed to.') ->param('subscriberId', '', new UID(), 'Subscriber ID.') ->inject('dbForProject') + ->inject('authorization') ->inject('response') - ->action(function (string $topicId, string $subscriberId, Database $dbForProject, Response $response) { - $topic = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('topics', $topicId)); + ->action(function (string $topicId, string $subscriberId, Database $dbForProject, Authorization $authorization, Response $response) { + $topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { throw new Exception(Exception::TOPIC_NOT_FOUND); @@ -3071,8 +3077,8 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') throw new Exception(Exception::SUBSCRIBER_NOT_FOUND); } - $target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); - $user = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); + $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); + $user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); $subscriber ->setAttribute('target', $target) @@ -3108,9 +3114,10 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->param('subscriberId', '', new UID(), 'Subscriber ID.') ->inject('queueForEvents') ->inject('dbForProject') + ->inject('authorization') ->inject('response') - ->action(function (string $topicId, string $subscriberId, Event $queueForEvents, Database $dbForProject, Response $response) { - $topic = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('topics', $topicId)); + ->action(function (string $topicId, string $subscriberId, Event $queueForEvents, Database $dbForProject, Authorization $authorization, Response $response) { + $topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { throw new Exception(Exception::TOPIC_NOT_FOUND); @@ -3133,7 +3140,7 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE), }; - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->decreaseDocumentAttribute( + $authorization->skip(fn () => $dbForProject->decreaseDocumentAttribute( 'topics', $topicId, $totalAttribute, @@ -3689,8 +3696,9 @@ App::get('/v1/messaging/messages') ->param('queries', [], new Messages(), '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(', ', Messages::ALLOWED_ATTRIBUTES), true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('dbForProject') + ->inject('authorization') ->inject('response') - ->action(function (array $queries, string $search, Database $dbForProject, Response $response) { + ->action(function (array $queries, string $search, Database $dbForProject, Authorization $authorization, Response $response) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -3716,7 +3724,7 @@ App::get('/v1/messaging/messages') } $messageId = $cursor->getValue(); - $cursorDocument = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('messages', $messageId)); + $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('messages', $messageId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Message '{$messageId}' for the 'cursor' value not found."); diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index db1b867a4b..7d526580ad 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -19,6 +19,7 @@ use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Queries\Documents; use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\UID; @@ -334,18 +335,19 @@ App::post('/v1/migrations/csv/imports') ->inject('response') ->inject('dbForProject') ->inject('dbForPlatform') + ->inject('authorization') ->inject('project') ->inject('deviceForFiles') ->inject('deviceForMigrations') ->inject('queueForEvents') ->inject('queueForMigrations') - ->action(function (string $bucketId, string $fileId, string $resourceId, bool $internalFile, Response $response, Database $dbForProject, Database $dbForPlatform, Document $project, Device $deviceForFiles, Device $deviceForMigrations, Event $queueForEvents, Migration $queueForMigrations) { - $isAPIKey = Auth::isAppUser($dbForPlatform->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForPlatform->getAuthorization()->getRoles()); + ->action(function (string $bucketId, string $fileId, string $resourceId, bool $internalFile, Response $response, Database $dbForProject, Database $dbForPlatform, Authorization $authorization, Document $project, Device $deviceForFiles, Device $deviceForMigrations, Event $queueForEvents, Migration $queueForMigrations) { + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($internalFile && !$isPrivilegedUser) { throw new Exception(Exception::USER_UNAUTHORIZED); } - $bucket = $dbForPlatform->getAuthorization()->skip(function () use ($internalFile, $dbForPlatform, $dbForProject, $bucketId) { + $bucket = $authorization->skip(function () use ($internalFile, $dbForPlatform, $dbForProject, $bucketId) { if ($internalFile) { return $dbForPlatform->getDocument('buckets', 'default'); } @@ -356,7 +358,7 @@ App::post('/v1/migrations/csv/imports') throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - $file = $dbForPlatform->getAuthorization()->skip(fn () => $internalFile ? $dbForPlatform->getDocument('bucket_' . $bucket->getSequence(), $fileId) : $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); + $file = $authorization->skip(fn () => $internalFile ? $dbForPlatform->getDocument('bucket_' . $bucket->getSequence(), $fileId) : $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } @@ -472,6 +474,7 @@ App::post('/v1/migrations/csv/exports') ->inject('user') ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->inject('project') ->inject('queueForEvents') ->inject('queueForMigrations') @@ -489,6 +492,7 @@ App::post('/v1/migrations/csv/exports') Document $user, Response $response, Database $dbForProject, + Authorization $authorization, Document $project, Event $queueForEvents, Migration $queueForMigrations @@ -499,7 +503,7 @@ App::post('/v1/migrations/csv/exports') throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } - $bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty()) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } @@ -512,12 +516,12 @@ App::post('/v1/migrations/csv/exports') throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index 061711c96a..86f8f786c6 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -14,6 +14,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Datetime as DateTimeValidator; use Utopia\Database\Validator\UID; use Utopia\Validator\Boolean; @@ -43,9 +44,10 @@ App::get('/v1/project/usage') ->inject('response') ->inject('project') ->inject('dbForProject') + ->inject('authorization') ->inject('getLogsDB') ->inject('smsRates') - ->action(function (string $startDate, string $endDate, string $period, Response $response, Document $project, Database $dbForProject, callable $getLogsDB, array $smsRates) { + ->action(function (string $startDate, string $endDate, string $period, Response $response, Document $project, Database $dbForProject, Authorization $authorization, callable $getLogsDB, array $smsRates) { $stats = $total = $usage = []; $format = 'Y-m-d 00:00:00'; $firstDay = (new DateTime($startDate))->format($format); @@ -100,7 +102,7 @@ App::get('/v1/project/usage') '1d' => 'Y-m-d\T00:00:00.000P', }; - $dbForProject->getAuthorization()->skip(function () use ($dbForProject, $dbForLogs, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) { + $authorization->skip(function () use ($dbForProject, $dbForLogs, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) { foreach ($metrics['total'] as $metric) { $db = ($metric === METRIC_FILES_IMAGES_TRANSFORMED) ? $dbForLogs : $dbForProject; @@ -284,7 +286,7 @@ App::get('/v1/project/usage') }, $dbForProject->find('functions')); // This total is includes free and paid SMS usage - $authPhoneTotal = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->sum('stats', 'value', [ + $authPhoneTotal = $authorization->skip(fn () => $dbForProject->sum('stats', 'value', [ Query::equal('metric', [METRIC_AUTH_METHOD_PHONE]), Query::equal('period', ['1d']), Query::greaterThanEqual('time', $firstDay), @@ -292,7 +294,7 @@ App::get('/v1/project/usage') ])); // This estimate is only for paid SMS usage - $authPhoneMetrics = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->find('stats', [ + $authPhoneMetrics = $authorization->skip(fn () => $dbForProject->find('stats', [ Query::startsWith('metric', METRIC_AUTH_METHOD_PHONE . '.'), Query::equal('period', ['1d']), Query::greaterThanEqual('time', $firstDay), diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 1224bdd46e..f764b64ef6 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -31,6 +31,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Authorization\Input; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\Query\Cursor; @@ -426,19 +427,20 @@ App::post('/v1/storage/buckets/:bucketId/files') ->inject('mode') ->inject('deviceForFiles') ->inject('deviceForLocal') - ->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Device $deviceForFiles, Device $deviceForLocal) { + ->inject('authorization') + ->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Device $deviceForFiles, Device $deviceForLocal, Authorization $authorization) { - $bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - if (!$dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } $allowedPermissions = [ @@ -461,7 +463,7 @@ App::post('/v1/storage/buckets/:bucketId/files') } // Users can only manage their own roles, API keys and Admin users can manage any - $roles = $dbForProject->getAuthorization()->getRoles(); + $roles = $authorization->getRoles(); if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { @@ -474,7 +476,7 @@ App::post('/v1/storage/buckets/:bucketId/files') $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!$dbForProject->getAuthorization()->hasRole($role)) { + if (!$authorization->hasRole($role)) { throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); } } @@ -694,10 +696,10 @@ App::post('/v1/storage/buckets/:bucketId/files') * However as with chunk upload even if we are updating, we are essentially creating a file * adding it's new chunk so we validate create permission instead of update */ - if (!$dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } - $file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file)); + $file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file)); } } else { if ($file->isEmpty()) { @@ -736,12 +738,12 @@ App::post('/v1/storage/buckets/:bucketId/files') * However as with chunk upload even if we are updating, we are essentially creating a file * adding it's new chunk so we validate create permission instead of update */ - if (!$dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } try { - $file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file)); + $file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file)); } catch (NotFoundException) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } @@ -784,21 +786,22 @@ App::get('/v1/storage/buckets/:bucketId/files') ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->inject('mode') - ->action(function (string $bucketId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) { - $bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + ->action(function (string $bucketId, array $queries, string $search, Response $response, Database $dbForProject, Authorization $authorization, string $mode) { + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } $queries = Query::parseQueries($queries); @@ -827,7 +830,7 @@ App::get('/v1/storage/buckets/:bucketId/files') if ($fileSecurity && !$valid) { $cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); } else { - $cursorDocument = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); + $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); } if ($cursorDocument->isEmpty()) { @@ -844,8 +847,8 @@ App::get('/v1/storage/buckets/:bucketId/files') $files = $dbForProject->find('bucket_' . $bucket->getSequence(), $queries); $total = $dbForProject->count('bucket_' . $bucket->getSequence(), $filterQueries, APP_LIMIT_COUNT); } else { - $files = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->find('bucket_' . $bucket->getSequence(), $queries)); - $total = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->count('bucket_' . $bucket->getSequence(), $filterQueries, APP_LIMIT_COUNT)); + $files = $authorization->skip(fn () => $dbForProject->find('bucket_' . $bucket->getSequence(), $queries)); + $total = $authorization->skip(fn () => $dbForProject->count('bucket_' . $bucket->getSequence(), $filterQueries, APP_LIMIT_COUNT)); } } catch (NotFoundException) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -884,27 +887,28 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') ->param('fileId', '', new UID(), 'File ID.') ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->inject('mode') - ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, string $mode) { - $bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Authorization $authorization, string $mode) { + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); } else { - $file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); + $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); } if ($file->isEmpty()) { @@ -960,17 +964,18 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->inject('deviceForFiles') ->inject('deviceForLocal') ->inject('project') - ->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, ?string $token, Request $request, Response $response, Database $dbForProject, Document $resourceToken, Device $deviceForFiles, Device $deviceForLocal, Document $project) { + ->inject('authorization') + ->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, ?string $token, Request $request, Response $response, Database $dbForProject, Document $resourceToken, Device $deviceForFiles, Device $deviceForLocal, Document $project, Authorization $authorization) { if (!\extension_loaded('imagick')) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); } /* @type Document $bucket */ - $bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -978,20 +983,20 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence(); $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid && !$isToken) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } if ($fileSecurity && !$valid && !$isToken) { $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); } else { /* @type Document $file */ - $file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); + $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); } if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } if ($file->isEmpty()) { @@ -1109,11 +1114,11 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $contentType = (\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg']; //Do not update transformedAt if it's a console user - if (!Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles())) { + if (!Auth::isPrivilegedUser($authorization->getRoles())) { $transformedAt = $file->getAttribute('transformedAt', ''); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) { $file->setAttribute('transformedAt', DateTime::now()); - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file)); + $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file)); } } @@ -1153,15 +1158,16 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') ->inject('request') ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->inject('mode') ->inject('resourceToken') ->inject('deviceForFiles') - ->action(function (string $bucketId, string $fileId, ?string $token, Request $request, Response $response, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) { + ->action(function (string $bucketId, string $fileId, ?string $token, Request $request, Response $response, Database $dbForProject, Authorization $authorization, string $mode, Document $resourceToken, Device $deviceForFiles) { /* @type Document $bucket */ - $bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -1169,20 +1175,20 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') $isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence(); $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid && !$isToken) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } if ($fileSecurity && !$valid && !$isToken) { $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); } else { /* @type Document $file */ - $file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); + $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); } if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } if ($file->isEmpty()) { @@ -1316,12 +1322,13 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') ->inject('mode') ->inject('resourceToken') ->inject('deviceForFiles') - ->action(function (string $bucketId, string $fileId, ?string $token, Response $response, Request $request, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) { + ->inject('authorization') + ->action(function (string $bucketId, string $fileId, ?string $token, Response $response, Request $request, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles, Authorization $authorization) { /* @type Document $bucket */ - $bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -1329,20 +1336,20 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') $isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence(); $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid && !$isToken) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } if ($fileSecurity && !$valid && !$isToken) { $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); } else { /* @type Document $file */ - $file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); + $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); } if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } if ($file->isEmpty()) { @@ -1470,15 +1477,16 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push') ->inject('project') ->inject('mode') ->inject('deviceForFiles') - ->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Document $project, string $mode, Device $deviceForFiles) { - $bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + ->inject('authorization') + ->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Document $project, string $mode, Device $deviceForFiles, Authorization $authorization) { + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); $decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0); try { $decoded = $decoder->decode($jwt); } catch (JWTException) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } if ( @@ -1486,17 +1494,17 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push') $decoded['bucketId'] !== $bucketId || $decoded['fileId'] !== $fileId ) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - $file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); + $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); @@ -1642,25 +1650,26 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('user') ->inject('mode') ->inject('queueForEvents') - ->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents) { + ->inject('authorization') + ->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents, Authorization $authorization) { - $bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_UPDATE, $bucket->getUpdate())); + $valid = $authorization->isValid(new Input(Database::PERMISSION_UPDATE, $bucket->getUpdate())); if (!$fileSecurity && !$valid) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } // Read permission should not be required for update - $file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); + $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); @@ -1674,7 +1683,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') ]); // Users can only manage their own roles, API keys and Admin users can manage any - $roles = $dbForProject->getAuthorization()->getRoles(); + $roles = $authorization->getRoles(); if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles) && !\is_null($permissions)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { @@ -1687,7 +1696,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!$dbForProject->getAuthorization()->hasRole($role)) { + if (!$authorization->hasRole($role)) { throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); } } @@ -1708,7 +1717,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') if ($fileSecurity && !$valid) { $file = $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file); } else { - $file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file)); + $file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file)); } } catch (NotFoundException) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -1756,33 +1765,34 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('mode') ->inject('deviceForFiles') ->inject('queueForDeletes') - ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes) { - $bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + ->inject('authorization') + ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes, Authorization $authorization) { + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_DELETE, $bucket->getDelete())); + $valid = $authorization->isValid(new Input(Database::PERMISSION_DELETE, $bucket->getDelete())); if (!$fileSecurity && !$valid) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } // Read permission should not be required for delete - $file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); + $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } // Make sure we don't delete the file before the document permission check occurs - $validFile = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_DELETE, $file->getDelete())); + $validFile = $authorization->isValid(new Input(Database::PERMISSION_DELETE, $file->getDelete())); if ($fileSecurity && !$valid && !$validFile) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } $deviceDeleted = false; @@ -1806,7 +1816,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') if ($fileSecurity && !$valid) { $deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getSequence(), $fileId); } else { - $deleted = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getSequence(), $fileId)); + $deleted = $authorization->skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getSequence(), $fileId)); } } catch (NotFoundException) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -1851,7 +1861,8 @@ App::get('/v1/storage/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->action(function (string $range, Response $response, Database $dbForProject) { + ->inject('authorization') + ->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) { $periods = Config::getParam('usage', []); $stats = $usage = []; @@ -1863,7 +1874,7 @@ App::get('/v1/storage/usage') ]; $total = []; - $dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -1941,7 +1952,8 @@ App::get('/v1/storage/:bucketId/usage') ->inject('project') ->inject('dbForProject') ->inject('getLogsDB') - ->action(function (string $bucketId, string $range, Response $response, Document $project, Database $dbForProject, callable $getLogsDB) { + ->inject('authorization') + ->action(function (string $bucketId, string $range, Response $response, Document $project, Database $dbForProject, callable $getLogsDB, Authorization $authorization) { $dbForLogs = call_user_func($getLogsDB, $project); $bucket = $dbForProject->getDocument('buckets', $bucketId); @@ -1959,7 +1971,7 @@ App::get('/v1/storage/:bucketId/usage') str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED), ]; - $dbForProject->getAuthorization()->skip(function () use ($dbForProject, $dbForLogs, $bucket, $days, $metrics, &$stats) { + $authorization->skip(function () use ($dbForProject, $dbForLogs, $bucket, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $db = ($metric === str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED)) ? $dbForLogs diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 5843cebeca..c208da5c1c 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -41,6 +41,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Cursor; @@ -81,16 +82,17 @@ App::post('/v1/teams') ->inject('response') ->inject('user') ->inject('dbForProject') + ->inject('authorization') ->inject('queueForEvents') - ->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Authorization $authorization, Event $queueForEvents) { - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); - $isAppUser = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAppUser = Auth::isAppUser($authorization->getRoles()); $teamId = $teamId == 'unique()' ? ID::unique() : $teamId; try { - $team = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('teams', new Document([ + $team = $authorization->skip(fn () => $dbForProject->createDocument('teams', new Document([ '$id' => $teamId, '$permissions' => [ Permission::read(Role::team($teamId)), @@ -485,6 +487,7 @@ App::post('/v1/teams/:teamId/memberships') ->inject('project') ->inject('user') ->inject('dbForProject') + ->inject('authorization') ->inject('locale') ->inject('queueForMails') ->inject('queueForMessaging') @@ -492,9 +495,9 @@ App::post('/v1/teams/:teamId/memberships') ->inject('timelimit') ->inject('queueForStatsUsage') ->inject('plan') - ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) { - $isAppUser = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + ->action(function (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, StatsUsage $queueForStatsUsage, array $plan) { + $isAppUser = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); $url = htmlentities($url); if (empty($url)) { @@ -565,7 +568,7 @@ App::post('/v1/teams/:teamId/memberships') try { $userId = ID::unique(); - $invitee = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('users', new Document([ + $invitee = $authorization->skip(fn () => $dbForProject->createDocument('users', new Document([ '$id' => $userId, '$permissions' => [ Permission::read(Role::any()), @@ -601,7 +604,7 @@ App::post('/v1/teams/:teamId/memberships') } } - $isOwner = $dbForProject->getAuthorization()->hasRole('team:' . $team->getId() . '/owner'); + $isOwner = $authorization->hasRole('team:' . $team->getId() . '/owner'); if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server) throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to send invitations for this team'); @@ -637,11 +640,11 @@ App::post('/v1/teams/:teamId/memberships') ]); $membership = ($isPrivilegedUser || $isAppUser) ? - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('memberships', $membership)) : + $authorization->skip(fn () => $dbForProject->createDocument('memberships', $membership)) : $dbForProject->createDocument('memberships', $membership); if ($isPrivilegedUser || $isAppUser) { - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); + $authorization->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); } } elseif ($membership->getAttribute('confirm') === false) { @@ -654,7 +657,7 @@ App::post('/v1/teams/:teamId/memberships') } $membership = ($isPrivilegedUser || $isAppUser) ? - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('memberships', $membership->getId(), $membership)) : + $authorization->skip(fn () => $dbForProject->updateDocument('memberships', $membership->getId(), $membership)) : $dbForProject->updateDocument('memberships', $membership->getId(), $membership); } else { throw new Exception(Exception::MEMBERSHIP_ALREADY_CONFIRMED); @@ -840,7 +843,8 @@ App::get('/v1/teams/:teamId/memberships') ->inject('response') ->inject('project') ->inject('dbForProject') - ->action(function (string $teamId, array $queries, string $search, Response $response, Document $project, Database $dbForProject) { + ->inject('authorization') + ->action(function (string $teamId, array $queries, string $search, Response $response, Document $project, Database $dbForProject, Authorization $authorization) { $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { @@ -910,7 +914,7 @@ App::get('/v1/teams/:teamId/memberships') 'mfa' => $project->getAttribute('auths', [])['membershipsMfa'] ?? true, ]; - $roles = $dbForProject->getAuthorization()->getRoles(); + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -981,7 +985,8 @@ App::get('/v1/teams/:teamId/memberships/:membershipId') ->inject('response') ->inject('project') ->inject('dbForProject') - ->action(function (string $teamId, string $membershipId, Response $response, Document $project, Database $dbForProject) { + ->inject('authorization') + ->action(function (string $teamId, string $membershipId, Response $response, Document $project, Database $dbForProject, Authorization $authorization) { $team = $dbForProject->getDocument('teams', $teamId); @@ -1001,7 +1006,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId') 'mfa' => $project->getAttribute('auths', [])['membershipsMfa'] ?? true, ]; - $roles = $dbForProject->getAuthorization()->getRoles(); + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -1080,8 +1085,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') ->inject('user') ->inject('project') ->inject('dbForProject') + ->inject('authorization') ->inject('queueForEvents') - ->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Authorization $authorization, Event $queueForEvents) { $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { @@ -1098,9 +1104,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') throw new Exception(Exception::USER_NOT_FOUND); } - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); - $isAppUser = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isOwner = $dbForProject->getAuthorization()->hasRole('team:' . $team->getId() . '/owner'); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAppUser = Auth::isAppUser($authorization->getRoles()); + $isOwner = $authorization->hasRole('team:' . $team->getId() . '/owner'); if ($project->getId() === 'console') { // Quick check: fetch up to 2 owners to determine if only one exists @@ -1181,10 +1187,11 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->inject('response') ->inject('user') ->inject('dbForProject') + ->inject('authorization') ->inject('project') ->inject('geodb') ->inject('queueForEvents') - ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents) { + ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Authorization $authorization, Document $project, Reader $geodb, Event $queueForEvents) { $protocol = $request->getProtocol(); $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -1193,7 +1200,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') throw new Exception(Exception::MEMBERSHIP_NOT_FOUND); } - $team = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('teams', $teamId)); + $team = $authorization->skip(fn () => $dbForProject->getDocument('teams', $teamId)); if ($team->isEmpty()) { throw new Exception(Exception::TEAM_NOT_FOUND); @@ -1229,11 +1236,11 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->setAttribute('confirm', true) ; - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true))); + $authorization->skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true))); // Create session for the user if not logged in if (!$hasSession) { - $dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $detector = new Detector($request->getUserAgent('UNKNOWN')); $record = $geodb->get($request->getIP()); @@ -1261,7 +1268,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') $session = $dbForProject->createDocument('sessions', $session); - $dbForProject->getAuthorization()->addRole(Role::user($userId)->toString()); + $authorization->addRole(Role::user($userId)->toString()); if (!Config::getParam('domainVerification')) { $response->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])); @@ -1294,7 +1301,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') $dbForProject->purgeCachedDocument('users', $user->getId()); - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); + $authorization->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); $queueForEvents ->setParam('userId', $user->getId()) @@ -1338,8 +1345,9 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') ->inject('project') ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->inject('queueForEvents') - ->action(function (string $teamId, string $membershipId, Document $user, Document $project, Response $response, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $teamId, string $membershipId, Document $user, Document $project, Response $response, Database $dbForProject, Authorization $authorization, Event $queueForEvents) { $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -1397,7 +1405,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') $dbForProject->purgeCachedDocument('users', $profile->getId()); if ($membership->getAttribute('confirm')) { // Count only confirmed members - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0)); + $authorization->skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0)); } $queueForEvents diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index d7b4b56806..f7a814c51f 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -43,6 +43,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\Query\Limit; @@ -2617,8 +2618,9 @@ App::get('/v1/users/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->inject('register') - ->action(function (string $range, Response $response, Database $dbForProject) { + ->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) { $periods = Config::getParam('usage', []); $stats = $usage = []; @@ -2628,7 +2630,7 @@ App::get('/v1/users/usage') METRIC_SESSIONS, ]; - $dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $count => $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 88998dd798..7e46e35299 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -27,6 +27,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Query\Cursor; use Utopia\Detector\Detection\Framework\Analog; use Utopia\Detector\Detection\Framework\Angular; @@ -70,7 +71,7 @@ use Utopia\VCS\Exception\RepositoryNotFound; use function Swoole\Coroutine\batch; -$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForPlatform, Build $queueForBuilds, callable $getProjectDB, Request $request) { +$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForPlatform, Authorization $authorization, Build $queueForBuilds, callable $getProjectDB, Request $request) { $errors = []; foreach ($repositories as $repository) { try { @@ -81,12 +82,12 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId } $projectId = $repository->getAttribute('projectId'); - $project = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('projects', $projectId)); + $project = $authorization->skip(fn () => $dbForPlatform->getDocument('projects', $projectId)); $dbForProject = $getProjectDB($project); $resourceCollection = $resourceType === "function" ? 'functions' : 'sites'; $resourceId = $repository->getAttribute('resourceId'); - $resource = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId)); + $resource = $authorization->skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId)); $resourceInternalId = $resource->getSequence(); $deploymentId = ID::unique(); @@ -135,7 +136,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $latestCommentId = ''; if (!empty($providerPullRequestId) && $resource->getAttribute('providerSilentMode', false) === false) { - $latestComment = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->findOne('vcsComments', [ + $latestComment = $authorization->skip(fn () => $dbForPlatform->findOne('vcsComments', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::equal('providerPullRequestId', [$providerPullRequestId]), Query::orderDesc('$createdAt'), @@ -174,7 +175,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment())); } finally { - Authorization::skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId)); + $authorization->skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId)); } } } else { @@ -185,7 +186,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId if (!empty($latestCommentId)) { $teamId = $project->getAttribute('teamId', ''); - $latestComment = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->createDocument('vcsComments', new Document([ + $latestComment = $authorization->skip(fn () => $dbForPlatform->createDocument('vcsComments', new Document([ '$id' => ID::unique(), '$permissions' => [ Permission::read(Role::team(ID::custom($teamId))), @@ -206,7 +207,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId } } } elseif (!empty($providerBranch)) { - $latestComments = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->find('vcsComments', [ + $latestComments = $authorization->skip(fn () => $dbForPlatform->find('vcsComments', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::equal('providerBranch', [$providerBranch]), Query::orderDesc('$createdAt'), @@ -245,7 +246,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment())); } finally { - Authorization::skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId)); + $authorization->skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId)); } } } @@ -288,7 +289,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $commands[] = $resource->getAttribute('commands', ''); } - $deployment = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('deployments', new Document([ + $deployment = $authorization->skip(fn () => $dbForProject->createDocument('deployments', new Document([ '$id' => $deploymentId, '$permissions' => [ Permission::read(Role::any()), @@ -328,7 +329,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId ->setAttribute('latestDeploymentInternalId', $deployment->getSequence()) ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource)); + $authorization->skip(fn () => $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource)); if ($resource->getCollection() === 'sites') { $projectId = $project->getId(); @@ -338,7 +339,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $domain = ID::unique() . "." . $sitesDomain; $ruleId = md5($domain); $previewRuleId = $ruleId; - $dbForProject->getAuthorization()->skip( + $authorization->skip( fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), @@ -371,7 +372,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $domain = "branch-{$branchPrefix}-{$resourceProjectHash}.{$sitesDomain}"; $ruleId = md5($domain); try { - $dbForProject->getAuthorization()->skip( + $authorization->skip( fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), @@ -402,7 +403,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $domain = "commit-" . substr($providerCommitHash, 0, 16) . ".{$sitesDomain}"; $ruleId = md5($domain); try { - $dbForProject->getAuthorization()->skip( + $authorization->skip( fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), @@ -454,7 +455,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId if ($lockAcquired) { // Wrap in try/finally to ensure lock file gets deleted try { - $rule = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('rules', $previewRuleId)); + $rule = $authorization->skip(fn () => $dbForPlatform->getDocument('rules', $previewRuleId)); $protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https'; $previewUrl = !empty($rule) ? ("{$protocol}://" . $rule->getAttribute('domain', '')) : ''; @@ -466,7 +467,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment()); } } finally { - Authorization::skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId)); + $authorization->skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId)); } } } @@ -1382,10 +1383,11 @@ App::post('/v1/vcs/github/events') ->inject('request') ->inject('response') ->inject('dbForPlatform') + ->inject('authorization') ->inject('getProjectDB') ->inject('queueForBuilds') ->action( - function (GitHub $github, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Build $queueForBuilds) use ($createGitDeployments) { + function (GitHub $github, Request $request, Response $response, Database $dbForPlatform, Authorization $authorization, callable $getProjectDB, Build $queueForBuilds) use ($createGitDeployments) { $payload = $request->getRawPayload(); $signatureRemote = $request->getHeader('x-hub-signature-256', ''); $signatureLocal = System::getEnv('_APP_VCS_GITHUB_WEBHOOK_SECRET', ''); @@ -1421,14 +1423,14 @@ App::post('/v1/vcs/github/events') $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); //find resourceId from relevant resources table - $repositories = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->find('repositories', [ + $repositories = $authorization->skip(fn () => $dbForPlatform->find('repositories', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::limit(100), ])); // create new deployment only on push (not committed by us) and not when branch is created or deleted if ($providerCommitAuthorEmail !== APP_VCS_GITHUB_EMAIL && !$providerBranchCreated && !$providerBranchDeleted) { - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthorName, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForPlatform, $queueForBuilds, $getProjectDB, $request); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthorName, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForPlatform, $authorization, $queueForBuilds, $getProjectDB, $request); } } elseif ($event == $github::EVENT_INSTALLATION) { if ($parsedPayload["action"] == "deleted") { @@ -1441,16 +1443,16 @@ App::post('/v1/vcs/github/events') ]); foreach ($installations as $installation) { - $repositories = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->find('repositories', [ + $repositories = $authorization->skip(fn () => $dbForPlatform->find('repositories', [ Query::equal('installationInternalId', [$installation->getSequence()]), Query::limit(1000) ])); foreach ($repositories as $repository) { - $dbForProject->getAuthorization()->skip(fn () => $dbForPlatform->deleteDocument('repositories', $repository->getId())); + $authorization->skip(fn () => $dbForPlatform->deleteDocument('repositories', $repository->getId())); } - Authorization::skip(fn () => $dbForPlatform->deleteDocument('installations', $installation->getId())); + $authorization->skip(fn () => $dbForPlatform->deleteDocument('installations', $installation->getId())); } } } elseif ($event == $github::EVENT_PULL_REQUEST) { @@ -1479,12 +1481,12 @@ App::post('/v1/vcs/github/events') $providerCommitAuthor = $commitDetails["commitAuthor"] ?? ''; $providerCommitMessage = $commitDetails["commitMessage"] ?? ''; - $repositories = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->find('repositories', [ + $repositories = $authorization->skip(fn () => $dbForPlatform->find('repositories', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::orderDesc('$createdAt') ])); - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForPlatform, $queueForBuilds, $getProjectDB, $request); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForPlatform, $authorization, $queueForBuilds, $getProjectDB, $request); } elseif ($parsedPayload["action"] == "closed") { // Allowed external contributions cleanup @@ -1493,7 +1495,7 @@ App::post('/v1/vcs/github/events') $external = $parsedPayload["external"] ?? true; if ($external) { - $repositories = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->find('repositories', [ + $repositories = $authorization->skip(fn () => $dbForPlatform->find('repositories', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::orderDesc('$createdAt') ])); @@ -1504,7 +1506,7 @@ App::post('/v1/vcs/github/events') if (\in_array($providerPullRequestId, $providerPullRequestIds)) { $providerPullRequestIds = \array_diff($providerPullRequestIds, [$providerPullRequestId]); $repository = $repository->setAttribute('providerPullRequestIds', $providerPullRequestIds); - $repository = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository)); + $repository = $authorization->skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository)); } } } @@ -1691,16 +1693,17 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor ->inject('response') ->inject('project') ->inject('dbForPlatform') + ->inject('authorization') ->inject('getProjectDB') ->inject('queueForBuilds') - ->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Request $request, Response $response, Document $project, Database $dbForPlatform, callable $getProjectDB, Build $queueForBuilds) use ($createGitDeployments) { + ->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Request $request, Response $response, Document $project, Database $dbForPlatform, Authorization $authorization, callable $getProjectDB, Build $queueForBuilds) use ($createGitDeployments) { $installation = $dbForPlatform->getDocument('installations', $installationId); if ($installation->isEmpty()) { throw new Exception(Exception::INSTALLATION_NOT_FOUND); } - $repository = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('repositories', $repositoryId, [ + $repository = $authorization->skip(fn () => $dbForPlatform->getDocument('repositories', $repositoryId, [ Query::equal('projectInternalId', [$project->getSequence()]) ])); @@ -1717,7 +1720,7 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor // TODO: Delete from array when PR is closed - $repository = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository)); + $repository = $authorization->skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository)); $privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); $githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID'); @@ -1741,7 +1744,7 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor $providerBranch = \explode(':', $pullRequestResponse['head']['label'])[1] ?? ''; $providerCommitHash = $pullRequestResponse['head']['sha'] ?? ''; - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerCommitHash, $providerPullRequestId, true, $dbForPlatform, $queueForBuilds, $getProjectDB, $request); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, '', '', '', '', $providerCommitHash, '', '', '', '', $providerPullRequestId, true, $dbForPlatform, $authorization, $queueForBuilds, $getProjectDB, $request); $response->noContent(); }); diff --git a/src/Appwrite/Platform/Modules/Console/Http/Resources/Get.php b/src/Appwrite/Platform/Modules/Console/Http/Resources/Get.php index 2abe4e0957..55a60a5cbf 100644 --- a/src/Appwrite/Platform/Modules/Console/Http/Resources/Get.php +++ b/src/Appwrite/Platform/Modules/Console/Http/Resources/Get.php @@ -10,6 +10,7 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Domains\Domain as Domain; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -58,6 +59,7 @@ class Get extends Action ->param('type', '', new WhiteList(['rules']), 'Resource type.') ->inject('response') ->inject('dbForPlatform') + ->inject('authorization') ->callback($this->action(...)); } @@ -65,7 +67,8 @@ class Get extends Action string $value, string $type, Response $response, - Database $dbForPlatform + Database $dbForPlatform, + Authorization $authorization ) { if ($type === 'rules') { $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); @@ -123,7 +126,7 @@ class Get extends Action throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain may not start with http:// or https://.'); } - $document = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->findOne('rules', [ + $document = $authorization()->skip(fn () => $dbForPlatform->findOne('rules', [ Query::equal('domain', [$value]), ])); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php index ec8005993c..07ab604a5a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php @@ -12,6 +12,7 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\IndexDependency as IndexDependencyValidator; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; @@ -66,12 +67,13 @@ class Delete extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void { - $db = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php index 158184e336..d31b17667e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php @@ -9,6 +9,7 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -67,12 +68,13 @@ class Get extends Action ->param('key', '', new Key(), 'Attribute Key.') ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject): void + public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void { - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php index a502c2012b..411fd3bf5b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php @@ -13,6 +13,7 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -81,15 +82,16 @@ class Create extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $relatedCollectionId, string $type, bool $twoWay, ?string $key, ?string $twoWayKey, string $onDelete, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + public function action(string $databaseId, string $collectionId, string $relatedCollectionId, string $type, bool $twoWay, ?string $key, ?string $twoWayKey, string $onDelete, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void { $key ??= $relatedCollectionId; $twoWayKey ??= $collectionId; - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php index e632f6397e..c56a3c5c00 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php @@ -14,6 +14,7 @@ use Utopia\Database\Document; use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -61,12 +62,13 @@ class XList extends Action ->param('queries', [], new Attributes(), '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(', ', Attributes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject): void + public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void { - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php index 8a9088382d..71ea9a99a7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php @@ -19,6 +19,7 @@ use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -76,12 +77,13 @@ class Create extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void + public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void { - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php index a9eebbc9d6..67f53fa33a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php @@ -12,6 +12,7 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -63,12 +64,13 @@ class Delete extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void { - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index bbf62b2e83..d42dca57fa 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -7,6 +7,7 @@ use Appwrite\Extend\Exception; use Appwrite\Platform\Action as AppwriteAction; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Validator\Authorization; abstract class Action extends AppwriteAction { @@ -257,6 +258,7 @@ abstract class Action extends AppwriteAction Document $collection, Document $document, Database $dbForProject, + Authorization $authorization, /* options */ array &$collectionsCache, @@ -296,7 +298,7 @@ abstract class Action extends AppwriteAction $relatedCollectionId = $relationship->getAttribute('relatedCollection'); if (!isset($collectionsCache[$relatedCollectionId])) { - $relatedCollectionDoc = $dbForProject->getAuthorization()->skip( + $relatedCollectionDoc = $authorization->skip( fn () => $dbForProject->getDocument( 'database_' . $database->getSequence(), $relatedCollectionId 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 9f92a707f8..904f3be95e 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 @@ -21,6 +21,7 @@ use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; use Utopia\Database\Exception\Type as TypeException; use Utopia\Database\Helpers\ID; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -83,20 +84,21 @@ class Decrement extends Action ->inject('queueForEvents') ->inject('queueForStatsUsage') ->inject('plan') + ->inject('authorization') ->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, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): 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, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan, Authorization $authorization): void { - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); } @@ -104,7 +106,7 @@ class Decrement extends Action // Handle transaction staging if ($transactionId !== null) { $transaction = ($isAPIKey || $isPrivilegedUser) - ? $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) + ? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) : $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); 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 b97a937e58..ec23672f98 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 @@ -21,6 +21,7 @@ use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; use Utopia\Database\Exception\Type as TypeException; use Utopia\Database\Helpers\ID; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -83,20 +84,21 @@ class Increment extends Action ->inject('queueForEvents') ->inject('queueForStatsUsage') ->inject('plan') + ->inject('authorization') ->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, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): 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, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan, Authorization $authorization): void { - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); } @@ -104,7 +106,7 @@ class Increment extends Action // Handle transaction staging if ($transactionId !== null) { $transaction = ($isAPIKey || $isPrivilegedUser) - ? $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) + ? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) : $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); 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 011a9f5a52..d7ec69777d 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 @@ -23,6 +23,7 @@ use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Authorization\Input; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; @@ -131,9 +132,10 @@ class Create extends Action ->inject('queueForFunctions') ->inject('queueForWebhooks') ->inject('plan') + ->inject('authorization') ->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, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void + public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan, Authorization $authorization): void { $data = \is_string($data) ? \json_decode($data, true) @@ -177,19 +179,19 @@ class Create extends Action $documents = [$data]; } - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization()->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization()->getRoles()); if ($isBulk && !$isAPIKey && !$isPrivilegedUser) { throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE); } - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + $collection = $authorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception($this->getParentNotFoundException()); } @@ -246,8 +248,8 @@ class Create extends Action $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!$dbForProject->getAuthorization()->hasRole($role)) { - throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $dbForProject->getAuthorization()->getRoles()) . ')'); + if (!$authorization()->hasRole($role)) { + throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $authorization()->getRoles()) . ')'); } } } @@ -263,20 +265,20 @@ class Create extends Action $documentSecurity = $collection->getAttribute('documentSecurity', false); - $validCollection = $dbForProject->getAuthorization()->isValid( + $validCollection = $authorization()->isValid( new Input($permission, $collection->getPermissionsByType($permission)) ); if (($permission === Database::PERMISSION_UPDATE && !$documentSecurity) || !$validCollection) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization()->getDescription()); } if ($permission === Database::PERMISSION_UPDATE) { - $validDocument = $dbForProject->getAuthorization()->isValid( + $validDocument = $authorization()->isValid( new Input($permission, $document->getUpdate()) ); $valid = $validCollection || $validDocument; if ($documentSecurity && !$valid) { - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization()->getDescription()); } } @@ -301,7 +303,7 @@ class Create extends Action } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $dbForProject->getAuthorization()->skip( + $relatedCollection = $authorization()->skip( fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) ); @@ -317,7 +319,7 @@ class Create extends Action if ($relation instanceof Document) { $relation = $this->removeReadonlyAttributes($relation, $isAPIKey || $isPrivilegedUser); - $current = $dbForProject->getAuthorization()->skip( + $current = $authorization()->skip( fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(), $relation->getId()) ); @@ -372,7 +374,7 @@ class Create extends Action // Handle transaction staging if ($transactionId !== null) { $transaction = ($isAPIKey || $isPrivilegedUser) - ? $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) + ? $authorization()->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) : $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty()) { throw new Exception(Exception::TRANSACTION_NOT_FOUND); 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 325072f97e..fe754b6a68 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 @@ -18,6 +18,7 @@ use Utopia\Database\Document; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Helpers\ID; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -81,6 +82,7 @@ class Delete extends Action ->inject('queueForStatsUsage') ->inject('transactionState') ->inject('plan') + ->inject('authorization') ->callback($this->action(...)); } @@ -95,18 +97,19 @@ class Delete extends Action Event $queueForEvents, StatsUsage $queueForStatsUsage, TransactionState $transactionState, - array $plan + array $plan, + Authorization $authorization ): void { - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception($this->getParentNotFoundException()); @@ -119,7 +122,7 @@ class Delete extends Action // Use transaction-aware document retrieval to see changes from same transaction $document = $transactionState->getDocument($collectionTableId, $documentId, $transactionId); } else { - $document = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId)); + $document = $authorization->skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId)); } if ($document->isEmpty()) { @@ -129,7 +132,7 @@ class Delete extends Action // Handle transaction staging if ($transactionId !== null) { $transaction = ($isAPIKey || $isPrivilegedUser) - ? $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) + ? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) : $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty()) { throw new Exception(Exception::TRANSACTION_NOT_FOUND); 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 93f66ed989..5dcef98b90 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 @@ -15,6 +15,7 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; @@ -68,20 +69,21 @@ class Get extends Action ->inject('dbForProject') ->inject('queueForStatsUsage') ->inject('transactionState') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionState $transactionState): void + public function action(string $databaseId, string $collectionId, string $documentId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionState $transactionState, Authorization $authorization): void { - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception($this->getParentNotFoundException()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php index 6c38a03ea2..68fd41221d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php @@ -18,6 +18,7 @@ use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; @@ -72,12 +73,13 @@ class XList extends Action ->inject('dbForProject') ->inject('locale') ->inject('geodb') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void + public function action(string $databaseId, string $collectionId, string $documentId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization): void { - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } 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 a969585b6c..128f57708f 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 @@ -22,6 +22,7 @@ use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -85,10 +86,11 @@ class Update extends Action ->inject('queueForStatsUsage') ->inject('transactionState') ->inject('plan') + ->inject('authorization') ->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, Event $queueForEvents, StatsUsage $queueForStatsUsage, TransactionState $transactionState, array $plan): void + public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, TransactionState $transactionState, array $plan, Authorization $authorization): void { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -96,16 +98,16 @@ class Update extends Action throw new Exception($this->getMissingPayloadException()); } - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception($this->getParentNotFoundException()); @@ -119,7 +121,7 @@ class Update extends Action // Use transaction-aware document retrieval to see changes from same transaction $document = $transactionState->getDocument($collectionTableId, $documentId, $transactionId); } else { - $document = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId)); + $document = $authorization->skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId)); } if ($document->isEmpty()) { @@ -134,7 +136,7 @@ class Update extends Action ]); // Users can only manage their own roles, API keys and Admin users can manage any - $roles = $dbForProject->getAuthorization()->getRoles(); + $roles = $authorization->getRoles(); if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { @@ -147,7 +149,7 @@ class Update extends Action $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!$dbForProject->getAuthorization()->hasRole($role)) { + if (!$authorization->hasRole($role)) { throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); } } @@ -189,7 +191,7 @@ class Update extends Action } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $dbForProject->getAuthorization()->skip( + $relatedCollection = $authorization->skip( fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) ); @@ -206,7 +208,7 @@ class Update extends Action if ($relation instanceof Document) { $relation = $this->removeReadonlyAttributes($relation, $isAPIKey || $isPrivilegedUser); - $oldDocument = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument( + $oldDocument = $authorization->skip(fn () => $dbForProject->getDocument( 'database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(), $relation->getId() )); @@ -244,7 +246,7 @@ class Update extends Action // Handle transaction staging if ($transactionId !== null) { $transaction = ($isAPIKey || $isPrivilegedUser) - ? $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) + ? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) : $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty()) { throw new Exception(Exception::TRANSACTION_NOT_FOUND); 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 6cf2f646eb..139ff7c466 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 @@ -23,6 +23,7 @@ use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -89,10 +90,11 @@ class Upsert extends Action ->inject('queueForStatsUsage') ->inject('transactionState') ->inject('plan') + ->inject('authorization') ->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, Event $queueForEvents, StatsUsage $queueForStatsUsage, TransactionState $transactionState, array $plan): void + public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Document $user, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, TransactionState $transactionState, array $plan, Authorization $authorization): void { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -104,15 +106,15 @@ class Upsert extends Action throw new Exception($this->getMissingPayloadException()); } - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception($this->getParentNotFoundException()); } @@ -133,7 +135,7 @@ class Upsert extends Action // Use transaction-aware document retrieval to see changes from same transaction $oldDocument = $transactionState->getDocument($collectionTableId, $documentId, $transactionId); } else { - $oldDocument = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId)); + $oldDocument = $authorization->skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId)); } if ($oldDocument->isEmpty()) { if (!empty($user->getId())) { @@ -149,7 +151,7 @@ class Upsert extends Action } // Users can only manage their own roles, API keys and Admin users can manage any - $roles = $dbForProject->getAuthorization()->getRoles(); + $roles = $authorization->getRoles(); if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { @@ -162,7 +164,7 @@ class Upsert extends Action $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!$dbForProject->getAuthorization()->hasRole($role)) { + if (!$authorization->hasRole($role)) { throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); } } @@ -199,7 +201,7 @@ class Upsert extends Action } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $dbForProject->getAuthorization()->skip( + $relatedCollection = $authorization->skip( fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) ); @@ -216,7 +218,7 @@ class Upsert extends Action if ($relation instanceof Document) { $relation = $this->removeReadonlyAttributes($relation, $isAPIKey || $isPrivilegedUser); - $oldDocument = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument( + $oldDocument = $authorization->skip(fn () => $dbForProject->getDocument( 'database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(), $relation->getId() )); @@ -253,7 +255,7 @@ class Upsert extends Action // Handle transaction staging if ($transactionId !== null) { $transaction = ($isAPIKey || $isPrivilegedUser) - ? $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) + ? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) : $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty()) { throw new Exception(Exception::TRANSACTION_NOT_FOUND); 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 dbb30fcc1b..483c34708a 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 @@ -17,6 +17,7 @@ use Utopia\Database\Document; use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -70,20 +71,21 @@ class XList extends Action ->inject('dbForProject') ->inject('queueForStatsUsage') ->inject('transactionState') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionState $transactionState): void + public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionState $transactionState, Authorization $authorization): void { - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception($this->getParentNotFoundException()); } @@ -111,7 +113,7 @@ class XList extends Action $documentId = $cursor->getValue(); - $cursorDocument = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId)); + $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId)); if ($cursorDocument->isEmpty()) { $type = ucfirst($this->getContext()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php index 9d4183d75f..725b53ff9e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php @@ -10,6 +10,7 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -56,12 +57,13 @@ class Get extends Action ->param('collectionId', '', new UID(), 'Collection ID.') ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject): void + public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void { - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php index 7c0d61b588..6afc29a265 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php @@ -16,6 +16,7 @@ use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Index as IndexValidator; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; @@ -78,12 +79,13 @@ class Create extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, array $lengths, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + public function action(string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, array $lengths, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void { - $db = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php index cc6ed66466..89d79ef353 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php @@ -12,6 +12,7 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -69,12 +70,13 @@ class Delete extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void { - $db = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php index 01b186b2c9..27e0767f88 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php @@ -10,6 +10,7 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -58,12 +59,13 @@ class Get extends Action ->param('key', null, new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject): void + public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void { - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php index 7828b91221..cacfd39f44 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php @@ -15,6 +15,7 @@ use Utopia\Database\Document; use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -63,13 +64,14 @@ class XList extends Action ->param('queries', [], new Indexes(), '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(', ', Indexes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject): void + public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void { /** @var Document $database */ - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -109,7 +111,7 @@ class XList extends Action } $indexId = $cursor->getValue(); - $cursorDocument = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->find('indexes', [ + $cursorDocument = $authorization->skip(fn () => $dbForProject->find('indexes', [ Query::equal('collectionInternalId', [$collection->getSequence()]), Query::equal('databaseInternalId', [$database->getSequence()]), Query::equal('key', [$indexId]), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php index a7f24e7c2b..928ba55ee0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php @@ -18,6 +18,7 @@ use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; @@ -71,12 +72,13 @@ class XList extends Action ->inject('dbForProject') ->inject('locale') ->inject('geodb') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void + public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization): void { - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php index 116b8cdccf..d1760089f6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php @@ -12,6 +12,7 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Helpers\Permission; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -69,12 +70,13 @@ class Update extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void + public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void { - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php index 9ade789cb9..beca779bf6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php @@ -14,6 +14,7 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\WhiteList; @@ -62,10 +63,11 @@ class Get extends Action ->param('collectionId', '', new UID(), 'Collection ID.') ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $range, string $collectionId, UtopiaResponse $response, Database $dbForProject): void + public function action(string $databaseId, string $range, string $collectionId, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void { $database = $dbForProject->getDocument('databases', $databaseId); $collectionDocument = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); @@ -82,7 +84,7 @@ class Get extends Action str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getSequence(), $collectionDocument->getSequence()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), ]; - $dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php index 43b4d59255..9e3829abe4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php @@ -15,6 +15,7 @@ use Utopia\Database\Document; use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -64,12 +65,13 @@ class XList extends Action ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, array $queries, string $search, UtopiaResponse $response, Database $dbForProject): void + public function action(string $databaseId, array $queries, string $search, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void { - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php index cd01a03ca4..41d3f3022a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php @@ -12,6 +12,7 @@ use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; +use Utopia\Database\Validator\Authorization; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Range; @@ -54,10 +55,11 @@ class Create extends Action ->inject('response') ->inject('dbForProject') ->inject('user') + ->inject('authorization') ->callback($this->action(...)); } - public function action(int $ttl, UtopiaResponse $response, Database $dbForProject, Document $user): void + public function action(int $ttl, UtopiaResponse $response, Database $dbForProject, Document $user, Authorization $authorization): void { $permissions = []; if (!empty($user->getId())) { @@ -72,7 +74,7 @@ class Create extends Action } } - $transaction = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('transactions', new Document([ + $transaction = $authorization->skip(fn () => $dbForProject->createDocument('transactions', new Document([ '$id' => ID::unique(), '$permissions' => $permissions, 'status' => 'pending', 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 1e90d7f2b5..98a0708f66 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 @@ -17,6 +17,7 @@ use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Authorization\Input; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -63,21 +64,22 @@ class Create extends Action ->inject('dbForProject') ->inject('transactionState') ->inject('plan') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $transactionId, array $operations, UtopiaResponse $response, Database $dbForProject, TransactionState $transactionState, array $plan): void + public function action(string $transactionId, array $operations, UtopiaResponse $response, Database $dbForProject, TransactionState $transactionState, array $plan, Authorization $authorization): void { if (empty($operations)) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Operations array cannot be empty'); } - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); // API keys and admins can read any transaction, regular users need permissions $transaction = ($isAPIKey || $isPrivilegedUser) - ? $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) + ? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) : $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty()) { throw new Exception(Exception::TRANSACTION_NOT_FOUND); @@ -113,13 +115,13 @@ class Create extends Action throw new Exception(Exception::USER_UNAUTHORIZED); } - $database = $databases[$operation['databaseId']] ??= $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $operation['databaseId'])); + $database = $databases[$operation['databaseId']] ??= $authorization->skip(fn () => $dbForProject->getDocument('databases', $operation['databaseId'])); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $collections[$operation[$this->getGroupId()]] ??= - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $operation[$this->getGroupId()])); + $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $operation[$this->getGroupId()])); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -166,17 +168,17 @@ class Create extends Action if (!$isAPIKey && !$isPrivilegedUser) { $documentSecurity = $collection->getAttribute('documentSecurity', false); - $collectionValid = $dbForProject->getAuthorization()->isValid( + $collectionValid = $authorization->isValid( new Input($permissionType, $collection->getPermissionsByType($permissionType)) ); $documentValid = false; if ($document !== null && !$document->isEmpty() && $documentSecurity) { if ($permissionType === Database::PERMISSION_UPDATE) { - $documentValid = $dbForProject->getAuthorization()->isValid( + $documentValid = $authorization->isValid( new Input(Database::PERMISSION_UPDATE, $document->getUpdate()) ); } elseif ($permissionType === Database::PERMISSION_DELETE) { - $documentValid = $dbForProject->getAuthorization()->isValid( + $documentValid = $authorization->isValid( new Input(Database::PERMISSION_DELETE, $document->getDelete()) ); } @@ -195,7 +197,7 @@ class Create extends Action // Users can only set permissions for roles they have if (isset($operation['data']['$permissions'])) { $permissions = $operation['data']['$permissions']; - $roles = $dbForProject->getAuthorization()->getRoles(); + $roles = $authorization->getRoles(); foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { $permission = Permission::parse($permission); @@ -207,7 +209,7 @@ class Create extends Action $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!$dbForProject->getAuthorization()->hasRole($role)) { + if (!$authorization->hasRole($role)) { throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); } } @@ -236,7 +238,7 @@ class Create extends Action } } - $transaction = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged, $existing, $operations) { + $transaction = $authorization->skip(fn () => $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged, $existing, $operations) { $dbForProject->createDocuments('transactionLogs', $staged); return $dbForProject->increaseDocumentAttribute( 'transactions', 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 ec43cfa87f..24e149800d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -76,6 +76,7 @@ class Update extends Action ->inject('queueForRealtime') ->inject('queueForFunctions') ->inject('queueForWebhooks') + ->inject('authorization') ->callback($this->action(...)); } @@ -102,7 +103,7 @@ class Update extends Action * @throws Structure * @throws \Utopia\Exception */ - public function action(string $transactionId, bool $commit, bool $rollback, UtopiaResponse $response, Database $dbForProject, Document $user, TransactionState $transactionState, Delete $queueForDeletes, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks): void + public function action(string $transactionId, bool $commit, bool $rollback, UtopiaResponse $response, Database $dbForProject, Document $user, TransactionState $transactionState, Delete $queueForDeletes, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, Authorization $authorization): void { if (!$commit && !$rollback) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Either commit or rollback must be true'); @@ -111,11 +112,11 @@ class Update extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Cannot commit and rollback at the same time'); } - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); $transaction = ($isAPIKey || $isPrivilegedUser) - ? $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) + ? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) : $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty()) { throw new Exception(Exception::TRANSACTION_NOT_FOUND); @@ -138,11 +139,11 @@ class Update extends Action try { $dbForProject->withTransaction(function () use ($dbForProject, $transactionState, $queueForDeletes, $transactionId, &$transaction, &$operations, &$totalOperations, &$databaseOperations, $queueForEvents, $queueForStatsUsage, $queueForRealtime, $queueForFunctions, $queueForWebhooks) { - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ + $authorization->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'committing', ]))); - $operations = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->find('transactionLogs', [ + $operations = $authorization->skip(fn () => $dbForProject->find('transactionLogs', [ Query::equal('transactionInternalId', [$transaction->getSequence()]), Query::orderAsc(), Query::limit(PHP_INT_MAX), @@ -218,7 +219,7 @@ class Update extends Action } } - $transaction = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument( + $transaction = $authorization->skip(fn () => $dbForProject->updateDocument( 'transactions', $transactionId, new Document(['status' => 'committed']) @@ -230,32 +231,32 @@ class Update extends Action }); } catch (NotFoundException $e) { - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ + $authorization->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ]))); throw new Exception(Exception::DOCUMENT_NOT_FOUND, previous: $e); } catch (DuplicateException|ConflictException $e) { - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ + $authorization->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ]))); throw new Exception(Exception::TRANSACTION_CONFLICT, previous: $e); } catch (StructureException $e) { - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ + $authorization->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ]))); throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); } catch (LimitException $e) { - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ + $authorization->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ]))); throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, $e->getMessage()); } catch (TransactionException $e) { - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ + $authorization->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ]))); throw new Exception(Exception::TRANSACTION_FAILED, $e->getMessage()); } catch (QueryException $e) { - $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ + $authorization->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ]))); throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); @@ -283,11 +284,11 @@ class Update extends Action $data = $data->getArrayCopy(); } - $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->findOne('databases', [ + $database = $authorization->skip(fn () => $dbForProject->findOne('databases', [ Query::equal('$sequence', [$databaseInternalId]) ])); - $collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->findOne('database_' . $databaseInternalId, [ + $collection = $authorization->skip(fn () => $dbForProject->findOne('database_' . $databaseInternalId, [ Query::equal('$sequence', [$collectionInternalId]) ])); @@ -379,7 +380,7 @@ class Update extends Action } if ($rollback) { - $transaction = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument( + $transaction = $authorization->skip(fn () => $dbForProject->updateDocument( 'transactions', $transactionId, new Document(['status' => 'failed']) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php index f75506e5cf..7668ad0a79 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php @@ -13,6 +13,7 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Swoole\Response as SwooleResponse; @@ -58,10 +59,11 @@ class Get extends Action ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $databaseId, string $range, UtopiaResponse $response, Database $dbForProject): void + public function action(string $databaseId, string $range, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void { $database = $dbForProject->getDocument('databases', $databaseId); @@ -80,7 +82,7 @@ class Get extends Action str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES) ]; - $dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php index 5927f98a02..757f845c68 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php @@ -12,6 +12,7 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Platform\Action; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\WhiteList; @@ -55,10 +56,11 @@ class XList extends Action ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $range, UtopiaResponse $response, Database $dbForProject): void + public function action(string $range, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void { $periods = Config::getParam('usage', []); @@ -73,7 +75,7 @@ class XList extends Action METRIC_DATABASES_OPERATIONS_WRITES, ]; - $dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php index 572736f975..a3b9b31380 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php @@ -26,6 +26,7 @@ use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Authorization\Input; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\UID; @@ -93,6 +94,7 @@ class Create extends Base ->inject('queueForFunctions') ->inject('geodb') ->inject('executor') + ->inject('authorization') ->callback($this->action(...)); } @@ -114,7 +116,8 @@ class Create extends Base StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, - Executor $executor + Executor $executor, + Authorization $authorization ) { $async = \strval($async) === 'true' || \strval($async) === '1'; @@ -152,10 +155,10 @@ class Create extends Base throw new Exception($validator->getDescription(), 400); } - $function = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::FUNCTION_NOT_FOUND); @@ -171,7 +174,7 @@ class Create extends Base throw new Exception(Exception::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); } - $deployment = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deploymentId', ''))); + $deployment = $authorization->skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deploymentId', ''))); if ($deployment->getAttribute('resourceId') !== $function->getId()) { throw new Exception(Exception::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); @@ -185,8 +188,8 @@ class Create extends Base throw new Exception(Exception::BUILD_NOT_READY); } - if (!$dbForProject->getAuthorization()->isValid(new Input('execute', $function->getAttribute('execute')))) { // Check if user has write access to execute function - throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription()); + if (!$authorization->isValid(new Input('execute', $function->getAttribute('execute')))) { // Check if user has write access to execute function + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } $jwt = ''; // initialize @@ -284,7 +287,7 @@ class Create extends Base if ($async) { if (is_null($scheduledAt)) { - $execution = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('executions', $execution)); + $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); $queueForFunctions ->setType('http') ->setExecution($execution) @@ -325,7 +328,7 @@ class Create extends Base ->setAttribute('scheduleInternalId', $schedule->getSequence()) ->setAttribute('scheduledAt', $scheduledAt); - $execution = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('executions', $execution)); + $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); } return $response @@ -478,7 +481,7 @@ class Create extends Base ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) ; - $execution = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('executions', $execution)); + $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); } $executionResponse['headers']['x-appwrite-execution-id'] = $execution->getId(); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Delete.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Delete.php index c9dffc82b8..ccf345dbb5 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Delete.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Delete.php @@ -13,6 +13,7 @@ use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -60,6 +61,7 @@ class Delete extends Base ->inject('dbForProject') ->inject('dbForPlatform') ->inject('queueForEvents') + ->inject('authorization') ->callback($this->action(...)); } @@ -69,7 +71,8 @@ class Delete extends Base Response $response, Database $dbForProject, Database $dbForPlatform, - Event $queueForEvents + Event $queueForEvents, + Authorization $authorization ) { $function = $dbForProject->getDocument('functions', $functionId); @@ -107,7 +110,7 @@ class Delete extends Base ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('active', false); - $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule)); + $authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule)); } } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Get.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Get.php index 1f11976b8a..69c4080f8a 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Get.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Get.php @@ -10,6 +10,7 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\Database\Database; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -51,6 +52,7 @@ class Get extends Base ->param('executionId', '', new UID(), 'Execution ID.') ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->callback($this->action(...)); } @@ -58,12 +60,13 @@ class Get extends Base string $functionId, string $executionId, Response $response, - Database $dbForProject + Database $dbForProject, + Authorization $authorization ) { - $function = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($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 3cd07b6cea..0237689b88 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/XList.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/XList.php @@ -15,6 +15,7 @@ use Utopia\Database\Document; use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; @@ -57,6 +58,7 @@ class XList extends Base ->param('queries', [], new Executions(), '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(', ', Executions::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->callback($this->action(...)); } @@ -64,12 +66,13 @@ class XList extends Base string $functionId, array $queries, Response $response, - Database $dbForProject + Database $dbForProject, + Authorization $authorization ) { - $function = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($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/Functions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php index 6105aa49c7..182f39e274 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php @@ -27,6 +27,7 @@ use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Roles; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -114,6 +115,7 @@ class Create extends Base ->inject('dbForPlatform') ->inject('request') ->inject('gitHub') + ->inject('authorization') ->callback($this->action(...)); } @@ -151,7 +153,8 @@ class Create extends Base Func $queueForFunctions, Database $dbForPlatform, Request $request, - GitHub $github + GitHub $github, + Authorization $authorization ) { // Temporary abuse check @@ -236,7 +239,7 @@ class Create extends Base throw new Exception(Exception::FUNCTION_ALREADY_EXISTS); } - $schedule = $dbForProject->getAuthorization()->skip( + $schedule = $authorization->skip( fn () => $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), 'resourceType' => SCHEDULE_RESOURCE_TYPE_FUNCTION, @@ -364,7 +367,7 @@ class Create extends Base // TODO: @christyjacob remove once we migrate the rules in 1.7.x $ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain) : ID::unique(); - $rule = $dbForPlatform->getAuthorization()->skip( + $rule = $authorization->skip( fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Delete.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Delete.php index 5b87031fa0..6c9c9c3c6f 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Delete.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Delete.php @@ -13,6 +13,7 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\DateTime; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -60,6 +61,7 @@ class Delete extends Base ->inject('queueForDeletes') ->inject('queueForEvents') ->inject('dbForPlatform') + ->inject('authorization') ->callback($this->action(...)); } @@ -69,7 +71,8 @@ class Delete extends Base Database $dbForProject, DeleteEvent $queueForDeletes, Event $queueForEvents, - Database $dbForPlatform + Database $dbForPlatform, + Authorization $authorization ) { $function = $dbForProject->getDocument('functions', $functionId); @@ -86,7 +89,7 @@ class Delete extends Base $schedule ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('active', false); - $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule)); + $authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule)); $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Deployment/Update.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Deployment/Update.php index c1851e3eb3..b913848a13 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Deployment/Update.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Deployment/Update.php @@ -13,6 +13,7 @@ use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -61,6 +62,7 @@ class Update extends Base ->inject('dbForProject') ->inject('queueForEvents') ->inject('dbForPlatform') + ->inject('authorization') ->callback($this->action(...)); } @@ -71,7 +73,8 @@ class Update extends Base Response $response, Database $dbForProject, Event $queueForEvents, - Database $dbForPlatform + Database $dbForPlatform, + Authorization $authorization ) { $function = $dbForProject->getDocument('functions', $functionId); $deployment = $dbForProject->getDocument('deployments', $deploymentId); @@ -100,7 +103,7 @@ class Update extends Base ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deploymentId'))); - $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule)); + $authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule)); $queries = [ Query::equal('trigger', ['manual']), @@ -111,12 +114,12 @@ class Update extends Base Query::equal('projectInternalId', [$project->getSequence()]) ]; - $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->foreach('rules', function (Document $rule) use ($dbForPlatform, $deployment) { + $authorization->skip(fn () => $dbForPlatform->foreach('rules', function (Document $rule) use ($dbForPlatform, $deployment, $authorization) { $rule = $rule ->setAttribute('deploymentId', $deployment->getId()) ->setAttribute('deploymentInternalId', $deployment->getSequence()); - $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), $rule)); + $authorization->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), $rule)); }, $queries)); $queueForEvents diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php index d571d81d84..d182bb67ea 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php @@ -22,6 +22,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Roles; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; @@ -103,6 +104,7 @@ class Update extends Base ->inject('dbForPlatform') ->inject('gitHub') ->inject('executor') + ->inject('authorization') ->callback($this->action(...)); } @@ -133,7 +135,8 @@ class Update extends Base Build $queueForBuilds, Database $dbForPlatform, GitHub $github, - Executor $executor + Executor $executor, + Authorization $authorization ) { // TODO: If only branch changes, re-deploy $function = $dbForProject->getDocument('functions', $functionId); @@ -281,7 +284,7 @@ class Update extends Base ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deploymentId'))); - $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule)); + $authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule)); $queueForEvents->setParam('functionId', $function->getId()); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Usage/Get.php b/src/Appwrite/Platform/Modules/Functions/Http/Usage/Get.php index 501b6bb468..1fa65d0cc9 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Usage/Get.php @@ -12,6 +12,7 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -54,10 +55,11 @@ class Get extends Base ->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true) ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $functionId, string $range, Response $response, Database $dbForProject) + public function action(string $functionId, string $range, Response $response, Database $dbForProject, Authorization $authorization) { $function = $dbForProject->getDocument('functions', $functionId); @@ -82,7 +84,7 @@ class Get extends Base str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_FAILED), ]; - $dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Usage/XList.php b/src/Appwrite/Platform/Modules/Functions/Http/Usage/XList.php index 2b8308a75a..38a95d4469 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Usage/XList.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Usage/XList.php @@ -11,6 +11,7 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Validator\WhiteList; @@ -51,10 +52,11 @@ class XList extends Base ->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true) ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $range, Response $response, Database $dbForProject) + public function action(string $range, Response $response, Database $dbForProject, Authorization $authorization) { $periods = Config::getParam('usage', []); $stats = $usage = []; @@ -74,7 +76,7 @@ class XList extends Base str_replace("{resourceType}", RESOURCE_TYPE_FUNCTIONS, METRIC_RESOURCE_TYPE_BUILDS_FAILED), ]; - $dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Variables/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Variables/Create.php index 4aedbdabc5..c61a610617 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Variables/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Variables/Create.php @@ -20,6 +20,7 @@ use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Validator\Boolean; use Utopia\Validator\Text; +use Utopia\Database\Validator\Authorization; class Create extends Base { @@ -64,6 +65,7 @@ class Create extends Base ->inject('dbForProject') ->inject('dbForPlatform') ->inject('project') + ->inject('authorization') ->callback($this->action(...)); } @@ -75,7 +77,8 @@ class Create extends Base Response $response, Database $dbForProject, Database $dbForPlatform, - Document $project + Document $project, + Authorization $authorization ) { $function = $dbForProject->getDocument('functions', $functionId); @@ -118,7 +121,7 @@ class Create extends Base ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deploymentId'))); - $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule)); + $authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule)); $response ->setStatusCode(Response::STATUS_CODE_CREATED) diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Variables/Delete.php b/src/Appwrite/Platform/Modules/Functions/Http/Variables/Delete.php index ecbdf3e13b..0ab315b212 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Variables/Delete.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Variables/Delete.php @@ -14,6 +14,7 @@ use Utopia\Database\DateTime; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; +use Utopia\Database\Validator\Authorization; class Delete extends Base { @@ -56,6 +57,7 @@ class Delete extends Base ->inject('response') ->inject('dbForProject') ->inject('dbForPlatform') + ->inject('authorization') ->callback($this->action(...)); } @@ -64,7 +66,8 @@ class Delete extends Base string $variableId, Response $response, Database $dbForProject, - Database $dbForPlatform + Database $dbForPlatform, + Authorization $authorization ) { $function = $dbForProject->getDocument('functions', $functionId); @@ -91,7 +94,7 @@ class Delete extends Base ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deploymentId'))); - $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule)); + $authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule)); $response->noContent(); } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Variables/Update.php b/src/Appwrite/Platform/Modules/Functions/Http/Variables/Update.php index d83af69a0b..e581c3e887 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Variables/Update.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Variables/Update.php @@ -16,6 +16,7 @@ use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Validator\Boolean; use Utopia\Validator\Text; +use Utopia\Database\Validator\Authorization; class Update extends Base { @@ -60,6 +61,7 @@ class Update extends Base ->inject('response') ->inject('dbForProject') ->inject('dbForPlatform') + ->inject('authorization') ->callback($this->action(...)); } @@ -71,7 +73,8 @@ class Update extends Base ?bool $secret, Response $response, Database $dbForProject, - Database $dbForPlatform + Database $dbForPlatform, + Authorization $authorization ) { $function = $dbForProject->getDocument('functions', $functionId); @@ -108,7 +111,7 @@ class Update extends Base ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deploymentId'))); - $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule)); + $authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule)); $response->dynamic($variable, Response::MODEL_VARIABLE); } diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php index 9e3812bb1c..b590f7c1d3 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php @@ -29,6 +29,7 @@ use Utopia\Swoole\Request; use Utopia\System\System; use Utopia\Validator\Boolean; use Utopia\Validator\Text; +use Utopia\Database\Validator\Authorization; class Create extends Action { @@ -85,6 +86,7 @@ class Create extends Action ->inject('deviceForLocal') ->inject('queueForBuilds') ->inject('plan') + ->inject('authorization') ->callback($this->action(...)); } @@ -104,7 +106,8 @@ class Create extends Action Device $deviceForSites, Device $deviceForLocal, Build $queueForBuilds, - array $plan + array $plan, + Authorization $authorization ) { $activate = \strval($activate) === 'true' || \strval($activate) === '1'; @@ -273,7 +276,7 @@ class Create extends Action // TODO: @christyjacob remove once we migrate the rules in 1.7.x $ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain) : ID::unique(); - $dbForPlatform->getAuthorization()->skip( + $authorization->skip( fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), @@ -338,7 +341,7 @@ class Create extends Action $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); $domain = ID::unique() . "." . $sitesDomain; $ruleId = md5($domain); - $dbForPlatform->getAuthorization()->skip( + $authorization->skip( fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php index 4a3b7c804f..21f86aea35 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php @@ -12,6 +12,7 @@ use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -64,6 +65,7 @@ class Create extends Action ->inject('queueForEvents') ->inject('queueForBuilds') ->inject('deviceForSites') + ->inject('authorization') ->callback($this->action(...)); } @@ -77,7 +79,8 @@ class Create extends Action Database $dbForPlatform, Event $queueForEvents, Build $queueForBuilds, - Device $deviceForSites + Device $deviceForSites, + Authorization $authorization ) { $site = $dbForProject->getDocument('sites', $siteId); @@ -145,7 +148,7 @@ class Create extends Action // TODO: @christyjacob remove once we migrate the rules in 1.7.x $ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain) : ID::unique(); - $dbForPlatform->getAuthorization()->skip( + $authorization->skip( fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php index 5888040476..7909b5ca3e 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php @@ -23,6 +23,7 @@ use Utopia\System\System; use Utopia\Validator\Boolean; use Utopia\Validator\Text; use Utopia\VCS\Adapter\Git\GitHub; +use Utopia\Database\Validator\Authorization; class Create extends Base { @@ -76,6 +77,7 @@ class Create extends Base ->inject('queueForEvents') ->inject('queueForBuilds') ->inject('gitHub') + ->inject('authorization') ->callback($this->action(...)); } @@ -93,7 +95,8 @@ class Create extends Base Document $project, Event $queueForEvents, Build $queueForBuilds, - GitHub $github + GitHub $github, + Authorization $authorization ) { $site = $dbForProject->getDocument('sites', $siteId); @@ -175,7 +178,7 @@ class Create extends Base // TODO: @christyjacob remove once we migrate the rules in 1.7.x $ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain) : ID::unique(); - $dbForPlatform->getAuthorization()->skip( + $authorization->skip( fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Deployment/Update.php b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Deployment/Update.php index d89014f7d8..382039f85d 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Deployment/Update.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Deployment/Update.php @@ -12,6 +12,7 @@ use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -59,6 +60,7 @@ class Update extends Base ->inject('dbForProject') ->inject('queueForEvents') ->inject('dbForPlatform') + ->inject('authorization') ->callback($this->action(...)); } @@ -69,7 +71,8 @@ class Update extends Base Response $response, Database $dbForProject, Event $queueForEvents, - Database $dbForPlatform + Database $dbForPlatform, + Authorization $authorization ) { $site = $dbForProject->getDocument('sites', $siteId); $deployment = $dbForProject->getDocument('deployments', $deploymentId); @@ -103,12 +106,12 @@ class Update extends Base Query::equal('projectInternalId', [$project->getSequence()]) ]; - $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->foreach('rules', function (Document $rule) use ($dbForPlatform, $deployment) { + $authorization->skip(fn () => $dbForPlatform->foreach('rules', function (Document $rule) use ($dbForPlatform, $deployment) { $rule = $rule ->setAttribute('deploymentId', $deployment->getId()) ->setAttribute('deploymentInternalId', $deployment->getSequence()); - $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), $rule)); + $authorization->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), $rule)); }, $queries)); $queueForEvents diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Usage/Get.php b/src/Appwrite/Platform/Modules/Sites/Http/Usage/Get.php index ca1ef5d9f3..5c274d6a20 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Usage/Get.php @@ -12,6 +12,7 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -54,6 +55,7 @@ class Get extends Base ->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true) ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->callback($this->action(...)); } @@ -61,7 +63,8 @@ class Get extends Base string $siteId, string $range, Response $response, - Database $dbForProject + Database $dbForProject, + Authorization $authorization ) { $site = $dbForProject->getDocument('sites', $siteId); @@ -90,7 +93,7 @@ class Get extends Base ]; - $dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Usage/XList.php b/src/Appwrite/Platform/Modules/Sites/Http/Usage/XList.php index f29ca2bec3..a90cb0cab9 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Usage/XList.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Usage/XList.php @@ -11,6 +11,7 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Validator\WhiteList; @@ -51,10 +52,11 @@ class XList extends Base ->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true) ->inject('response') ->inject('dbForProject') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $range, Response $response, Database $dbForProject) + public function action(string $range, Response $response, Database $dbForProject, Authorization $authorization) { $periods = Config::getParam('usage', []); $stats = $usage = []; @@ -77,7 +79,7 @@ class XList extends Base METRIC_SITES_OUTBOUND, ]; - $dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), 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 e0f51308d4..0a5a3da15b 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 @@ -13,6 +13,7 @@ use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Authorization\Input; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\UID; @@ -65,10 +66,11 @@ class Create extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') + ->inject('authorization') ->callback($this->action(...)); } - public function action(string $bucketId, string $fileId, ?string $expire, Response $response, Database $dbForProject, Event $queueForEvents): void + public function action(string $bucketId, string $fileId, ?string $expire, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void { /** @@ -78,10 +80,10 @@ class Create extends Action ['bucket' => $bucket, 'file' => $file] = $this->getFileAndBucket($dbForProject, $bucketId, $fileId); $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $bucketPermission = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_UPDATE, $bucket->getUpdate())); + $bucketPermission = $authorization->isValid(new Input(Database::PERMISSION_UPDATE, $bucket->getUpdate())); if ($fileSecurity) { - $filePermission = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_UPDATE, $file->getUpdate())); + $filePermission = $authorization->isValid(new Input(Database::PERMISSION_UPDATE, $file->getUpdate())); if (!$bucketPermission && !$filePermission) { throw new Exception(Exception::USER_UNAUTHORIZED); } diff --git a/src/Appwrite/Platform/Tasks/Migrate.php b/src/Appwrite/Platform/Tasks/Migrate.php index 4bab864653..1fd7525937 100644 --- a/src/Appwrite/Platform/Tasks/Migrate.php +++ b/src/Appwrite/Platform/Tasks/Migrate.php @@ -9,6 +9,7 @@ use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception; +use Utopia\Database\Validator\Authorization; use Utopia\Platform\Action; use Utopia\Registry\Registry; use Utopia\Validator\Text; @@ -30,6 +31,7 @@ class Migrate extends Action ->inject('dbForPlatform') ->inject('getProjectDB') ->inject('register') + ->inject('authorization') ->callback($this->action(...)); } @@ -46,8 +48,9 @@ class Migrate extends Action Database $dbForPlatform, callable $getProjectDB, Registry $register, + Authorization $authorization ): void { - $dbForPlatform->getAuthorization()->disable(); + $authorization->disable(); if (!\array_key_exists($version, Migration::$versions)) { Console::error("No migration found for version $version."); diff --git a/src/Appwrite/Platform/Tasks/StatsResources.php b/src/Appwrite/Platform/Tasks/StatsResources.php index 345c855920..b7da5d227d 100644 --- a/src/Appwrite/Platform/Tasks/StatsResources.php +++ b/src/Appwrite/Platform/Tasks/StatsResources.php @@ -8,6 +8,7 @@ use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\System\System; /** @@ -44,10 +45,11 @@ class StatsResources extends Action ->inject('dbForPlatform') ->inject('logError') ->inject('queueForStatsResources') + ->inject('authorization') ->callback($this->action(...)); } - public function action(Database $dbForPlatform, callable $logError, EventStatsResources $queue): void + public function action(Database $dbForPlatform, callable $logError, EventStatsResources $queue, Authorization $authorization): void { $this->logError = $logError; $this->dbForPlatform = $dbForPlatform; @@ -60,9 +62,9 @@ class StatsResources extends Action $interval = (int) System::getEnv('_APP_STATS_RESOURCES_INTERVAL', '3600'); - Console::loop(function () use ($queue, $dbForPlatform) { - $dbForPlatform->getAuthorization()->disable(); - $dbForPlatform->getAuthorization()->setDefaultStatus(false); + Console::loop(function () use ($queue, $dbForPlatform, $authorization) { + $authorization->disable(); + $authorization->setDefaultStatus(false); $last24Hours = (new \DateTime())->sub(\DateInterval::createFromDateString('24 hours')); /**