Refactor authorization handling across multiple modules to use the new getAuthorization() method. Update CHANGES.md to reflect the change in usage worker authorization. Adjust composer.json to specify the correct version for utopia-php/database. Update plugin-api-version in composer.lock.

This commit is contained in:
shimon
2025-10-22 16:35:48 +03:00
parent 16a2dd0b27
commit 33f88fa5e0
91 changed files with 688 additions and 551 deletions
+1 -1
View File
@@ -1155,7 +1155,7 @@
* Bump console to version 3.2.7 [#7148](https://github.com/appwrite/appwrite/pull/7148)
* Chore update database to 0.45.2 [#7138](https://github.com/appwrite/appwrite/pull/7138)
* Implement queue thresholds for the health API [#7123](https://github.com/appwrite/appwrite/pull/7123)
* Add Authorization::skip to the usage worker [#7124](https://github.com/appwrite/appwrite/pull/7124)
* Add Authorization->skip to the usage worker [#7124](https://github.com/appwrite/appwrite/pull/7124)
## Bug fixes
* fix: use queueForDeletes in git installation delete endpoint [#7140](https://github.com/appwrite/appwrite/pull/7140)
+19 -10
View File
@@ -40,8 +40,6 @@ Config::setParam('runtimes', (new Runtimes('v5'))->getAll(supported: false));
// require controllers after overwriting runtimes
require_once __DIR__ . '/controllers/general.php';
Authorization::disable();
CLI::setResource('register', fn () => $register);
CLI::setResource('cache', function ($pools) {
@@ -59,7 +57,12 @@ CLI::setResource('pools', function (Registry $register) {
return $register->get('pools');
}, ['register']);
CLI::setResource('dbForPlatform', function ($pools, $cache) {
CLI::setResource('authorization', function () {
$authorization = new Authorization();
return $authorization;
}, []);
CLI::setResource('dbForPlatform', function ($pools, $cache, $authorization) {
$sleep = 3;
$maxAttempts = 5;
$attempts = 0;
@@ -73,6 +76,7 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) {
$dbForPlatform = new Database($adapter, $cache);
$dbForPlatform
->setAuthorization($authorization)
->setNamespace('_console')
->setMetadata('host', \gethostname())
->setMetadata('project', 'console');
@@ -97,7 +101,7 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) {
}
return $dbForPlatform;
}, ['pools', 'cache']);
}, ['pools', 'cache', 'authorization']);
CLI::setResource('console', function () {
return new Document(Config::getParam('console'));
@@ -108,10 +112,10 @@ CLI::setResource(
fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false
);
CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache, $authorization) {
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases) {
return function (Document $project) use ($pools, $dbForPlatform, $cache, $authorization, &$databases) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
@@ -129,11 +133,13 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
if (\in_array($dsn->getHost(), $sharedTables)) {
$database
->setAuthorization($authorization)
->setSharedTables(true)
->setTenant((int)$project->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
->setAuthorization($authorization)
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getSequence());
@@ -149,11 +155,13 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
if (\in_array($dsn->getHost(), $sharedTables)) {
$database
->setAuthorization($authorization)
->setSharedTables(true)
->setTenant((int)$project->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
->setAuthorization($authorization)
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getSequence());
@@ -165,12 +173,12 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
return $database;
};
}, ['pools', 'dbForPlatform', 'cache']);
}, ['pools', 'dbForPlatform', 'cache', 'authorization']);
CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
CLI::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
$database = null;
return function (?Document $project = null) use ($pools, $cache, $database) {
return function (?Document $project = null) use ($pools, $cache, $database, $authorization) {
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant((int)$project->getSequence());
return $database;
@@ -180,6 +188,7 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database = new Database($adapter, $cache);
$database
->setAuthorization($authorization)
->setSharedTables(true)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_TASK)
@@ -192,7 +201,7 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
return $database;
};
}, ['pools', 'cache']);
}, ['pools', 'cache', 'authorization']);
CLI::setResource('publisher', function (Group $pools) {
return new BrokerPool(publisher: $pools->get('publisher'));
}, ['pools']);
+40 -40
View File
@@ -173,7 +173,7 @@ 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) {
/** @var Utopia\Database\Document $user */
$userFromRequest = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId));
$userFromRequest = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('users', $userId));
if ($userFromRequest->isEmpty()) {
throw new Exception(Exception::USER_INVALID_TOKEN);
@@ -219,7 +219,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
$detector->getDevice()
));
Authorization::setRole(Role::user($user->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString());
$session = $dbForProject->createDocument('sessions', $session
->setAttribute('$permissions', [
@@ -228,7 +228,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
Permission::delete(Role::user($user->getId())),
]));
Authorization::skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId()));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId()));
$dbForProject->purgeCachedDocument('users', $user->getId());
// Magic URL + Email OTP
@@ -403,9 +403,9 @@ App::post('/v1/account')
'accessedAt' => DateTime::now(),
]);
$user->removeAttribute('$sequence');
$user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
$user = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('users', $user));
try {
$target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([
$target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
@@ -431,9 +431,9 @@ App::post('/v1/account')
throw new Exception(Exception::USER_ALREADY_EXISTS);
}
Authorization::unsetRole(Role::guests()->toString());
Authorization::setRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::users()->toString());
$dbForProject->getAuthorization()->removeRole(Role::guests()->toString());
$dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::users()->toString());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -937,7 +937,7 @@ App::post('/v1/account/sessions/email')
$detector->getDevice()
));
Authorization::setRole(Role::user($user->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString());
// Re-hash if not using recommended algo
if ($user->getAttribute('hash') !== Auth::DEFAULT_ALGO) {
@@ -1074,7 +1074,7 @@ App::post('/v1/account/sessions/anonymous')
'accessedAt' => DateTime::now(),
]);
$user->removeAttribute('$sequence');
Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
$dbForProject->getAuthorization()-> skip(fn () => $dbForProject->createDocument('users', $user));
// Create session token
$duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
@@ -1100,7 +1100,7 @@ App::post('/v1/account/sessions/anonymous')
$detector->getDevice()
));
Authorization::setRole(Role::user($user->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString());
$session = $dbForProject->createDocument('sessions', $session->setAttribute('$permissions', [
Permission::read(Role::user($user->getId())),
@@ -1606,7 +1606,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'accessedAt' => DateTime::now(),
]);
$user->removeAttribute('$sequence');
$userDoc = Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
$userDoc = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('users', $user));
$dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
@@ -1625,8 +1625,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
}
}
Authorization::setRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::users()->toString());
$dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::users()->toString());
if (false === $user->getAttribute('status')) { // Account is blocked
$failureRedirect(Exception::USER_BLOCKED); // User is in status blocked
@@ -1685,7 +1685,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$dbForProject->updateDocument('users', $user->getId(), $user);
Authorization::setRole(Role::user($user->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString());
$state['success'] = URLParser::parse($state['success']);
$query = URLParser::parseQuery($state['success']['query']);
@@ -1707,7 +1707,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'ip' => $request->getIP(),
]);
Authorization::setRole(Role::user($user->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
@@ -1996,7 +1996,7 @@ App::post('/v1/account/tokens/magic-url')
]);
$user->removeAttribute('$sequence');
Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('users', $user));
}
$tokenSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_MAGIC_URL);
@@ -2013,7 +2013,7 @@ App::post('/v1/account/tokens/magic-url')
'ip' => $request->getIP(),
]);
Authorization::setRole(Role::user($user->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
@@ -2244,9 +2244,9 @@ App::post('/v1/account/tokens/email')
]);
$user->removeAttribute('$sequence');
$user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
$user = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('users', $user));
try {
$target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([
$target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
@@ -2284,7 +2284,7 @@ App::post('/v1/account/tokens/email')
'ip' => $request->getIP(),
]);
Authorization::setRole(Role::user($user->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
@@ -2591,9 +2591,9 @@ App::post('/v1/account/tokens/phone')
]);
$user->removeAttribute('$sequence');
Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('users', $user));
try {
$target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([
$target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
@@ -2639,7 +2639,7 @@ App::post('/v1/account/tokens/phone')
'ip' => $request->getIP(),
]);
Authorization::setRole(Role::user($user->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
@@ -3063,7 +3063,7 @@ App::patch('/v1/account/email')
->setAttribute('passwordUpdate', DateTime::now());
}
$target = Authorization::skip(fn () => $dbForProject->findOne('targets', [
$target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->findOne('targets', [
Query::equal('identifier', [$email]),
]));
@@ -3079,7 +3079,7 @@ App::patch('/v1/account/email')
$oldTarget = $user->find('identifier', $oldEmail, 'targets');
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
Authorization::skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email)));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email)));
}
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Duplicate) {
@@ -3134,7 +3134,7 @@ App::patch('/v1/account/phone')
$hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]);
$target = Authorization::skip(fn () => $dbForProject->findOne('targets', [
$target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->findOne('targets', [
Query::equal('identifier', [$phone]),
]));
@@ -3165,7 +3165,7 @@ App::patch('/v1/account/phone')
$oldTarget = $user->find('identifier', $oldPhone, 'targets');
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
Authorization::skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone)));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone)));
}
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Duplicate $th) {
@@ -3335,7 +3335,7 @@ App::post('/v1/account/recovery')
'ip' => $request->getIP(),
]);
Authorization::setRole(Role::user($profile->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::user($profile->getId())->toString());
$recovery = $dbForProject->createDocument('tokens', $recovery
->setAttribute('$permissions', [
@@ -3491,7 +3491,7 @@ App::put('/v1/account/recovery')
throw new Exception(Exception::USER_INVALID_TOKEN);
}
Authorization::setRole(Role::user($profile->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::user($profile->getId())->toString());
$newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
@@ -3618,7 +3618,7 @@ App::post('/v1/account/verifications/email')
'ip' => $request->getIP(),
]);
Authorization::setRole(Role::user($user->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString());
$verification = $dbForProject->createDocument('tokens', $verification
->setAttribute('$permissions', [
@@ -3803,7 +3803,7 @@ App::put('/v1/account/verifications/email')
->inject('queueForEvents')
->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
$profile = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId));
$profile = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('users', $userId));
if ($profile->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
@@ -3816,7 +3816,7 @@ App::put('/v1/account/verifications/email')
throw new Exception(Exception::USER_INVALID_TOKEN);
}
Authorization::setRole(Role::user($profile->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::user($profile->getId())->toString());
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true));
@@ -3914,7 +3914,7 @@ App::post('/v1/account/verifications/phone')
'ip' => $request->getIP(),
]);
Authorization::setRole(Role::user($user->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString());
$verification = $dbForProject->createDocument('tokens', $verification
->setAttribute('$permissions', [
@@ -4021,7 +4021,7 @@ App::put('/v1/account/verifications/phone')
->inject('queueForEvents')
->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
$profile = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId));
$profile = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('users', $userId));
if ($profile->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
@@ -4033,7 +4033,7 @@ App::put('/v1/account/verifications/phone')
throw new Exception(Exception::USER_INVALID_TOKEN);
}
Authorization::setRole(Role::user($profile->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::user($profile->getId())->toString());
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('phoneVerification', true));
@@ -5024,9 +5024,9 @@ App::post('/v1/account/targets/push')
->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) {
$targetId = $targetId == 'unique()' ? ID::unique() : $targetId;
$provider = Authorization::skip(fn () => $dbForProject->getDocument('providers', $providerId));
$provider = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('providers', $providerId));
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
$target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('targets', $targetId));
if (!$target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
@@ -5103,7 +5103,7 @@ App::put('/v1/account/targets/:targetId/push')
->inject('dbForProject')
->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) {
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
$target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('targets', $targetId));
if ($target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
@@ -5167,7 +5167,7 @@ App::delete('/v1/account/targets/:targetId/push')
->inject('response')
->inject('dbForProject')
->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject) {
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
$target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('targets', $targetId));
if ($target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
+6 -6
View File
@@ -69,7 +69,7 @@ $avatarCallback = function (string $type, string $code, int $width, int $height,
$getUserGitHub = function (string $userId, Document $project, Database $dbForProject, Database $dbForPlatform, ?Logger $logger) {
try {
$user = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
$user = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('users', $userId));
$sessions = $user->getAttribute('sessions', []);
@@ -120,7 +120,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
->setAttribute('providerRefreshToken', $refreshToken)
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$oauth2->getAccessTokenExpiry('')));
Authorization::skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession));
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Throwable $err) {
@@ -128,7 +128,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
do {
$previousAccessToken = $gitHubSession->getAttribute('providerAccessToken');
$user = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
$user = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('users', $userId));
$sessions = $user->getAttribute('sessions', []);
$gitHubSession = new Document();
@@ -658,7 +658,7 @@ App::get('/v1/cards/cloud')
->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 = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
$user = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('users', $userId));
if ($user->isEmpty() && empty($mock)) {
throw new Exception(Exception::USER_NOT_FOUND);
@@ -865,7 +865,7 @@ App::get('/v1/cards/cloud-back')
->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 = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
$user = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('users', $userId));
if ($user->isEmpty() && empty($mock)) {
throw new Exception(Exception::USER_NOT_FOUND);
@@ -943,7 +943,7 @@ App::get('/v1/cards/cloud-og')
->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 = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
$user = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('users', $userId));
if ($user->isEmpty() && empty($mock)) {
throw new Exception(Exception::USER_NOT_FOUND);
+3 -2
View File
@@ -28,11 +28,12 @@ use Utopia\Validator\Text;
App::init()
->groups(['graphql'])
->inject('project')
->action(function (Document $project) {
->inject('authorization')
->action(function (Document $project, Authorization $authorization) {
if (
array_key_exists('graphql', $project->getAttribute('apis', []))
&& !$project->getAttribute('apis', [])['graphql']
&& !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles()))
&& !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles()))
) {
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
}
+19 -22
View File
@@ -35,7 +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;
use Utopia\Database\Validator\Query\Cursor;
@@ -1013,7 +1013,7 @@ App::get('/v1/messaging/providers')
}
$providerId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('providers', $providerId));
$cursorDocument = $dbForProject->getAuthorization()->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.");
@@ -2320,7 +2320,7 @@ App::get('/v1/messaging/topics')
}
$topicId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
$cursorDocument = $dbForProject->getAuthorization()->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.");
@@ -2599,25 +2599,22 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
->action(function (string $subscriberId, string $topicId, string $targetId, Event $queueForEvents, Database $dbForProject, Response $response) {
$subscriberId = $subscriberId == 'unique()' ? ID::unique() : $subscriberId;
$topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
$topic = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('topics', $topicId));
if ($topic->isEmpty()) {
throw new Exception(Exception::TOPIC_NOT_FOUND);
}
$validator = new Authorization('subscribe');
if (!$validator->isValid($topic->getAttribute('subscribe'))) {
throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription());
if (!$dbForProject->getAuthorization()->isValid(new Input('subscribe', $topic->getAttribute('subscribe')))) {
throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription());
}
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
$target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('targets', $targetId));
if ($target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
}
$user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
$user = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
$subscriber = new Document([
'$id' => $subscriberId,
@@ -2650,7 +2647,7 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE),
};
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute(
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->increaseDocumentAttribute(
'topics',
$topicId,
$totalAttribute,
@@ -2706,7 +2703,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
$queries[] = Query::search('search', $search);
}
$topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
$topic = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('topics', $topicId));
if ($topic->isEmpty()) {
throw new Exception(Exception::TOPIC_NOT_FOUND);
@@ -2729,7 +2726,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
}
$subscriberId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId));
$cursorDocument = $dbForProject->getAuthorization()->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.");
@@ -2745,8 +2742,8 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
$subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject) {
return function () use ($subscriber, $dbForProject) {
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
$user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
$target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
$user = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
return $subscriber
->setAttribute('target', $target)
@@ -2882,7 +2879,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->inject('dbForProject')
->inject('response')
->action(function (string $topicId, string $subscriberId, Database $dbForProject, Response $response) {
$topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
$topic = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('topics', $topicId));
if ($topic->isEmpty()) {
throw new Exception(Exception::TOPIC_NOT_FOUND);
@@ -2894,8 +2891,8 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
throw new Exception(Exception::SUBSCRIBER_NOT_FOUND);
}
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
$user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
$target = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
$user = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
$subscriber
->setAttribute('target', $target)
@@ -2933,7 +2930,7 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->inject('dbForProject')
->inject('response')
->action(function (string $topicId, string $subscriberId, Event $queueForEvents, Database $dbForProject, Response $response) {
$topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
$topic = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('topics', $topicId));
if ($topic->isEmpty()) {
throw new Exception(Exception::TOPIC_NOT_FOUND);
@@ -2956,7 +2953,7 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE),
};
Authorization::skip(fn () => $dbForProject->decreaseDocumentAttribute(
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->decreaseDocumentAttribute(
'topics',
$topicId,
$totalAttribute,
@@ -3539,7 +3536,7 @@ App::get('/v1/messaging/messages')
}
$messageId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('messages', $messageId));
$cursorDocument = $dbForProject->getAuthorization()->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.");
+4 -4
View File
@@ -339,12 +339,12 @@ App::post('/v1/migrations/csv')
->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 $deviceForImports, Event $queueForEvents, Migration $queueForMigrations) {
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForPlatform->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForPlatform->getAuthorization()->getRoles());
if ($internalFile && !$isPrivilegedUser) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$bucket = Authorization::skip(function () use ($internalFile, $dbForPlatform, $dbForProject, $bucketId) {
$bucket = $dbForPlatform->getAuthorization()->skip(function () use ($internalFile, $dbForPlatform, $dbForProject, $bucketId) {
if ($internalFile) {
return $dbForPlatform->getDocument('buckets', 'default');
}
@@ -355,7 +355,7 @@ App::post('/v1/migrations/csv')
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$file = Authorization::skip(fn () => $internalFile ? $dbForPlatform->getDocument('bucket_' . $bucket->getSequence(), $fileId) : $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = $dbForPlatform->getAuthorization()->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);
}
+3 -3
View File
@@ -101,7 +101,7 @@ App::get('/v1/project/usage')
'1d' => 'Y-m-d\T00:00:00.000P',
};
Authorization::skip(function () use ($dbForProject, $dbForLogs, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) {
$dbForProject->getAuthorization()->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;
@@ -285,7 +285,7 @@ App::get('/v1/project/usage')
}, $dbForProject->find('functions'));
// This total is includes free and paid SMS usage
$authPhoneTotal = Authorization::skip(fn () => $dbForProject->sum('stats', 'value', [
$authPhoneTotal = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->sum('stats', 'value', [
Query::equal('metric', [METRIC_AUTH_METHOD_PHONE]),
Query::equal('period', ['1d']),
Query::greaterThanEqual('time', $firstDay),
@@ -293,7 +293,7 @@ App::get('/v1/project/usage')
]));
// This estimate is only for paid SMS usage
$authPhoneMetrics = Authorization::skip(fn () => $dbForProject->find('stats', [
$authPhoneMetrics = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->find('stats', [
Query::startsWith('metric', METRIC_AUTH_METHOD_PHONE . '.'),
Query::equal('period', ['1d']),
Query::greaterThanEqual('time', $firstDay),
+71 -80
View File
@@ -31,7 +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;
use Utopia\Database\Validator\UID;
@@ -428,18 +428,17 @@ App::post('/v1/storage/buckets/:bucketId/files')
->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) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$validator = new Authorization(Database::PERMISSION_CREATE);
if (!$validator->isValid($bucket->getCreate())) {
throw new Exception(Exception::USER_UNAUTHORIZED);
if (!$dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) {
throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription());
}
$allowedPermissions = [
@@ -462,7 +461,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
}
// Users can only manage their own roles, API keys and Admin users can manage any
$roles = Authorization::getRoles();
$roles = $dbForProject->getAuthorization()->getRoles();
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
foreach (Database::PERMISSIONS as $type) {
foreach ($permissions as $permission) {
@@ -475,7 +474,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
if (!Authorization::isRole($role)) {
if (!$dbForProject->getAuthorization()->hasRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
}
}
@@ -695,11 +694,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
*/
$validator = new Authorization(Database::PERMISSION_CREATE);
if (!$validator->isValid($bucket->getCreate())) {
throw new Exception(Exception::USER_UNAUTHORIZED);
if (!$dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) {
throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription());
}
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
$file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
}
} else {
if ($file->isEmpty()) {
@@ -738,13 +736,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
*/
$validator = new Authorization(Database::PERMISSION_CREATE);
if (!$validator->isValid($bucket->getCreate())) {
throw new Exception(Exception::USER_UNAUTHORIZED);
if (!$dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) {
throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription());
}
try {
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
$file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
} catch (NotFoundException) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
@@ -789,20 +786,19 @@ App::get('/v1/storage/buckets/:bucketId/files')
->inject('dbForProject')
->inject('mode')
->action(function (string $bucketId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
$valid = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
if (!$fileSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription());
}
$queries = Query::parseQueries($queries);
@@ -831,7 +827,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
if ($fileSecurity && !$valid) {
$cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$cursorDocument = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
if ($cursorDocument->isEmpty()) {
@@ -848,8 +844,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 = Authorization::skip(fn () => $dbForProject->find('bucket_' . $bucket->getSequence(), $queries));
$total = Authorization::skip(fn () => $dbForProject->count('bucket_' . $bucket->getSequence(), $filterQueries, APP_LIMIT_COUNT));
$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));
}
} catch (NotFoundException) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
@@ -890,26 +886,25 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
->inject('dbForProject')
->inject('mode')
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, string $mode) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
$valid = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
if (!$fileSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription());
}
if ($fileSecurity && !$valid) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
if ($file->isEmpty()) {
@@ -972,10 +967,10 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
}
/* @type Document $bucket */
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
@@ -983,17 +978,16 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
$valid = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
if (!$fileSecurity && !$valid && !$isToken) {
throw new Exception(Exception::USER_UNAUTHORIZED);
throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription());
}
if ($fileSecurity && !$valid && !$isToken) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
/* @type Document $file */
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) {
@@ -1115,11 +1109,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(Authorization::getRoles())) {
if (!Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles())) {
$transformedAt = $file->getAttribute('transformedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) {
$file->setAttribute('transformedAt', DateTime::now());
Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file));
}
}
@@ -1164,10 +1158,10 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
->inject('deviceForFiles')
->action(function (string $bucketId, string $fileId, ?string $token, Request $request, Response $response, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) {
/* @type Document $bucket */
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
@@ -1175,8 +1169,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
$valid = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
if (!$fileSecurity && !$valid && !$isToken) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
@@ -1185,7 +1178,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
/* @type Document $file */
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) {
@@ -1325,10 +1318,10 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
->inject('deviceForFiles')
->action(function (string $bucketId, string $fileId, ?string $token, Response $response, Request $request, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) {
/* @type Document $bucket */
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
@@ -1336,8 +1329,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
$valid = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
if (!$fileSecurity && !$valid && !$isToken) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
@@ -1346,7 +1338,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
/* @type Document $file */
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) {
@@ -1479,7 +1471,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
->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 = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
@@ -1497,14 +1489,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
@@ -1652,24 +1644,23 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
->inject('queueForEvents')
->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_UPDATE);
$valid = $validator->isValid($bucket->getUpdate());
$valid = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_UPDATE, $bucket->getUpdate()));
if (!$fileSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription());
}
// Read permission should not be required for update
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
@@ -1683,7 +1674,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 = Authorization::getRoles();
$roles = $dbForProject->getAuthorization()->getRoles();
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles) && !\is_null($permissions)) {
foreach (Database::PERMISSIONS as $type) {
foreach ($permissions as $permission) {
@@ -1696,7 +1687,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
if (!Authorization::isRole($role)) {
if (!$dbForProject->getAuthorization()->hasRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
}
}
@@ -1717,7 +1708,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
if ($fileSecurity && !$valid) {
$file = $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file);
} else {
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
$file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
}
} catch (NotFoundException) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
@@ -1766,32 +1757,32 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
->inject('deviceForFiles')
->inject('queueForDeletes')
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_DELETE);
$valid = $validator->isValid($bucket->getDelete());
$valid = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_DELETE, $bucket->getDelete()));
if (!$fileSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
// Read permission should not be required for delete
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = $dbForProject->getAuthorization()->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
if ($fileSecurity && !$valid && !$validator->isValid($file->getDelete())) {
throw new Exception(Exception::USER_UNAUTHORIZED);
$validFile = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_DELETE, $file->getDelete()));
if ($fileSecurity && !$valid && !$validFile) {
throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription());
}
$deviceDeleted = false;
@@ -1815,7 +1806,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
if ($fileSecurity && !$valid) {
$deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
$deleted = Authorization::skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getSequence(), $fileId));
$deleted = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getSequence(), $fileId));
}
} catch (NotFoundException) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
@@ -1872,7 +1863,7 @@ App::get('/v1/storage/usage')
];
$total = [];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
$dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
@@ -1968,7 +1959,7 @@ App::get('/v1/storage/:bucketId/usage')
str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED),
];
Authorization::skip(function () use ($dbForProject, $dbForLogs, $bucket, $days, $metrics, &$stats) {
$dbForProject->getAuthorization()->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
+21 -21
View File
@@ -85,13 +85,13 @@ App::post('/v1/teams')
->inject('queueForEvents')
->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAppUser = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
$isAppUser = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$teamId = $teamId == 'unique()' ? ID::unique() : $teamId;
try {
$team = Authorization::skip(fn () => $dbForProject->createDocument('teams', new Document([
$team = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('teams', new Document([
'$id' => $teamId,
'$permissions' => [
Permission::read(Role::team($teamId)),
@@ -494,8 +494,8 @@ App::post('/v1/teams/:teamId/memberships')
->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(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAppUser = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
$url = htmlentities($url);
if (empty($url)) {
@@ -566,7 +566,7 @@ App::post('/v1/teams/:teamId/memberships')
try {
$userId = ID::unique();
$invitee = Authorization::skip(fn () => $dbForProject->createDocument('users', new Document([
$invitee = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('users', new Document([
'$id' => $userId,
'$permissions' => [
Permission::read(Role::any()),
@@ -602,7 +602,7 @@ App::post('/v1/teams/:teamId/memberships')
}
}
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
$isOwner = $dbForProject->getAuthorization()->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');
@@ -638,11 +638,11 @@ App::post('/v1/teams/:teamId/memberships')
]);
$membership = ($isPrivilegedUser || $isAppUser) ?
Authorization::skip(fn () => $dbForProject->createDocument('memberships', $membership)) :
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('memberships', $membership)) :
$dbForProject->createDocument('memberships', $membership);
if ($isPrivilegedUser || $isAppUser) {
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
}
} elseif ($membership->getAttribute('confirm') === false) {
@@ -655,7 +655,7 @@ App::post('/v1/teams/:teamId/memberships')
}
$membership = ($isPrivilegedUser || $isAppUser) ?
Authorization::skip(fn () => $dbForProject->updateDocument('memberships', $membership->getId(), $membership)) :
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('memberships', $membership->getId(), $membership)) :
$dbForProject->updateDocument('memberships', $membership->getId(), $membership);
} else {
throw new Exception(Exception::MEMBERSHIP_ALREADY_CONFIRMED);
@@ -911,7 +911,7 @@ App::get('/v1/teams/:teamId/memberships')
'mfa' => $project->getAttribute('auths', [])['membershipsMfa'] ?? true,
];
$roles = Authorization::getRoles();
$roles = $dbForProject->getAuthorization()->getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
@@ -1002,7 +1002,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
'mfa' => $project->getAttribute('auths', [])['membershipsMfa'] ?? true,
];
$roles = Authorization::getRoles();
$roles = $dbForProject->getAuthorization()->getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
@@ -1099,9 +1099,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
throw new Exception(Exception::USER_NOT_FOUND);
}
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAppUser = Auth::isAppUser(Authorization::getRoles());
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
$isAppUser = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isOwner = $dbForProject->getAuthorization()->hasRole('team:' . $team->getId() . '/owner');
if ($project->getId() === 'console') {
// Quick check: fetch up to 2 owners to determine if only one exists
@@ -1194,7 +1194,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
throw new Exception(Exception::MEMBERSHIP_NOT_FOUND);
}
$team = Authorization::skip(fn () => $dbForProject->getDocument('teams', $teamId));
$team = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('teams', $teamId));
if ($team->isEmpty()) {
throw new Exception(Exception::TEAM_NOT_FOUND);
@@ -1230,11 +1230,11 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->setAttribute('confirm', true)
;
Authorization::skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true)));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true)));
// Create session for the user if not logged in
if (!$hasSession) {
Authorization::setRole(Role::user($user->getId())->toString());
$dbForProject->getAuthorization()->addRole(Role::user($user->getId())->toString());
$detector = new Detector($request->getUserAgent('UNKNOWN'));
$record = $geodb->get($request->getIP());
@@ -1262,7 +1262,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
$session = $dbForProject->createDocument('sessions', $session);
Authorization::setRole(Role::user($userId)->toString());
$dbForProject->getAuthorization()->addRole(Role::user($userId)->toString());
if (!Config::getParam('domainVerification')) {
$response->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]));
@@ -1295,7 +1295,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
$dbForProject->purgeCachedDocument('users', $user->getId());
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
$queueForEvents
->setParam('userId', $user->getId())
@@ -1398,7 +1398,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
$dbForProject->purgeCachedDocument('users', $profile->getId());
if ($membership->getAttribute('confirm')) { // Count only confirmed members
Authorization::skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0));
}
$queueForEvents
+1 -1
View File
@@ -2629,7 +2629,7 @@ App::get('/v1/users/usage')
METRIC_SESSIONS,
];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
$dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $count => $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
+19 -19
View File
@@ -73,12 +73,12 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
}
$projectId = $repository->getAttribute('projectId');
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
$project = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
$dbForProject = $getProjectDB($project);
$resourceCollection = $resourceType === "function" ? 'functions' : 'sites';
$resourceId = $repository->getAttribute('resourceId');
$resource = Authorization::skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId));
$resource = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId));
$resourceInternalId = $resource->getSequence();
$deploymentId = ID::unique();
@@ -127,7 +127,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$latestCommentId = '';
if (!empty($providerPullRequestId) && $resource->getAttribute('providerSilentMode', false) === false) {
$latestComment = Authorization::skip(fn () => $dbForPlatform->findOne('vcsComments', [
$latestComment = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->findOne('vcsComments', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::equal('providerPullRequestId', [$providerPullRequestId]),
Query::orderDesc('$createdAt'),
@@ -177,7 +177,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
if (!empty($latestCommentId)) {
$teamId = $project->getAttribute('teamId', '');
$latestComment = Authorization::skip(fn () => $dbForPlatform->createDocument('vcsComments', new Document([
$latestComment = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->createDocument('vcsComments', new Document([
'$id' => ID::unique(),
'$permissions' => [
Permission::read(Role::team(ID::custom($teamId))),
@@ -198,7 +198,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
}
}
} elseif (!empty($providerBranch)) {
$latestComments = Authorization::skip(fn () => $dbForPlatform->find('vcsComments', [
$latestComments = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->find('vcsComments', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::equal('providerBranch', [$providerBranch]),
Query::orderDesc('$createdAt'),
@@ -280,7 +280,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$commands[] = $resource->getAttribute('commands', '');
}
$deployment = Authorization::skip(fn () => $dbForProject->createDocument('deployments', new Document([
$deployment = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('deployments', new Document([
'$id' => $deploymentId,
'$permissions' => [
Permission::read(Role::any()),
@@ -320,7 +320,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
Authorization::skip(fn () => $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource));
if ($resource->getCollection() === 'sites') {
$projectId = $project->getId();
@@ -330,7 +330,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$domain = ID::unique() . "." . $sitesDomain;
$ruleId = md5($domain);
$previewRuleId = $ruleId;
Authorization::skip(
$dbForProject->getAuthorization()->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -363,7 +363,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$domain = "branch-{$branchPrefix}-{$resourceProjectHash}.{$sitesDomain}";
$ruleId = md5($domain);
try {
Authorization::skip(
$dbForProject->getAuthorization()->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -394,7 +394,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$domain = "commit-" . substr($providerCommitHash, 0, 16) . ".{$sitesDomain}";
$ruleId = md5($domain);
try {
Authorization::skip(
$dbForProject->getAuthorization()->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -446,7 +446,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
if ($lockAcquired) {
// Wrap in try/finally to ensure lock file gets deleted
try {
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', $previewRuleId));
$rule = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('rules', $previewRuleId));
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$previewUrl = !empty($rule) ? ("{$protocol}://" . $rule->getAttribute('domain', '')) : '';
@@ -1345,7 +1345,7 @@ App::post('/v1/vcs/github/events')
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
//find resourceId from relevant resources table
$repositories = Authorization::skip(fn () => $dbForPlatform->find('repositories', [
$repositories = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->find('repositories', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::limit(100),
]));
@@ -1365,13 +1365,13 @@ App::post('/v1/vcs/github/events')
]);
foreach ($installations as $installation) {
$repositories = Authorization::skip(fn () => $dbForPlatform->find('repositories', [
$repositories = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->find('repositories', [
Query::equal('installationInternalId', [$installation->getSequence()]),
Query::limit(1000)
]));
foreach ($repositories as $repository) {
Authorization::skip(fn () => $dbForPlatform->deleteDocument('repositories', $repository->getId()));
$dbForProject->getAuthorization()->skip(fn () => $dbForPlatform->deleteDocument('repositories', $repository->getId()));
}
$dbForPlatform->deleteDocument('installations', $installation->getId());
@@ -1403,7 +1403,7 @@ App::post('/v1/vcs/github/events')
$providerCommitAuthor = $commitDetails["commitAuthor"] ?? '';
$providerCommitMessage = $commitDetails["commitMessage"] ?? '';
$repositories = Authorization::skip(fn () => $dbForPlatform->find('repositories', [
$repositories = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->find('repositories', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::orderDesc('$createdAt')
]));
@@ -1417,7 +1417,7 @@ App::post('/v1/vcs/github/events')
$external = $parsedPayload["external"] ?? true;
if ($external) {
$repositories = Authorization::skip(fn () => $dbForPlatform->find('repositories', [
$repositories = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->find('repositories', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::orderDesc('$createdAt')
]));
@@ -1428,7 +1428,7 @@ App::post('/v1/vcs/github/events')
if (\in_array($providerPullRequestId, $providerPullRequestIds)) {
$providerPullRequestIds = \array_diff($providerPullRequestIds, [$providerPullRequestId]);
$repository = $repository->setAttribute('providerPullRequestIds', $providerPullRequestIds);
$repository = Authorization::skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository));
$repository = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository));
}
}
}
@@ -1624,7 +1624,7 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
}
$repository = Authorization::skip(fn () => $dbForPlatform->getDocument('repositories', $repositoryId, [
$repository = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('repositories', $repositoryId, [
Query::equal('projectInternalId', [$project->getSequence()])
]));
@@ -1641,7 +1641,7 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor
// TODO: Delete from array when PR is closed
$repository = Authorization::skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository));
$repository = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository));
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
+19 -18
View File
@@ -65,9 +65,9 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
// TODO: @christyjacob remove once we migrate the rules in 1.7.x
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($host)));
$rule = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('rules', md5($host)));
} else {
$rule = Authorization::skip(
$rule = $dbForPlatform->getAuthorization()->skip(
fn () => $dbForPlatform->find('rules', [
Query::equal('domain', [$host]),
Query::limit(1)
@@ -108,7 +108,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
}
$projectId = $rule->getAttribute('projectId');
$project = Authorization::skip(
$project = $dbForPlatform->getAuthorization()->skip(
fn () => $dbForPlatform->getDocument('projects', $projectId)
);
@@ -116,7 +116,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
$accessedAt = $project->getAttribute('accessedAt', 0);
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
$project->setAttribute('accessedAt', DateTime::now());
Authorization::skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
}
/**
@@ -155,7 +155,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
/** @var Document $deployment */
if (!empty($rule->getAttribute('deploymentId', ''))) {
$deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $rule->getAttribute('deploymentId')));
$deployment = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('deployments', $rule->getAttribute('deploymentId')));
} else {
// 1.6.x DB schema compatibility
// TODO: Make sure deploymentId is never empty, and remove this code
@@ -169,15 +169,15 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
// Document of site or function
$resource = $resourceType === 'function' ?
Authorization::skip(fn () => $dbForProject->getDocument('functions', $resourceId)) :
Authorization::skip(fn () => $dbForProject->getDocument('sites', $resourceId));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('functions', $resourceId)) :
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('sites', $resourceId));
// ID of active deployments
// Attempts to use attribute from both schemas (1.6 and 1.7)
$activeDeploymentId = $resource->getAttribute('deploymentId', $resource->getAttribute('deployment', ''));
// Get deployment document, as intended originally
$deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $activeDeploymentId));
$deployment = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('deployments', $activeDeploymentId));
}
if ($deployment->getAttribute('resourceType', '') === 'functions') {
@@ -196,8 +196,8 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
}
$resource = $type === 'function' ?
Authorization::skip(fn () => $dbForProject->getDocument('functions', $deployment->getAttribute('resourceId', ''))) :
Authorization::skip(fn () => $dbForProject->getDocument('sites', $deployment->getAttribute('resourceId', '')));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('functions', $deployment->getAttribute('resourceId', ''))) :
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('sites', $deployment->getAttribute('resourceId', '')));
$isPreview = $type === 'function' ? false : ($rule->getAttribute('trigger', '') !== 'manual');
@@ -239,7 +239,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
$userExists = false;
$userId = $payload['userId'] ?? '';
if (!empty($userId)) {
$user = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
$user = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('users', $userId));
if (!$user->isEmpty() && $user->getAttribute('status', false)) {
$userExists = true;
}
@@ -252,7 +252,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
}
$membershipExists = false;
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
$project = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
if (!$project->isEmpty() && isset($user)) {
$teamId = $project->getAttribute('teamId', '');
$membership = $user->find('teamId', $teamId, 'memberships');
@@ -919,7 +919,7 @@ App::init()
} elseif (str_starts_with($request->getURI(), '/.well-known/acme-challenge')) {
Console::warning('Skipping SSL certificates generation on ACME challenge.');
} else {
Authorization::disable();
$dbForPlatform->getAuthorization()->disable();
$envDomain = System::getEnv('_APP_DOMAIN', '');
$mainDomain = null;
@@ -989,7 +989,7 @@ App::init()
}
$domains[$domain->get()] = true;
Authorization::reset(); // ensure authorization is re-enabled
$dbForPlatform->getAuthorization()->reset(); // ensure authorization is re-enabled
}
Config::setParam('domains', $domains);
}
@@ -1174,7 +1174,8 @@ App::error()
->inject('log')
->inject('queueForStatsUsage')
->inject('devKey')
->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, StatsUsage $queueForStatsUsage) {
->inject('authorization')
->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, StatsUsage $queueForStatsUsage, Authorization $authorization) {
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
$route = $utopia->getRoute();
$class = \get_class($error);
@@ -1256,7 +1257,7 @@ App::error()
* If not a publishable error, track usage stats. Publishable errors are >= 500 or those explicitly marked as publish=true in errors.php
*/
if (!$publish && $project->getId() !== 'console') {
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
if (!Auth::isPrivilegedUser($authorization->getRoles())) {
$fileSize = 0;
$file = $request->getFiles('file');
if (!empty($file)) {
@@ -1318,7 +1319,7 @@ App::error()
$log->addExtra('file', $error->getFile());
$log->addExtra('line', $error->getLine());
$log->addExtra('trace', $error->getTraceAsString());
$log->addExtra('roles', Authorization::getRoles());
$log->addExtra('roles', $authorization->getRoles());
$action = 'UNKNOWN_NAMESPACE.UNKNOWN.METHOD';
if (!empty($sdk)) {
@@ -1580,7 +1581,7 @@ App::get('/v1/ping')
->setAttribute('pingCount', $pingCount)
->setAttribute('pingedAt', $pingedAt);
Authorization::skip(function () use ($dbForPlatform, $project) {
$dbForPlatform->getAuthorization()->skip(function () use ($dbForPlatform, $project) {
$dbForPlatform->updateDocument('projects', $project->getId(), $project);
});
+22 -23
View File
@@ -28,7 +28,7 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Authorization\Input;
use Utopia\Queue\Publisher;
use Utopia\System\System;
use Utopia\Telemetry\Adapter as Telemetry;
@@ -249,7 +249,7 @@ App::init()
if ($apiKey->getRole() === Auth::USER_ROLE_APPS) {
// Disable authorization checks for API keys
Authorization::setDefaultStatus(false);
$dbForPlatform->getAuthorization()->setDefaultStatus(false);
$user = new Document([
'$id' => '',
@@ -322,14 +322,14 @@ App::init()
$scopes = \array_merge($scopes, $roles[$role]['scopes']);
}
Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
$dbForPlatform->getAuthorization()->setDefaultStatus(false); // Cancel security segmentation for admin users.
}
$scopes = \array_unique($scopes);
Authorization::setRole($role);
foreach (Auth::getRoles($user) as $authRole) {
Authorization::setRole($authRole);
$dbForPlatform->getAuthorization()->addRole($role);
foreach (Auth::getRoles($user, $dbForPlatform->getAuthorization()) as $authRole) {
$dbForPlatform->getAuthorization()->addRole($authRole);
}
// Update project last activity
@@ -337,7 +337,7 @@ App::init()
$accessedAt = $project->getAttribute('accessedAt', 0);
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
$project->setAttribute('accessedAt', DateTime::now());
Authorization::skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
}
}
@@ -372,7 +372,7 @@ App::init()
if (
array_key_exists($namespace, $project->getAttribute('services', []))
&& !$project->getAttribute('services', [])[$namespace]
&& !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles()))
&& !(Auth::isPrivilegedUser($dbForPlatform->getAuthorization()->getRoles()) || Auth::isAppUser($dbForPlatform->getAuthorization()->getRoles()))
) {
throw new Exception(Exception::GENERAL_SERVICE_DISABLED);
}
@@ -439,7 +439,7 @@ App::init()
if (
array_key_exists('rest', $project->getAttribute('apis', []))
&& !$project->getAttribute('apis', [])['rest']
&& !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles()))
&& !(Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles()) || Auth::isAppUser($dbForProject->getAuthorization() ->getRoles()))
) {
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
}
@@ -469,7 +469,7 @@ App::init()
$closestLimit = null;
$roles = Authorization::getRoles();
$roles = $dbForProject->getAuthorization()->getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
@@ -569,10 +569,10 @@ App::init()
if ($useCache) {
$route = $utopia->match($request);
$isImageTransformation = $route->getPath() === '/v1/storage/buckets/:bucketId/files/:fileId/preview';
$isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && !Auth::isPrivilegedUser(Authorization::getRoles());
$isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && !Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
$key = $request->cacheIdentifier();
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$cacheLog = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('cache', $key));
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
);
@@ -585,18 +585,17 @@ App::init()
if ($type === 'bucket' && (!$isImageTransformation || !$isDisabled)) {
$bucketId = $parts[1] ?? null;
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAppUser && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
$valid = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
if (!$fileSecurity && !$valid && !$isToken) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
@@ -607,7 +606,7 @@ App::init()
if ($fileSecurity && !$valid && !$isToken) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) {
@@ -618,11 +617,11 @@ App::init()
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
//Do not update transformedAt if it's a console user
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
if (!Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles())) {
$transformedAt = $file->getAttribute('transformedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) {
$file->setAttribute('transformedAt', DateTime::now());
Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file));
}
}
}
@@ -844,11 +843,11 @@ App::shutdown()
$key = $request->cacheIdentifier();
$signature = md5($data['payload']);
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$cacheLog = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('cache', $key));
$accessedAt = $cacheLog->getAttribute('accessedAt', 0);
$now = DateTime::now();
if ($cacheLog->isEmpty()) {
Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('cache', new Document([
'$id' => $key,
'resource' => $resource,
'resourceType' => $resourceType,
@@ -858,7 +857,7 @@ App::shutdown()
])));
} elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) {
$cacheLog->setAttribute('accessedAt', $now);
Authorization::skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog));
// Overwrite the file every APP_CACHE_UPDATE seconds to update the file modified time that is used in the TTL checks in cache->load()
$cache->save($key, $data['payload']);
}
@@ -870,7 +869,7 @@ App::shutdown()
}
if ($project->getId() !== 'console') {
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
if (!Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles())) {
$fileSize = 0;
$file = $request->getFiles('file');
if (!empty($file)) {
+4 -3
View File
@@ -36,7 +36,8 @@ App::init()
->inject('request')
->inject('project')
->inject('geodb')
->action(function (App $utopia, Request $request, Document $project, Reader $geodb) {
->inject('authorization')
->action(function (App $utopia, Request $request, Document $project, Reader $geodb, Authorization $authorization) {
$denylist = System::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', '');
if (!empty($denylist && $project->getId() === 'console')) {
$countries = explode(',', $denylist);
@@ -49,8 +50,8 @@ App::init()
$route = $utopia->match($request);
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAppUser = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
$isAppUser = Auth::isAppUser($authorization->getRoles());
if ($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs
return;
+13 -8
View File
@@ -318,9 +318,9 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
$dbForPlatform->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes);
}
if (Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')->isEmpty())) {
if ($dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')->isEmpty())) {
Console::info(" └── Creating screenshots bucket...");
Authorization::skip(fn () => $dbForPlatform->createDocument('buckets', new Document([
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->createDocument('buckets', new Document([
'$id' => ID::custom('screenshots'),
'$collection' => ID::custom('buckets'),
'name' => 'Screenshots',
@@ -335,7 +335,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
'search' => 'buckets Screenshots',
])));
$bucket = Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots'));
$bucket = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots'));
Console::info(" └── Creating files collection for screenshots bucket...");
$files = $collections['buckets']['files'] ?? [];
@@ -363,7 +363,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
'orders' => $index['orders'],
]), $files['indexes']);
Authorization::skip(fn () => $dbForPlatform->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes));
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes));
}
});
@@ -454,8 +454,13 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
App::setResource('pools', fn () => $pools);
try {
Authorization::cleanRoles();
Authorization::setRole(Role::any()->toString());
$authorization = $app->getResource('authorization');
$request->setAuthorization($authorization);
$response->setAuthorization($authorization);
$authorization->cleanRoles();
$authorization->addRole(Role::any()->toString());
$app->run($request, $response);
} catch (\Throwable $th) {
@@ -497,7 +502,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
$log->addExtra('file', $th->getFile());
$log->addExtra('line', $th->getLine());
$log->addExtra('trace', $th->getTraceAsString());
$log->addExtra('roles', Authorization::getRoles());
$log->addExtra('roles', $authorization->getRoles());
$sdk = $route->getLabel("sdk", false);
@@ -573,7 +578,7 @@ $http->on(Constant::EVENT_TASK, function () use ($register, $domains) {
}
$results = [];
try {
$results = Authorization::skip(fn () => $dbForPlatform->find('rules', $queries));
$results = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->find('rules', $queries));
} catch (Throwable $th) {
Console::error($th->getMessage());
}
+7 -7
View File
@@ -176,7 +176,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database->find('sessions', [
return $database->getAuthorization()->skip(fn () => $database->find('sessions', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
@@ -189,7 +189,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
return $database->getAuthorization()->skip(fn () => $database
->find('tokens', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
@@ -203,7 +203,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
return $database->getAuthorization()->skip(fn () => $database
->find('challenges', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
@@ -217,7 +217,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
return $database->getAuthorization()->skip(fn () => $database
->find('authenticators', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
@@ -231,7 +231,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
return $database->getAuthorization()->skip(fn () => $database
->find('memberships', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
@@ -331,7 +331,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
return $database->getAuthorization()->skip(fn () => $database
->find('targets', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY)
@@ -345,7 +345,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
$targetIds = Authorization::skip(fn () => \array_map(
$targetIds = $database->getAuthorization()->skip(fn () => \array_map(
fn ($document) => $document->getAttribute('targetInternalId'),
$database->find('subscribers', [
Query::equal('topicInternalId', [$document->getSequence()]),
+32 -23
View File
@@ -200,9 +200,9 @@ App::setResource('platforms', function (Request $request, Document $console, Doc
// Safe if rule with same project ID exists
if (!empty($origin)) {
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin ?? '')));
$rule = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('rules', md5($origin ?? '')));
} else {
$rule = Authorization::skip(
$rule = $dbForPlatform->getAuthorization()->skip(
fn () => $dbForPlatform->find('rules', [
Query::equal('domain', [$origin]),
Query::limit(1)
@@ -232,9 +232,10 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Database $dbForPlatform */
/** @var Utopia\Database\Authorization $authorization */
/** @var string $mode */
Authorization::setDefaultStatus(true);
$dbForPlatform->getAuthorization()->setDefaultStatus(true);
Auth::setCookieName('a_session_' . $project->getId());
@@ -349,7 +350,7 @@ App::setResource('project', function ($dbForPlatform, $request, $console) {
return $console;
}
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
$project = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
return $project;
}, ['dbForPlatform', 'request', 'console']);
@@ -379,7 +380,11 @@ App::setResource('console', function () {
return new Document(Config::getParam('console'));
}, []);
App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project) {
App::setResource('authorization', function () {
return new Authorization();
}, []);
App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project, Authorization $authorization) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
@@ -395,6 +400,7 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform
$database = new Database($adapter, $cache);
$database
->setAuthorization($authorization)
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId())
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
@@ -415,13 +421,14 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform
}
return $database;
}, ['pools', 'dbForPlatform', 'cache', 'project']);
}, ['pools', 'dbForPlatform', 'cache', 'project', 'authorization']);
App::setResource('dbForPlatform', function (Group $pools, Cache $cache) {
App::setResource('dbForPlatform', function (Group $pools, Cache $cache, Authorization $authorization) {
$adapter = new DatabasePool($pools->get('console'));
$database = new Database($adapter, $cache);
$database
->setAuthorization($authorization)
->setNamespace('_console')
->setMetadata('host', \gethostname())
->setMetadata('project', 'console')
@@ -429,12 +436,12 @@ App::setResource('dbForPlatform', function (Group $pools, Cache $cache) {
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
return $database;
}, ['pools', 'cache']);
}, ['pools', 'cache', 'authorization']);
App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache, Authorization $authorization) {
$databases = [];
return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases) {
return function (Document $project) use ($pools, $dbForPlatform, $cache, $authorization, &$databases) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
@@ -446,8 +453,9 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
}
$configure = (function (Database $database) use ($project, $dsn) {
$configure = (function (Database $database) use ($project, $dsn, $authorization) {
$database
->setAuthorization($authorization)
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId())
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
@@ -481,12 +489,12 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
return $database;
};
}, ['pools', 'dbForPlatform', 'cache']);
}, ['pools', 'dbForPlatform', 'cache', 'authorization']);
App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
App::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
$database = null;
return function (?Document $project = null) use ($pools, $cache, &$database) {
return function (?Document $project = null) use ($pools, $cache, $authorization, &$database) {
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant((int) $project->getSequence());
return $database;
@@ -496,6 +504,7 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database = new Database($adapter, $cache);
$database
->setAuthorization($authorization)
->setSharedTables(true)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
@@ -508,7 +517,7 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
return $database;
};
}, ['pools', 'cache']);
}, ['pools', 'cache', 'authorization']);
App::setResource('telemetry', fn () => new NoTelemetry());
@@ -713,7 +722,7 @@ App::setResource('schema', function ($utopia, $dbForProject) {
};
$attributes = function (int $limit, int $offset) use ($dbForProject) {
$attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [
$attrs = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->find('attributes', [
Query::limit($limit),
Query::offset($offset),
]));
@@ -852,7 +861,7 @@ App::setResource('devKey', function (Request $request, Document $project, array
$accessedAt = $key->getAttribute('accessedAt', 0);
if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
$key->setAttribute('accessedAt', DatabaseDateTime::now());
Authorization::skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key));
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key));
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
@@ -869,7 +878,7 @@ App::setResource('devKey', function (Request $request, Document $project, array
/** Update access time as well */
$key->setAttribute('accessedAt', DatabaseDateTime::now());
$key = Authorization::skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key));
$key = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key));
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
}
@@ -886,7 +895,7 @@ App::setResource('team', function (Document $project, Database $dbForPlatform, A
if (str_starts_with($path, '/v1/projects/:projectId')) {
$uri = $request->getURI();
$pid = explode('/', $uri)[3];
$p = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $pid));
$p = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('projects', $pid));
$teamInternalId = $p->getAttribute('teamInternalId', '');
} elseif ($path === '/v1/projects') {
$teamId = $request->getParam('teamId', '');
@@ -895,7 +904,7 @@ App::setResource('team', function (Document $project, Database $dbForPlatform, A
return new Document([]);
}
$team = Authorization::skip(fn () => $dbForPlatform->getDocument('teams', $teamId));
$team = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('teams', $teamId));
return $team;
}
}
@@ -904,7 +913,7 @@ App::setResource('team', function (Document $project, Database $dbForPlatform, A
return new Document([]);
}
$team = Authorization::skip(function () use ($dbForPlatform, $teamInternalId) {
$team = $dbForPlatform->getAuthorization()->skip(function () use ($dbForPlatform, $teamInternalId) {
return $dbForPlatform->findOne('teams', [
Query::equal('$sequence', [$teamInternalId]),
]);
@@ -966,7 +975,7 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) {
return new Document([]);
}
$token = Authorization::skip(fn () => $dbForProject->getDocument('resourceTokens', $tokenId));
$token = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('resourceTokens', $tokenId));
if ($token->isEmpty()) {
return new Document([]);
@@ -995,7 +1004,7 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) {
$accessedAt = $token->getAttribute('accessedAt', 0);
if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_RESOURCE_TOKEN_ACCESS)) > $accessedAt) {
$token->setAttribute('accessedAt', DatabaseDateTime::now());
Authorization::skip(fn () => $dbForProject->updateDocument('resourceTokens', $token->getId(), $token));
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('resourceTokens', $token->getId(), $token));
}
return new Document([
+11 -11
View File
@@ -28,7 +28,6 @@ use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\DSN\DSN;
use Utopia\Logger\Log;
use Utopia\Pools\Group;
@@ -299,7 +298,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
'value' => '{}'
]);
$statsDocument = Authorization::skip(fn () => $database->createDocument('realtime', $document));
$statsDocument = $database->getAuthorization()->skip(fn () => $database->createDocument('realtime', $document));
break;
} catch (Throwable) {
Console::warning("Collection not ready. Retrying connection ({$attempts})...");
@@ -329,7 +328,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
->setAttribute('timestamp', DateTime::now())
->setAttribute('value', json_encode($payload));
Authorization::skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument));
$database->getAuthorization()->skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument));
} catch (Throwable $th) {
$logError($th, "updateWorkerDocument");
}
@@ -360,7 +359,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
$payload = [];
$list = Authorization::skip(fn () => $database->find('realtime', [
$list = $database->getAuthorization()->skip(fn () => $database->find('realtime', [
Query::greaterThan('timestamp', DateTime::addSeconds(new \DateTime(), -15)),
]));
@@ -454,12 +453,12 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
if ($realtime->hasSubscriber($projectId, 'user:' . $userId)) {
$connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId]));
$consoleDatabase = getConsoleDB();
$project = Authorization::skip(fn () => $consoleDatabase->getDocument('projects', $projectId));
$project = $consoleDatabase->getAuthorization()->skip(fn () => $consoleDatabase->getDocument('projects', $projectId));
$database = getProjectDB($project);
$user = $database->getDocument('users', $userId);
$roles = Auth::getRoles($user);
$roles = $consoleDatabase->getAuthorization()->getRoles($user);
$channels = $realtime->connections[$connection]['channels'];
$realtime->unsubscribe($connection);
@@ -515,6 +514,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
try {
/** @var Document $project */
$project = $app->getResource('project');
$authorization = $app->getResource('authorization');
/*
* Project Check
@@ -526,7 +526,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
if (
array_key_exists('realtime', $project->getAttribute('apis', []))
&& !$project->getAttribute('apis', [])['realtime']
&& !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles()))
&& !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles()))
) {
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
}
@@ -563,7 +563,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $originValidator->getDescription());
}
$roles = Auth::getRoles($user);
$roles = Auth::getRoles($user, $authorization);
$channels = Realtime::convertChannels($request->getQuery('channels', []), $user->getId());
@@ -637,8 +637,8 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
$database = getConsoleDB();
if ($projectId !== 'console') {
$project = Authorization::skip(fn () => $database->getDocument('projects', $projectId));
$database = getProjectDB($project);
$project = $database->getAuthorization()->skip(fn () => $database->getDocument('projects', $projectId));
$database = getProjectDB($project, $database->getAuthorization());
} else {
$project = null;
}
@@ -692,7 +692,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Session is not valid.');
}
$roles = Auth::getRoles($user);
$roles = Auth::getRoles($user, $database->getAuthorization());
$channels = Realtime::convertChannels(array_flip($realtime->connections[$connection]['channels']), $user->getId());
$realtime->subscribe($realtime->connections[$connection]['projectId'], $connection, $roles, $channels);
+26 -13
View File
@@ -45,19 +45,24 @@ use Utopia\System\System;
use Utopia\Telemetry\Adapter as Telemetry;
use Utopia\Telemetry\Adapter\None as NoTelemetry;
Authorization::disable();
Runtime::enableCoroutine();
Server::setResource('register', fn () => $register);
Server::setResource('dbForPlatform', function (Cache $cache, Registry $register) {
Server::setResource('authorization', function () {
$authorization = new Authorization();
return $authorization;
}, []);
Server::setResource('dbForPlatform', function (Cache $cache, Registry $register, Authorization $authorization) {
$pools = $register->get('pools');
$adapter = new DatabasePool($pools->get('console'));
$dbForPlatform = new Database($adapter, $cache);
$dbForPlatform->setNamespace('_console');
$dbForPlatform->setAuthorization($authorization);
return $dbForPlatform;
}, ['cache', 'register']);
}, ['cache', 'register', 'authorization']);
Server::setResource('project', function (Message $message, Database $dbForPlatform) {
$payload = $message->getPayload() ?? [];
@@ -70,7 +75,7 @@ Server::setResource('project', function (Message $message, Database $dbForPlatfo
return $dbForPlatform->getDocument('projects', $project->getId());
}, ['message', 'dbForPlatform']);
Server::setResource('dbForProject', function (Cache $cache, Registry $register, Message $message, Document $project, Database $dbForPlatform) {
Server::setResource('dbForProject', function (Cache $cache, Registry $register, Message $message, Document $project, Database $dbForPlatform, Authorization $authorization) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
@@ -91,11 +96,13 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register,
if (\in_array($dsn->getHost(), $sharedTables)) {
$database
->setAuthorization($authorization)
->setSharedTables(true)
->setTenant((int)$project->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
->setAuthorization($authorization)
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getSequence());
@@ -104,12 +111,12 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register,
$database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
return $database;
}, ['cache', 'register', 'message', 'project', 'dbForPlatform']);
}, ['cache', 'register', 'message', 'project', 'dbForPlatform', 'authorization']);
Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache, Authorization $authorization) {
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases): Database {
return function (Document $project) use ($pools, $dbForPlatform, $cache, $authorization, &$databases): Database {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
@@ -128,11 +135,13 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf
if (\in_array($dsn->getHost(), $sharedTables)) {
$database
->setAuthorization($authorization)
->setSharedTables(true)
->setTenant((int)$project->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
->setAuthorization($authorization)
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getSequence());
@@ -150,11 +159,13 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf
if (\in_array($dsn->getHost(), $sharedTables)) {
$database
->setAuthorization($authorization)
->setSharedTables(true)
->setTenant((int)$project->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
->setAuthorization($authorization)
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getSequence());
@@ -164,11 +175,11 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf
return $database;
};
}, ['pools', 'dbForPlatform', 'cache']);
}, ['pools', 'dbForPlatform', 'cache', 'authorization']);
Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
Server::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
$database = null;
return function (?Document $project = null) use ($pools, $cache, $database) {
return function (?Document $project = null) use ($pools, $cache, $database, $authorization) {
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant((int)$project->getSequence());
return $database;
@@ -178,6 +189,7 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database = new Database($adapter, $cache);
$database
->setAuthorization($authorization)
->setSharedTables(true)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER)
@@ -190,7 +202,7 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
return $database;
};
}, ['pools', 'cache']);
}, ['pools', 'cache', 'authorization']);
Server::setResource('abuseRetention', function () {
return time() - (int) System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400); // 1 day
@@ -478,7 +490,8 @@ $worker
->inject('log')
->inject('pools')
->inject('project')
->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($worker, $queueName) {
->inject('authorization')
->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project, Authorization $authorization) use ($worker, $queueName) {
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
if ($logger) {
@@ -494,7 +507,7 @@ $worker
$log->addExtra('file', $error->getFile());
$log->addExtra('line', $error->getLine());
$log->addExtra('trace', $error->getTraceAsString());
$log->addExtra('roles', Authorization::getRoles());
$log->addExtra('roles', $authorization->getRoles());
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
+1 -1
View File
@@ -52,7 +52,7 @@
"utopia-php/cache": "0.13.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "3.*",
"utopia-php/database": "dev-feat-authorization-instance as 3.0.2",
"utopia-php/detector": "0.1.*",
"utopia-php/domains": "0.8.*",
"utopia-php/dns": "0.3.*",
Generated
+76 -20
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "407c1717bfef580d733ff2bbb232ec8a",
"content-hash": "f27ea68014d28d445baa355466cdb7a9",
"packages": [
{
"name": "adhocore/jwt",
@@ -3791,17 +3791,65 @@
"time": "2020-10-24T09:49:09+00:00"
},
{
"name": "utopia-php/database",
"version": "3.0.2",
"name": "utopia-php/console",
"version": "0.0.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "b92554e2e7b3b00f0f0acb2b53c6a11e1349b81e"
"url": "https://github.com/utopia-php/console.git",
"reference": "f77104e4a888fa9cb3e08f32955ec09479ab7a92"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/b92554e2e7b3b00f0f0acb2b53c6a11e1349b81e",
"reference": "b92554e2e7b3b00f0f0acb2b53c6a11e1349b81e",
"url": "https://api.github.com/repos/utopia-php/console/zipball/f77104e4a888fa9cb3e08f32955ec09479ab7a92",
"reference": "f77104e4a888fa9cb3e08f32955ec09479ab7a92",
"shasum": ""
},
"require": {
"php": ">=7.4"
},
"require-dev": {
"laravel/pint": "1.2.*",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.3",
"squizlabs/php_codesniffer": "^3.6",
"swoole/ide-helper": "4.8.8"
},
"type": "library",
"autoload": {
"psr-4": {
"Utopia\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Console helpers for logging, prompting, and executing commands",
"keywords": [
"cli",
"console",
"php",
"terminal",
"utopia"
],
"support": {
"issues": "https://github.com/utopia-php/console/issues",
"source": "https://github.com/utopia-php/console/tree/0.0.1"
},
"time": "2025-10-20T14:41:36+00:00"
},
{
"name": "utopia-php/database",
"version": "dev-feat-authorization-instance",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "4088c1002bbc837fd9299987cce5d460ded210d1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/4088c1002bbc837fd9299987cce5d460ded210d1",
"reference": "4088c1002bbc837fd9299987cce5d460ded210d1",
"shasum": ""
},
"require": {
@@ -3844,9 +3892,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/3.0.2"
"source": "https://github.com/utopia-php/database/tree/feat-authorization-instance"
},
"time": "2025-10-20T23:58:56+00:00"
"time": "2025-10-20T13:53:53+00:00"
},
{
"name": "utopia-php/detector",
@@ -4346,16 +4394,16 @@
},
{
"name": "utopia-php/migration",
"version": "1.2.2",
"version": "1.3.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "e0b6687620dd67fe2b96eb4419e8243577b11c8f"
"reference": "b6985b235ab64f07a6b88569e20cf9b2df7d838c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/e0b6687620dd67fe2b96eb4419e8243577b11c8f",
"reference": "e0b6687620dd67fe2b96eb4419e8243577b11c8f",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/b6985b235ab64f07a6b88569e20cf9b2df7d838c",
"reference": "b6985b235ab64f07a6b88569e20cf9b2df7d838c",
"shasum": ""
},
"require": {
@@ -4363,9 +4411,9 @@
"ext-curl": "*",
"ext-openssl": "*",
"php": ">=8.1",
"utopia-php/console": "0.0.*",
"utopia-php/database": "3.*",
"utopia-php/dsn": "0.2.*",
"utopia-php/framework": "0.33.*",
"utopia-php/storage": "0.18.*"
},
"require-dev": {
@@ -4373,7 +4421,6 @@
"laravel/pint": "1.*",
"phpstan/phpstan": "1.*",
"phpunit/phpunit": "11.*",
"utopia-php/cli": "0.16.*",
"vlucas/phpdotenv": "5.*"
},
"type": "library",
@@ -4396,9 +4443,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/1.2.2"
"source": "https://github.com/utopia-php/migration/tree/1.3.1"
},
"time": "2025-10-20T10:12:11+00:00"
"time": "2025-10-21T08:13:54+00:00"
},
{
"name": "utopia-php/mongo",
@@ -8736,9 +8783,18 @@
"time": "2024-03-07T20:33:40+00:00"
}
],
"aliases": [],
"aliases": [
{
"package": "utopia-php/database",
"version": "dev-feat-authorization-instance",
"alias": "3.0.2",
"alias_normalized": "3.0.2.0"
}
],
"minimum-stability": "stable",
"stability-flags": {},
"stability-flags": {
"utopia-php/database": 20
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
@@ -8762,5 +8818,5 @@
"platform-overrides": {
"php": "8.3"
},
"plugin-api-version": "2.6.0"
"plugin-api-version": "2.2.0"
}
+2 -2
View File
@@ -453,11 +453,11 @@ class Auth
* @param Document $user
* @return array<string>
*/
public static function getRoles(Document $user): array
public static function getRoles(Document $user, Authorization $authorization): array
{
$roles = [];
if (!self::isPrivilegedUser(Authorization::getRoles()) && !self::isAppUser(Authorization::getRoles())) {
if (!self::isPrivilegedUser($authorization->getRoles()) && !self::isAppUser($authorization->getRoles())) {
if ($user->getId()) {
$roles[] = Role::user($user->getId())->toString();
$roles[] = Role::users()->toString();
+2 -2
View File
@@ -342,12 +342,12 @@ class TransactionState
*/
private function getTransactionState(string $transactionId): array
{
$transaction = Authorization::skip(fn () => $this->dbForProject->getDocument('transactions', $transactionId));
$transaction = $this->dbForProject->getAuthorization()->skip(fn () => $this->dbForProject->getDocument('transactions', $transactionId));
if ($transaction->isEmpty() || $transaction->getAttribute('status') !== 'pending') {
return [];
}
$operations = Authorization::skip(fn () => $this->dbForProject->find('transactionLogs', [
$operations =$this->dbForProject->getAuthorization()->skip(fn () => $this->dbForProject->find('transactionLogs', [
Query::equal('transactionInternalId', [$transaction->getSequence()]),
Query::orderAsc(),
Query::limit(PHP_INT_MAX)
+3 -2
View File
@@ -99,8 +99,6 @@ abstract class Migration
public function __construct()
{
Authorization::disable();
Authorization::setDefaultStatus(false);
$this->collections = Config::getParam('collections', []);
@@ -135,6 +133,9 @@ abstract class Migration
$this->dbForPlatform = $dbForPlatform;
$this->getProjectDB = $getProjectDB;
$this->dbForPlatform->getAuthorization()->disable();
$this->dbForPlatform->getAuthorization()->setDefaultStatus(false);
return $this;
}
@@ -205,7 +205,7 @@ class Base 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();
Authorization::skip(
$dbForProject->getAuthorization()->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -231,7 +231,7 @@ class Base extends Action
$domain = "commit-" . substr($commitDetails['commitHash'], 0, 16) . ".{$sitesDomain}";
$ruleId = md5($domain);
try {
Authorization::skip(
$dbForProject->getAuthorization()->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -268,7 +268,7 @@ class Base extends Action
$domain = "branch-{$branchPrefix}-{$resourceProjectHash}.{$sitesDomain}";
$ruleId = md5($domain);
try {
Authorization::skip(
$dbForProject->getAuthorization()->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -124,7 +124,7 @@ class Get extends Action
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain may not start with http:// or https://.');
}
$document = Authorization::skip(fn () => $dbForPlatform->findOne('rules', [
$document = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->findOne('rules', [
Query::equal('domain', [$value]),
]));
@@ -310,7 +310,7 @@ abstract class Action extends UtopiaAction
throw new Exception($this->getSpatialTypeNotSupportedException());
}
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$db = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -371,7 +371,7 @@ abstract class Action extends UtopiaAction
\in_array($attribute->getAttribute('type'), Database::SPATIAL_TYPES) &&
$attribute->getAttribute('required')
) {
$hasData = !Authorization::skip(fn () => $dbForProject
$hasData = !$dbForProject->getAuthorization()->skip(fn () => $dbForProject
->findOne('database_' . $db->getSequence() . '_collection_' . $collection->getSequence()))
->isEmpty();
@@ -474,7 +474,7 @@ abstract class Action extends UtopiaAction
protected function updateAttribute(string $databaseId, string $collectionId, string $key, Database $dbForProject, Event $queueForEvents, string $type, int $size = null, string $filter = null, string|bool|int|float|array $default = null, bool $required = null, int|float|null $min = null, int|float|null $max = null, array $elements = null, array $options = [], string $newKey = null): Document
{
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$db = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -72,7 +72,7 @@ class Delete extends Action
public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$db = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
@@ -73,7 +73,7 @@ class Get extends Action
public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
@@ -90,7 +90,7 @@ class Create extends Action
$key ??= $relatedCollectionId;
$twoWayKey ??= $collectionId;
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
@@ -67,7 +67,7 @@ class XList extends Action
public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
@@ -82,7 +82,7 @@ class Create extends Action
public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -69,7 +69,7 @@ class Delete extends Action
public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
@@ -297,7 +297,7 @@ abstract class Action extends AppwriteAction
$relatedCollectionId = $relationship->getAttribute('relatedCollection');
if (!isset($collectionsCache[$relatedCollectionId])) {
$relatedCollectionDoc = Authorization::skip(
$relatedCollectionDoc = $dbForProject->getAuthorization()->skip(
fn () => $dbForProject->getDocument(
'database_' . $database->getSequence(),
$relatedCollectionId
@@ -89,15 +89,15 @@ class Decrement extends 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
{
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty()) {
throw new Exception($this->getParentNotFoundException());
}
@@ -105,7 +105,7 @@ class Decrement extends Action
// Handle transaction staging
if ($transactionId !== null) {
$transaction = ($isAPIKey || $isPrivilegedUser)
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? $dbForProject->getAuthorization()->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');
@@ -89,15 +89,15 @@ class Increment extends 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
{
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty()) {
throw new Exception($this->getParentNotFoundException());
}
@@ -105,7 +105,7 @@ class Increment extends Action
// Handle transaction staging
if ($transactionId !== null) {
$transaction = ($isAPIKey || $isPrivilegedUser)
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? $dbForProject->getAuthorization()->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,7 +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;
use Utopia\Swoole\Response as SwooleResponse;
@@ -177,19 +177,19 @@ class Create extends Action
$documents = [$data];
}
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
if ($isBulk && !$isAPIKey && !$isPrivilegedUser) {
throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE);
}
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception($this->getParentNotFoundException());
}
@@ -246,8 +246,8 @@ class Create extends Action
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
if (!Authorization::isRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')');
if (!$dbForProject->getAuthorization()->hasRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $dbForProject->getAuthorization()->getRoles()) . ')');
}
}
}
@@ -262,17 +262,17 @@ class Create extends Action
$operations++;
$documentSecurity = $collection->getAttribute('documentSecurity', false);
$validator = new Authorization($permission);
$valid = $validator->isValid($collection->getPermissionsByType($permission));
if (($permission === Database::PERMISSION_UPDATE && !$documentSecurity) || !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
$validCollection = $dbForProject->getAuthorization()->isValid(new Input($permission, $collection->getPermissionsByType($permission)));
if (($permission === Database::PERMISSION_UPDATE && !$documentSecurity) || !$validCollection) {
throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription());
}
if ($permission === Database::PERMISSION_UPDATE) {
$valid = $valid || $validator->isValid($document->getUpdate());
$validDocument= $dbForProject->getAuthorization()->isValid(new Input($permission, $document->getUpdate()));
$valid = $validCollection || $validDocument;
if ($documentSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription());
}
}
@@ -297,7 +297,7 @@ class Create extends Action
}
$relatedCollectionId = $relationship->getAttribute('relatedCollection');
$relatedCollection = Authorization::skip(
$relatedCollection = $dbForProject->getAuthorization()->skip(
fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId)
);
@@ -313,7 +313,7 @@ class Create extends Action
if ($relation instanceof Document) {
$relation = $this->removeReadonlyAttributes($relation, $isAPIKey || $isPrivilegedUser);
$current = Authorization::skip(
$current = $dbForProject->getAuthorization()->skip(
fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(), $relation->getId())
);
@@ -368,7 +368,7 @@ class Create extends Action
// Handle transaction staging
if ($transactionId !== null) {
$transaction = ($isAPIKey || $isPrivilegedUser)
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
: $dbForProject->getDocument('transactions', $transactionId);
if ($transaction->isEmpty()) {
throw new Exception(Exception::TRANSACTION_NOT_FOUND);
@@ -503,4 +503,4 @@ class Create extends Action
$this->getResponseModel()
);
}
}
}
@@ -98,16 +98,16 @@ class Delete extends Action
TransactionState $transactionState,
array $plan
): void {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception($this->getParentNotFoundException());
@@ -120,7 +120,7 @@ class Delete extends Action
// Use transaction-aware document retrieval to see changes from same transaction
$document = $transactionState->getDocument($collectionTableId, $documentId, $transactionId);
} else {
$document = Authorization::skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId));
$document = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId));
}
if ($document->isEmpty()) {
@@ -130,7 +130,7 @@ class Delete extends Action
// Handle transaction staging
if ($transactionId !== null) {
$transaction = ($isAPIKey || $isPrivilegedUser)
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
: $dbForProject->getDocument('transactions', $transactionId);
if ($transaction->isEmpty()) {
throw new Exception(Exception::TRANSACTION_NOT_FOUND);
@@ -74,15 +74,15 @@ class Get extends Action
public function action(string $databaseId, string $collectionId, string $documentId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionState $transactionState): void
{
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception($this->getParentNotFoundException());
@@ -78,7 +78,7 @@ class XList extends Action
public function action(string $databaseId, string $collectionId, string $documentId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
@@ -97,16 +97,16 @@ class Update extends Action
throw new Exception($this->getMissingPayloadException());
}
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception($this->getParentNotFoundException());
@@ -120,7 +120,7 @@ class Update extends Action
// Use transaction-aware document retrieval to see changes from same transaction
$document = $transactionState->getDocument($collectionTableId, $documentId, $transactionId);
} else {
$document = Authorization::skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId));
$document = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId));
}
if ($document->isEmpty()) {
@@ -135,7 +135,7 @@ class Update extends Action
]);
// Users can only manage their own roles, API keys and Admin users can manage any
$roles = Authorization::getRoles();
$roles = $dbForProject->getAuthorization()->getRoles();
if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) {
foreach (Database::PERMISSIONS as $type) {
foreach ($permissions as $permission) {
@@ -148,7 +148,7 @@ class Update extends Action
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
if (!Authorization::isRole($role)) {
if (!$dbForProject->getAuthorization()->hasRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
}
}
@@ -190,7 +190,7 @@ class Update extends Action
}
$relatedCollectionId = $relationship->getAttribute('relatedCollection');
$relatedCollection = Authorization::skip(
$relatedCollection = $dbForProject->getAuthorization()->skip(
fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId)
);
@@ -207,7 +207,7 @@ class Update extends Action
if ($relation instanceof Document) {
$relation = $this->removeReadonlyAttributes($relation, $isAPIKey || $isPrivilegedUser);
$oldDocument = Authorization::skip(fn () => $dbForProject->getDocument(
$oldDocument = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument(
'database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(),
$relation->getId()
));
@@ -245,7 +245,7 @@ class Update extends Action
// Handle transaction staging
if ($transactionId !== null) {
$transaction = ($isAPIKey || $isPrivilegedUser)
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
: $dbForProject->getDocument('transactions', $transactionId);
if ($transaction->isEmpty()) {
throw new Exception(Exception::TRANSACTION_NOT_FOUND);
@@ -105,15 +105,15 @@ class Upsert extends Action
throw new Exception($this->getMissingPayloadException());
}
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception($this->getParentNotFoundException());
}
@@ -134,7 +134,7 @@ class Upsert extends Action
// Use transaction-aware document retrieval to see changes from same transaction
$oldDocument = $transactionState->getDocument($collectionTableId, $documentId, $transactionId);
} else {
$oldDocument = Authorization::skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId));
$oldDocument = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId));
}
if ($oldDocument->isEmpty()) {
if (!empty($user->getId())) {
@@ -150,7 +150,7 @@ class Upsert extends Action
}
// Users can only manage their own roles, API keys and Admin users can manage any
$roles = Authorization::getRoles();
$roles = $dbForProject->getAuthorization()->getRoles();
if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) {
foreach (Database::PERMISSIONS as $type) {
foreach ($permissions as $permission) {
@@ -163,7 +163,7 @@ class Upsert extends Action
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
if (!Authorization::isRole($role)) {
if (!$dbForProject->getAuthorization()->hasRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
}
}
@@ -200,7 +200,7 @@ class Upsert extends Action
}
$relatedCollectionId = $relationship->getAttribute('relatedCollection');
$relatedCollection = Authorization::skip(
$relatedCollection = $dbForProject->getAuthorization()->skip(
fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId)
);
@@ -217,7 +217,7 @@ class Upsert extends Action
if ($relation instanceof Document) {
$relation = $this->removeReadonlyAttributes($relation, $isAPIKey || $isPrivilegedUser);
$oldDocument = Authorization::skip(fn () => $dbForProject->getDocument(
$oldDocument = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument(
'database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(),
$relation->getId()
));
@@ -254,7 +254,7 @@ class Upsert extends Action
// Handle transaction staging
if ($transactionId !== null) {
$transaction = ($isAPIKey || $isPrivilegedUser)
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
: $dbForProject->getDocument('transactions', $transactionId);
if ($transaction->isEmpty()) {
throw new Exception(Exception::TRANSACTION_NOT_FOUND);
@@ -76,15 +76,15 @@ class XList extends Action
public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionState $transactionState): void
{
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception($this->getParentNotFoundException());
}
@@ -112,7 +112,7 @@ class XList extends Action
$documentId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId));
$cursorDocument = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId));
if ($cursorDocument->isEmpty()) {
$type = ucfirst($this->getContext());
@@ -62,7 +62,7 @@ class Get extends Action
public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -84,7 +84,7 @@ class Create extends 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
{
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$db = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -75,7 +75,7 @@ class Delete extends Action
public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$db = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -64,7 +64,7 @@ class Get extends Action
public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -70,7 +70,7 @@ class XList extends Action
public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject): void
{
/** @var Document $database */
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -110,7 +110,7 @@ class XList extends Action
}
$indexId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->find('indexes', [
$cursorDocument = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->find('indexes', [
Query::equal('collectionInternalId', [$collection->getSequence()]),
Query::equal('databaseInternalId', [$database->getSequence()]),
Query::equal('key', [$indexId]),
@@ -77,7 +77,7 @@ class XList extends Action
public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -75,7 +75,7 @@ class Update extends Action
public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
@@ -83,7 +83,7 @@ class Get extends Action
str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getSequence(), $collectionDocument->getSequence()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS),
];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
$dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
@@ -70,7 +70,7 @@ class XList extends Action
public function action(string $databaseId, array $queries, string $search, UtopiaResponse $response, Database $dbForProject): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -73,7 +73,7 @@ class Create extends Action
}
}
$transaction = Authorization::skip(fn () => $dbForProject->createDocument('transactions', new Document([
$transaction = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('transactions', new Document([
'$id' => ID::unique(),
'$permissions' => $permissions,
'status' => 'pending',
@@ -17,7 +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;
use Utopia\Validator\ArrayList;
@@ -72,12 +72,12 @@ class Create extends Action
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Operations array cannot be empty');
}
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
// API keys and admins can read any transaction, regular users need permissions
$transaction = ($isAPIKey || $isPrivilegedUser)
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
: $dbForProject->getDocument('transactions', $transactionId);
if ($transaction->isEmpty()) {
throw new Exception(Exception::TRANSACTION_NOT_FOUND);
@@ -113,13 +113,13 @@ class Create extends Action
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$database = $databases[$operation['databaseId']] ??= Authorization::skip(fn () => $dbForProject->getDocument('databases', $operation['databaseId']));
$database = $databases[$operation['databaseId']] ??= $dbForProject->getAuthorization()->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()]] ??=
Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $operation[$this->getGroupId()]));
$dbForProject->getAuthorization()->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);
@@ -165,8 +165,10 @@ class Create extends Action
// For individual operations, enforce permissions unless using API key/admin
if (!$isAPIKey && !$isPrivilegedUser) {
$documentSecurity = $collection->getAttribute('documentSecurity', false);
$validator = new Authorization($permissionType);
$collectionValid = $validator->isValid($collection->getPermissionsByType($permissionType));
$collectionValid = $dbForProject->getAuthorization()->isValid(
new input($permissionType, $collection->getPermissionsByType($permissionType))
);
$documentValid = false;
if ($document !== null && !$document->isEmpty() && $documentSecurity) {
if ($permissionType === Database::PERMISSION_UPDATE) {
@@ -189,7 +191,7 @@ class Create extends Action
// Users can only set permissions for roles they have
if (isset($operation['data']['$permissions'])) {
$permissions = $operation['data']['$permissions'];
$roles = Authorization::getRoles();
$roles = $dbForProject->getAuthorization()->getRoles();
foreach (Database::PERMISSIONS as $type) {
foreach ($permissions as $permission) {
$permission = Permission::parse($permission);
@@ -201,7 +203,7 @@ class Create extends Action
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
if (!Authorization::isRole($role)) {
if (!$dbForProject->getAuthorization()->hasRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
}
}
@@ -230,7 +232,7 @@ class Create extends Action
}
}
$transaction = Authorization::skip(fn () => $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged, $existing, $operations) {
$transaction = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged, $existing, $operations) {
$dbForProject->createDocuments('transactionLogs', $staged);
return $dbForProject->increaseDocumentAttribute(
'transactions',
@@ -111,11 +111,11 @@ class Update extends Action
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Cannot commit and rollback at the same time');
}
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
$transaction = ($isAPIKey || $isPrivilegedUser)
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
: $dbForProject->getDocument('transactions', $transactionId);
if ($transaction->isEmpty()) {
throw new Exception(Exception::TRANSACTION_NOT_FOUND);
@@ -138,11 +138,11 @@ class Update extends Action
try {
$dbForProject->withTransaction(function () use ($dbForProject, $transactionState, $queueForDeletes, $transactionId, &$transaction, &$operations, &$totalOperations, &$databaseOperations, $queueForEvents, $queueForStatsUsage, $queueForRealtime, $queueForFunctions, $queueForWebhooks) {
Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([
'status' => 'committing',
])));
$operations = Authorization::skip(fn () => $dbForProject->find('transactionLogs', [
$operations = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->find('transactionLogs', [
Query::equal('transactionInternalId', [$transaction->getSequence()]),
Query::orderAsc(),
Query::limit(PHP_INT_MAX),
@@ -218,7 +218,7 @@ class Update extends Action
}
}
$transaction = Authorization::skip(fn () => $dbForProject->updateDocument(
$transaction = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument(
'transactions',
$transactionId,
new Document(['status' => 'committed'])
@@ -230,32 +230,32 @@ class Update extends Action
});
} catch (NotFoundException $e) {
Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([
'status' => 'failed',
])));
throw new Exception(Exception::DOCUMENT_NOT_FOUND, previous: $e);
} catch (DuplicateException|ConflictException $e) {
Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([
'status' => 'failed',
])));
throw new Exception(Exception::TRANSACTION_CONFLICT, previous: $e);
} catch (StructureException $e) {
Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([
'status' => 'failed',
])));
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage());
} catch (LimitException $e) {
Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([
'status' => 'failed',
])));
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, $e->getMessage());
} catch (TransactionException $e) {
Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([
'status' => 'failed',
])));
throw new Exception(Exception::TRANSACTION_FAILED, $e->getMessage());
} catch (QueryException $e) {
Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([
$dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([
'status' => 'failed',
])));
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
@@ -283,11 +283,11 @@ class Update extends Action
$data = $data->getArrayCopy();
}
$database = Authorization::skip(fn () => $dbForProject->findOne('databases', [
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->findOne('databases', [
Query::equal('$sequence', [$databaseInternalId])
]));
$collection = Authorization::skip(fn () => $dbForProject->findOne('database_' . $databaseInternalId, [
$collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->findOne('database_' . $databaseInternalId, [
Query::equal('$sequence', [$collectionInternalId])
]));
@@ -379,7 +379,7 @@ class Update extends Action
}
if ($rollback) {
$transaction = Authorization::skip(fn () => $dbForProject->updateDocument(
$transaction = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->updateDocument(
'transactions',
$transactionId,
new Document(['status' => 'failed'])
@@ -81,7 +81,7 @@ class Get extends Action
str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES)
];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
$dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
@@ -74,7 +74,7 @@ class XList extends Action
METRIC_DATABASES_OPERATIONS_WRITES,
];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
$dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
@@ -26,7 +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;
use Utopia\Platform\Action;
@@ -152,10 +152,10 @@ class Create extends Base
throw new Exception($validator->getDescription(), 400);
}
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
$function = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('functions', $functionId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRole);
if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
@@ -171,7 +171,7 @@ class Create extends Base
throw new Exception(Exception::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported');
}
$deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deploymentId', '')));
$deployment = $dbForProject->getAuthorization()->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,10 +185,8 @@ class Create extends Base
throw new Exception(Exception::BUILD_NOT_READY);
}
$validator = new Authorization('execute');
if (!$validator->isValid($function->getAttribute('execute'))) { // Check if user has write access to execute function
throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription());
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());
}
$jwt = ''; // initialize
@@ -286,7 +284,7 @@ class Create extends Base
if ($async) {
if (is_null($scheduledAt)) {
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
$execution = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('executions', $execution));
$queueForFunctions
->setType('http')
->setExecution($execution)
@@ -327,7 +325,7 @@ class Create extends Base
->setAttribute('scheduleInternalId', $schedule->getSequence())
->setAttribute('scheduledAt', $scheduledAt);
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
$execution = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('executions', $execution));
}
return $response
@@ -480,7 +478,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 = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
$execution = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('executions', $execution));
}
$executionResponse['headers']['x-appwrite-execution-id'] = $execution->getId();
@@ -108,7 +108,7 @@ class Delete extends Base
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('active', false);
Authorization::skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
}
}
@@ -61,10 +61,10 @@ class Get extends Base
Response $response,
Database $dbForProject
) {
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
$function = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('functions', $functionId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
@@ -67,10 +67,10 @@ class XList extends Base
Response $response,
Database $dbForProject
) {
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
$function = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('functions', $functionId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
@@ -237,7 +237,7 @@ class Create extends Base
throw new Exception(Exception::FUNCTION_ALREADY_EXISTS);
}
$schedule = Authorization::skip(
$schedule = $dbForProject->getAuthorization()->skip(
fn () => $dbForPlatform->createDocument('schedules', new Document([
'region' => $project->getAttribute('region'),
'resourceType' => SCHEDULE_RESOURCE_TYPE_FUNCTION,
@@ -365,7 +365,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 = Authorization::skip(
$rule = $dbForPlatform->getAuthorization()->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -87,7 +87,7 @@ class Delete extends Base
$schedule
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('active', false);
Authorization::skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
$queueForDeletes
->setType(DELETE_TYPE_DOCUMENT)
@@ -101,7 +101,7 @@ class Update extends Base
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('schedule', $function->getAttribute('schedule'))
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deploymentId')));
Authorization::skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
$queries = [
Query::equal('trigger', ['manual']),
@@ -112,12 +112,12 @@ class Update extends Base
Query::equal('projectInternalId', [$project->getSequence()])
];
Authorization::skip(fn () => $dbForPlatform->foreach('rules', function (Document $rule) use ($dbForPlatform, $deployment) {
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->foreach('rules', function (Document $rule) use ($dbForPlatform, $deployment) {
$rule = $rule
->setAttribute('deploymentId', $deployment->getId())
->setAttribute('deploymentInternalId', $deployment->getSequence());
Authorization::skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), $rule));
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), $rule));
}, $queries));
$queueForEvents
@@ -282,7 +282,7 @@ class Update extends Base
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('schedule', $function->getAttribute('schedule'))
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deploymentId')));
Authorization::skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
$queueForEvents->setParam('functionId', $function->getId());
@@ -83,7 +83,7 @@ class Get extends Base
str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_FAILED),
];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
$dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
@@ -75,7 +75,7 @@ class XList extends Base
str_replace("{resourceType}", RESOURCE_TYPE_FUNCTIONS, METRIC_RESOURCE_TYPE_BUILDS_FAILED),
];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
$dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
@@ -119,7 +119,7 @@ class Create extends Base
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('schedule', $function->getAttribute('schedule'))
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deploymentId')));
Authorization::skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -92,7 +92,7 @@ class Delete extends Base
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('schedule', $function->getAttribute('schedule'))
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deploymentId')));
Authorization::skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
$response->noContent();
}
@@ -109,7 +109,7 @@ class Update extends Base
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('schedule', $function->getAttribute('schedule'))
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deploymentId')));
Authorization::skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
$response->dynamic($variable, Response::MODEL_VARIABLE);
}
@@ -916,7 +916,7 @@ class Builds extends Action
->trigger();
try {
$rule = Authorization::skip(fn () => $dbForPlatform->findOne('rules', [
$rule = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->findOne('rules', [
Query::equal("projectInternalId", [$project->getSequence()]),
Query::equal("type", ["deployment"]),
Query::equal('deploymentInternalId', [$deployment->getSequence()]),
@@ -930,7 +930,7 @@ class Builds extends Action
$client->setTimeout(\intval($resource->getAttribute('timeout', '15')));
$client->addHeader('content-type', FetchClient::CONTENT_TYPE_APPLICATION_JSON);
$bucket = Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots'));
$bucket = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots'));
$configs = [
'screenshotLight' => [
@@ -1052,7 +1052,7 @@ class Builds extends Action
'metadata' => ['content_type' => $mimeType],
]);
Authorization::skip(fn () => $dbForPlatform->createDocument('bucket_' . $bucket->getSequence(), $file));
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->createDocument('bucket_' . $bucket->getSequence(), $file));
$deployment->setAttribute($key, $fileId);
}
@@ -1275,7 +1275,7 @@ class Builds extends Action
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('schedule', $resource->getAttribute('schedule'))
->setAttribute('active', !empty($resource->getAttribute('schedule')) && !empty($resource->getAttribute('deploymentId')));
Authorization::skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
}
Console::info('Deployment action finished');
@@ -1570,7 +1570,7 @@ class Builds extends Action
default => throw new \Exception('Invalid resource type')
};
$rule = Authorization::skip(fn () => $dbForPlatform->findOne('rules', [
$rule = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->findOne('rules', [
Query::equal("projectInternalId", [$project->getSequence()]),
Query::equal("type", ["deployment"]),
Query::equal("deploymentInternalId", [$deployment->getSequence()]),
@@ -274,7 +274,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();
Authorization::skip(
$dbForPlatform->getAuthorization()->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -339,7 +339,7 @@ class Create extends Action
$sitesDomain = System::getEnv('_APP_DOMAIN_SITES', '');
$domain = ID::unique() . "." . $sitesDomain;
$ruleId = md5($domain);
Authorization::skip(
$dbForPlatform->getAuthorization()->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -146,7 +146,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();
Authorization::skip(
$dbForPlatform->getAuthorization()->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -176,7 +176,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();
Authorization::skip(
$dbForPlatform->getAuthorization()->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -104,12 +104,12 @@ class Update extends Base
Query::equal('projectInternalId', [$project->getSequence()])
];
Authorization::skip(fn () => $dbForPlatform->foreach('rules', function (Document $rule) use ($dbForPlatform, $deployment) {
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->foreach('rules', function (Document $rule) use ($dbForPlatform, $deployment) {
$rule = $rule
->setAttribute('deploymentId', $deployment->getId())
->setAttribute('deploymentInternalId', $deployment->getSequence());
Authorization::skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), $rule));
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), $rule));
}, $queries));
$queueForEvents
@@ -91,7 +91,7 @@ class Get extends Base
];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
$dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
@@ -78,7 +78,7 @@ class XList extends Base
METRIC_SITES_OUTBOUND,
];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
$dbForProject->getAuthorization()->skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
@@ -5,33 +5,31 @@ namespace Appwrite\Platform\Modules\Tokens\Http\Tokens\Buckets\Files;
use Appwrite\Auth\Auth;
use Appwrite\Extend\Exception;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Authorization\Input;
use Utopia\Platform\Action as UtopiaAction;
class Action extends UtopiaAction
{
protected function getFileAndBucket(Database $dbForProject, string $bucketId, string $fileId): array
{
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAPIKey = Auth::isAppUser($dbForProject->getAuthorization()->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($dbForProject->getAuthorization()->getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
if (!$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
if (!$dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()))){
throw new Exception(Exception::USER_UNAUTHORIZED, $dbForProject->getAuthorization()->getDescription());
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
if ($fileSecurity) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
if ($file->isEmpty()) {
@@ -13,7 +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;
use Utopia\Platform\Scope\HTTP;
@@ -78,11 +78,10 @@ class Create extends Action
['bucket' => $bucket, 'file' => $file] = $this->getFileAndBucket($dbForProject, $bucketId, $fileId);
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_UPDATE);
$bucketPermission = $validator->isValid($bucket->getUpdate());
$bucketPermission = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_UPDATE, $bucket->getUpdate()));
if ($fileSecurity) {
$filePermission = $validator->isValid($file->getUpdate());
$filePermission = $dbForProject->getAuthorization()->isValid(new Input(Database::PERMISSION_UPDATE, $file->getUpdate()));
if (!$bucketPermission && !$filePermission) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
+1 -1
View File
@@ -48,7 +48,7 @@ class Migrate extends Action
callable $getProjectDB,
Registry $register,
): void {
Authorization::disable();
$dbForPlatform->getAuthorization()->disable();
if (!\array_key_exists($version, Migration::$versions)) {
Console::error("No migration found for version $version.");
+1 -1
View File
@@ -62,7 +62,7 @@ abstract class ScheduleBase extends Action
$accessedAt = $project->getAttribute('accessedAt', 0);
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
$project->setAttribute('accessedAt', DateTime::now());
Authorization::skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
}
}
}
@@ -61,9 +61,9 @@ class StatsResources extends Action
$interval = (int) System::getEnv('_APP_STATS_RESOURCES_INTERVAL', '3600');
Console::loop(function () use ($queue) {
Authorization::disable();
Authorization::setDefaultStatus(false);
Console::loop(function () use ($queue, $dbForPlatform) {
$dbForPlatform->getAuthorization()->disable();
$dbForPlatform->getAuthorization()->setDefaultStatus(false);
$last24Hours = (new \DateTime())->sub(\DateInterval::createFromDateString('24 hours'));
/**
+2 -2
View File
@@ -954,14 +954,14 @@ class Deletes extends Action
}
Console::info("Deleting screenshots for deployment " . $deployment->getId());
$bucket = ValidatorAuthorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots'));
$bucket = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots'));
if ($bucket->isEmpty()) {
Console::error('Failed to get bucket for deployment screenshots');
return;
}
foreach ($screenshotIds as $id) {
$file = ValidatorAuthorization::skip(fn () => $dbForPlatform->getDocument('bucket_' . $bucket->getSequence(), $id));
$file = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('bucket_' . $bucket->getSequence(), $id));
if ($file->isEmpty()) {
Console::error('Failed to get deployment screenshot: ' . $id);
+8 -1
View File
@@ -210,7 +210,7 @@ class Request extends UtopiaRequest
{
$forwardedUserAgent = $this->getHeader('x-forwarded-user-agent');
if (!empty($forwardedUserAgent)) {
$roles = Authorization::getRoles();
$roles = $this->authorization->getRoles();
$isAppUser = Auth::isAppUser($roles);
if ($isAppUser) {
@@ -233,4 +233,11 @@ class Request extends UtopiaRequest
ksort($params);
return md5($this->getURI() . '*' . serialize($params) . '*' . APP_CACHE_BUSTER);
}
private Authorization $authorization;
public function setAuthorization(Authorization $authorization): void
{
$this->authorization = $authorization;
}
}
+2 -2
View File
@@ -139,7 +139,7 @@ class V20 extends Filter
}
try {
$database = Authorization::skip(fn () => $dbForProject->getDocument(
$database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument(
'databases',
$databaseId
));
@@ -151,7 +151,7 @@ class V20 extends Filter
}
try {
$collection = Authorization::skip(fn () => $dbForProject->getDocument(
$collection = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument(
'database_' . $database->getSequence(),
$collectionId
));
+8 -1
View File
@@ -809,7 +809,7 @@ class Response extends SwooleResponse
}
if ($rule['sensitive']) {
$roles = Authorization::getRoles();
$roles = $this->authorization->getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
@@ -977,4 +977,11 @@ class Response extends SwooleResponse
self::$showSensitive = false;
}
}
private Authorization $authorization;
public function setAuthorization(Authorization $authorization): void
{
$this->authorization = $authorization;
}
}
@@ -17,6 +17,16 @@ class DatabasesPermissionsGuestTest extends Scope
use SideClient;
use DatabasesPermissionsScope;
private $authorization;
public function getAuthorization(): Authorization
{
if (isset($this->authorization)) {
return $this->authorization;
}
return new Authorization();
}
public function createCollection(): array
{
$database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([
@@ -111,8 +121,8 @@ class DatabasesPermissionsGuestTest extends Scope
$this->assertEquals(201, $publicResponse['headers']['status-code']);
$this->assertEquals(201, $privateResponse['headers']['status-code']);
$roles = Authorization::getRoles();
Authorization::cleanRoles();
$roles = $this->getAuthorization()->getRoles();
$this->getAuthorization()->cleanRoles();
$publicDocuments = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents', [
'content-type' => 'application/json',
@@ -134,7 +144,7 @@ class DatabasesPermissionsGuestTest extends Scope
}
foreach ($roles as $role) {
Authorization::setRole($role);
$this->getAuthorization()->addRole($role);
}
}
@@ -145,8 +155,8 @@ class DatabasesPermissionsGuestTest extends Scope
$privateCollectionId = $data['privateCollectionId'];
$databaseId = $data['databaseId'];
$roles = Authorization::getRoles();
Authorization::cleanRoles();
$roles = $this->getAuthorization()->getRoles();
$this->getAuthorization()->cleanRoles();
$publicResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents', [
'content-type' => 'application/json',
@@ -222,7 +232,7 @@ class DatabasesPermissionsGuestTest extends Scope
$this->assertEquals(401, $privateDocument['headers']['status-code']);
foreach ($roles as $role) {
Authorization::setRole($role);
$this->getAuthorization()->addRole($role);
}
}
@@ -17,6 +17,17 @@ class DatabasesPermissionsGuestTest extends Scope
use SideClient;
use DatabasesPermissionsScope;
private $authorization;
public function getAuthorization(): Authorization
{
if (isset($this->authorization)) {
return $this->authorization;
}
return new Authorization();
}
public function createTable(): array
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([
@@ -111,8 +122,8 @@ class DatabasesPermissionsGuestTest extends Scope
$this->assertEquals(201, $publicResponse['headers']['status-code']);
$this->assertEquals(201, $privateResponse['headers']['status-code']);
$roles = Authorization::getRoles();
Authorization::cleanRoles();
$roles = $this->getAuthorization()->getRoles();
$this->getAuthorization()->cleanRoles();
$publicRows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $publicTableId . '/rows', [
'content-type' => 'application/json',
@@ -134,7 +145,7 @@ class DatabasesPermissionsGuestTest extends Scope
}
foreach ($roles as $role) {
Authorization::setRole($role);
$this->getAuthorization()->addRole($role);
}
}
@@ -145,8 +156,8 @@ class DatabasesPermissionsGuestTest extends Scope
$privateTableId = $data['privateTableId'];
$databaseId = $data['databaseId'];
$roles = Authorization::getRoles();
Authorization::cleanRoles();
$roles = $this->getAuthorization()->getRoles();
$this->getAuthorization()->cleanRoles();
$publicResponse = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $publicTableId . '/rows', [
'content-type' => 'application/json',
@@ -222,7 +233,7 @@ class DatabasesPermissionsGuestTest extends Scope
$this->assertEquals(401, $privateRow['headers']['status-code']);
foreach ($roles as $role) {
Authorization::setRole($role);
$this->getAuthorization()->addRole($role);
}
}
+21 -10
View File
@@ -13,13 +13,24 @@ use Utopia\Database\Validator\Roles;
class AuthTest extends TestCase
{
private $authorization;
public function getAuthorization(): Authorization
{
if (isset($this->authorization)) {
return $this->authorization;
}
return new Authorization();
}
/**
* Reset Roles
*/
public function tearDown(): void
{
Authorization::cleanRoles();
Authorization::setRole(Role::any()->toString());
$this->getAuthorization()->cleanRoles();
$this->getAuthorization()->addRole(Role::any()->toString());
}
public function testCookieName(): void
@@ -347,7 +358,7 @@ class AuthTest extends TestCase
'$id' => ''
]);
$roles = Auth::getRoles($user);
$roles = Auth::getRoles($user, new Authorization());
$this->assertCount(1, $roles);
$this->assertContains(Role::guests()->toString(), $roles);
}
@@ -383,7 +394,7 @@ class AuthTest extends TestCase
]
]);
$roles = Auth::getRoles($user);
$roles = Auth::getRoles($user, $this->getAuthorization());
$this->assertCount(13, $roles);
$this->assertContains(Role::users()->toString(), $roles);
@@ -404,21 +415,21 @@ class AuthTest extends TestCase
$user['emailVerification'] = false;
$user['phoneVerification'] = false;
$roles = Auth::getRoles($user);
$roles = Auth::getRoles($user, $this->getAuthorization());
$this->assertContains(Role::users(Roles::DIMENSION_UNVERIFIED)->toString(), $roles);
$this->assertContains(Role::user(ID::custom('123'), Roles::DIMENSION_UNVERIFIED)->toString(), $roles);
// Enable single verification type
$user['emailVerification'] = true;
$roles = Auth::getRoles($user);
$roles = Auth::getRoles($user, $this->getAuthorization());
$this->assertContains(Role::users(Roles::DIMENSION_VERIFIED)->toString(), $roles);
$this->assertContains(Role::user(ID::custom('123'), Roles::DIMENSION_VERIFIED)->toString(), $roles);
}
public function testPrivilegedUserRoles(): void
{
Authorization::setRole(Auth::USER_ROLE_OWNER);
$this->getAuthorization()->addRole(Auth::USER_ROLE_OWNER);
$user = new Document([
'$id' => ID::custom('123'),
'emailVerification' => true,
@@ -444,7 +455,7 @@ class AuthTest extends TestCase
]
]);
$roles = Auth::getRoles($user);
$roles = Auth::getRoles($user, $this->getAuthorization());
$this->assertCount(7, $roles);
$this->assertNotContains(Role::users()->toString(), $roles);
@@ -462,7 +473,7 @@ class AuthTest extends TestCase
public function testAppUserRoles(): void
{
Authorization::setRole(Auth::USER_ROLE_APPS);
$this->getAuthorization()->addRole(Auth::USER_ROLE_APPS);
$user = new Document([
'$id' => ID::custom('123'),
'memberships' => [
@@ -486,7 +497,7 @@ class AuthTest extends TestCase
]
]);
$roles = Auth::getRoles($user);
$roles = Auth::getRoles($user, $this->getAuthorization());
$this->assertCount(7, $roles);
$this->assertNotContains(Role::users()->toString(), $roles);
+13 -2
View File
@@ -8,6 +8,7 @@ use PHPUnit\Framework\TestCase;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
class MessagingChannelsTest extends TestCase
{
@@ -34,6 +35,16 @@ class MessagingChannelsTest extends TestCase
'functions.1',
];
private $authorization;
public function getAuthorization(): Authorization
{
if (isset($this->authorization)) {
return $this->authorization;
}
return new Authorization();
}
public function setUp(): void
{
/**
@@ -66,7 +77,7 @@ class MessagingChannelsTest extends TestCase
]
]);
$roles = Auth::getRoles($user);
$roles = Auth::getRoles($user, $this->getAuthorization());
$parsedChannels = Realtime::convertChannels([0 => $channel], $user->getId());
@@ -90,7 +101,7 @@ class MessagingChannelsTest extends TestCase
'$id' => ''
]);
$roles = Auth::getRoles($user);
$roles = Auth::getRoles($user, $this->getAuthorization());
$parsedChannels = Realtime::convertChannels([0 => $channel], $user->getId());