Refactor authorization handling across multiple modules to use a single instance of the Authorization class.

This commit is contained in:
shimon
2025-10-29 20:21:41 +02:00
parent 9503507011
commit 5c2828bc78
63 changed files with 647 additions and 457 deletions
+2 -1
View File
@@ -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());
+81 -59
View File
@@ -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);
+17 -13
View File
@@ -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'] ?? '';
+35 -27
View File
@@ -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.");
+12 -8
View File
@@ -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);
}
+6 -4
View File
@@ -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),
+99 -87
View File
@@ -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
+36 -28
View File
@@ -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
+4 -2
View File
@@ -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]),
+32 -29
View File
@@ -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();
});
@@ -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]),
]));
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
@@ -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);
}
@@ -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
@@ -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 nonpending transaction');
@@ -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 nonpending transaction');
@@ -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);
@@ -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);
@@ -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());
@@ -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);
}
@@ -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);
@@ -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);
@@ -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());
@@ -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);
@@ -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);
@@ -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);
@@ -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);
@@ -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]),
@@ -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);
@@ -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);
}
@@ -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]),
@@ -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);
@@ -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',
@@ -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',
@@ -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'])
@@ -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]),
@@ -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]),
@@ -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();
@@ -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));
}
}
@@ -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);
@@ -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);
@@ -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(),
@@ -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)
@@ -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
@@ -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());
@@ -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]),
@@ -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]),
@@ -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)
@@ -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();
}
@@ -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);
}
@@ -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(),
@@ -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(),
@@ -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(),
@@ -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
@@ -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]),
@@ -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]),
@@ -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);
}
+4 -1
View File
@@ -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.");
@@ -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'));
/**