1
0
Fork 0
mirror of synced 2024-06-13 08:14:46 +12:00

Avatars tests are green!

This commit is contained in:
Eldad Fux 2024-04-14 22:17:07 +02:00
parent 6cade89369
commit 766b2ba13e
10 changed files with 330 additions and 256 deletions

View file

@ -58,13 +58,13 @@ use Utopia\System\System;
$oauthDefaultSuccess = '/auth/oauth2/success';
$oauthDefaultFailure = '/auth/oauth2/failure';
$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Authorization $auth) {
$roles = $auth->getRoles();
$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Authorization $authorization) {
$roles = $authorization->getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
/** @var Utopia\Database\Document $user */
$userFromRequest = $auth->skip(fn () => $dbForProject->getDocument('users', $userId));
$userFromRequest = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId));
if ($userFromRequest->isEmpty()) {
throw new Exception(Exception::USER_INVALID_TOKEN);
@ -110,7 +110,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
$detector->getDevice()
));
$auth->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$session = $dbForProject->createDocument('sessions', $session
->setAttribute('$permissions', [
@ -120,7 +120,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
]));
$dbForProject->purgeCachedDocument('users', $user->getId());
$auth->skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId()));
$authorization->skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId()));
$dbForProject->purgeCachedDocument('users', $user->getId());
if ($verifiedToken->getAttribute('type') === Auth::TOKEN_TYPE_MAGIC_URL) {
@ -193,8 +193,8 @@ Http::post('/v1/account')
->inject('dbForProject')
->inject('queueForEvents')
->inject('hooks')
->inject('auth')
->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks, Authorization $auth) {
->inject('authorization')
->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks, Authorization $authorization) {
$email = \strtolower($email);
if ('console' === $project->getId()) {
@ -269,9 +269,9 @@ Http::post('/v1/account')
'accessedAt' => DateTime::now(),
]);
$user->removeAttribute('$internalId');
$user = $auth->skip(fn () => $dbForProject->createDocument('users', $user));
$user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
try {
$target = $auth->skip(fn () => $dbForProject->createDocument('targets', new Document([
$target = $authorization->skip(fn () => $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
@ -295,9 +295,9 @@ Http::post('/v1/account')
throw new Exception(Exception::USER_ALREADY_EXISTS);
}
$auth->removeRole(Role::guests()->toString());
$auth->addRole(Role::user($user->getId())->toString());
$auth->addRole(Role::users()->toString());
$authorization->removeRole(Role::guests()->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::users()->toString());
$queueForEvents->setParam('userId', $user->getId());
@ -392,10 +392,10 @@ Http::get('/v1/account/sessions')
->inject('response')
->inject('user')
->inject('locale')
->inject('auth')
->action(function (Response $response, Document $user, Locale $locale, Authorization $auth) {
->inject('authorization')
->action(function (Response $response, Document $user, Locale $locale, Authorization $authorization) {
$roles = $auth->getRoles();
$roles = $authorization->getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
@ -500,10 +500,10 @@ Http::get('/v1/account/sessions/:sessionId')
->inject('response')
->inject('user')
->inject('locale')
->inject('auth')
->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Authorization $auth) {
->inject('authorization')
->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Authorization $authorization) {
$roles = $auth->getRoles();
$roles = $authorization->getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
@ -714,8 +714,8 @@ Http::post('/v1/account/sessions/email')
->inject('geodb')
->inject('queueForEvents')
->inject('hooks')
->inject('auth')
->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Hooks $hooks, Authorization $auth) {
->inject('authorization')
->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Hooks $hooks, Authorization $authorization) {
$email = \strtolower($email);
$protocol = $request->getProtocol();
@ -731,7 +731,7 @@ Http::post('/v1/account/sessions/email')
throw new Exception(Exception::USER_BLOCKED); // User is in status blocked
}
$roles = $auth->getRoles();
$roles = $authorization->getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
@ -762,7 +762,7 @@ Http::post('/v1/account/sessions/email')
$detector->getDevice()
));
$auth->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
// Re-hash if not using recommended algo
if ($user->getAttribute('hash') !== Auth::DEFAULT_ALGO) {
@ -837,10 +837,10 @@ Http::post('/v1/account/sessions/anonymous')
->inject('dbForProject')
->inject('geodb')
->inject('queueForEvents')
->inject('auth')
->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $auth) {
->inject('authorization')
->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $authorization) {
$protocol = $request->getProtocol();
$roles = $auth->getRoles();
$roles = $authorization->getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
@ -886,7 +886,7 @@ Http::post('/v1/account/sessions/anonymous')
'accessedAt' => DateTime::now(),
]);
$user->removeAttribute('$internalId');
$auth->skip(fn () => $dbForProject->createDocument('users', $user));
$authorization->skip(fn () => $dbForProject->createDocument('users', $user));
// Create session token
$duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
@ -912,7 +912,7 @@ Http::post('/v1/account/sessions/anonymous')
$detector->getDevice()
));
$auth->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$session = $dbForProject->createDocument('sessions', $session-> setAttribute('$permissions', [
Permission::read(Role::user($user->getId())),
@ -1138,8 +1138,8 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
->inject('dbForProject')
->inject('geodb')
->inject('queueForEvents')
->inject('auth')
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $auth) use ($oauthDefaultSuccess) {
->inject('authorization')
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $authorization) use ($oauthDefaultSuccess) {
$protocol = $request->getProtocol();
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
@ -1363,7 +1363,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
'accessedAt' => DateTime::now(),
]);
$user->removeAttribute('$internalId');
$user = $auth->skip(fn () => $dbForProject->createDocument('users', $user));
$user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
$dbForProject->createDocument('targets', new Document([
'$permissions' => [
@ -1382,8 +1382,8 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
}
}
$auth->addRole(Role::user($user->getId())->toString());
$auth->addRole(Role::users()->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::users()->toString());
if (false === $user->getAttribute('status')) { // Account is blocked
$failureRedirect(Exception::USER_BLOCKED); // User is in status blocked
@ -1442,7 +1442,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
$dbForProject->updateDocument('users', $user->getId(), $user);
$auth->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$state['success'] = URLParser::parse($state['success']);
$query = URLParser::parseQuery($state['success']['query']);
@ -1464,7 +1464,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
'ip' => $request->getIP(),
]);
$auth->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
@ -1663,8 +1663,8 @@ Http::post('/v1/account/tokens/magic-url')
->inject('locale')
->inject('queueForEvents')
->inject('queueForMails')
->inject('Auth')
->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $auth) {
->inject('authorization')
->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) {
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled');
@ -1674,7 +1674,7 @@ Http::post('/v1/account/tokens/magic-url')
$phrase = Phrase::generate();
}
$roles = $auth->getRoles();
$roles = $authorization->getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
@ -1729,7 +1729,7 @@ Http::post('/v1/account/tokens/magic-url')
]);
$user->removeAttribute('$internalId');
$user = $auth->skip(fn () => $dbForProject->createDocument('users', $user));
$user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
}
$tokenSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_MAGIC_URL);
@ -1746,7 +1746,7 @@ Http::post('/v1/account/tokens/magic-url')
'ip' => $request->getIP(),
]);
$auth->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
@ -1906,8 +1906,8 @@ Http::post('/v1/account/tokens/email')
->inject('locale')
->inject('queueForEvents')
->inject('queueForMails')
->inject('auth')
->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $auth) {
->inject('authorization')
->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) {
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled');
}
@ -1916,7 +1916,7 @@ Http::post('/v1/account/tokens/email')
$phrase = Phrase::generate();
}
$roles = $auth->getRoles();
$roles = $authorization->getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
@ -1969,7 +1969,7 @@ Http::post('/v1/account/tokens/email')
]);
$user->removeAttribute('$internalId');
$user = $auth->skip(fn () => $dbForProject->createDocument('users', $user));
$user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
}
$tokenSecret = Auth::codeGenerator(6);
@ -1986,7 +1986,7 @@ Http::post('/v1/account/tokens/email')
'ip' => $request->getIP(),
]);
$auth->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
@ -2136,7 +2136,7 @@ Http::put('/v1/account/sessions/magic-url')
->inject('locale')
->inject('geodb')
->inject('queueForEvents')
->inject('auth')
->inject('authorization')
->action($createSession);
Http::put('/v1/account/sessions/phone')
@ -2167,7 +2167,7 @@ Http::put('/v1/account/sessions/phone')
->inject('locale')
->inject('geodb')
->inject('queueForEvents')
->inject('auth')
->inject('authorization')
->action($createSession);
Http::post('/v1/account/tokens/phone')
@ -2198,13 +2198,13 @@ Http::post('/v1/account/tokens/phone')
->inject('queueForEvents')
->inject('queueForMessaging')
->inject('locale')
->inject('auth')
->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, Authorization $auth) {
->inject('authorization')
->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, Authorization $authorization) {
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
$roles = $auth->getRoles();
$roles = $authorization->getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
@ -2248,9 +2248,9 @@ Http::post('/v1/account/tokens/phone')
]);
$user->removeAttribute('$internalId');
$user = $auth->skip(fn () => $dbForProject->createDocument('users', $user));
$user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
try {
$target = $auth->skip(fn () => $dbForProject->createDocument('targets', new Document([
$target = $authorization->skip(fn () => $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
@ -2285,7 +2285,7 @@ Http::post('/v1/account/tokens/phone')
'ip' => $request->getIP(),
]);
$auth->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
@ -2427,8 +2427,8 @@ Http::get('/v1/account/logs')
->inject('locale')
->inject('geodb')
->inject('dbForProject')
->inject('auth')
->action(function (array $queries, Response $response, Document $user, Locale $locale, Reader $geodb, Database $dbForProject, Authorization $auth) {
->inject('authorization')
->action(function (array $queries, Response $response, Document $user, Locale $locale, Reader $geodb, Database $dbForProject, Authorization $authorization) {
try {
$queries = Query::parseQueries($queries);
@ -2440,7 +2440,7 @@ Http::get('/v1/account/logs')
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new EventAudit($dbForProject, $auth);
$audit = new EventAudit($dbForProject, $authorization);
$logs = $audit->getLogsByUser($user->getInternalId(), $limit, $offset);
@ -2603,8 +2603,8 @@ Http::patch('/v1/account/email')
->inject('queueForEvents')
->inject('project')
->inject('hooks')
->inject('auth')
->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $auth) {
->inject('authorization')
->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $authorization) {
// passwordUpdate will be empty if the user has never set a password
$passwordUpdate = $user->getAttribute('passwordUpdate');
@ -2643,7 +2643,7 @@ Http::patch('/v1/account/email')
->setAttribute('passwordUpdate', DateTime::now());
}
$target = $auth->skip(fn () => $dbForProject->findOne('targets', [
$target = $authorization->skip(fn () => $dbForProject->findOne('targets', [
Query::equal('identifier', [$email]),
]));
@ -2659,7 +2659,7 @@ Http::patch('/v1/account/email')
$oldTarget = $user->find('identifier', $oldEmail, 'targets');
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
$auth->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email)));
$authorization->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email)));
}
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Duplicate) {
@ -2696,8 +2696,8 @@ Http::patch('/v1/account/phone')
->inject('queueForEvents')
->inject('project')
->inject('hooks')
->inject('auth')
->action(function (string $phone, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $auth) {
->inject('authorization')
->action(function (string $phone, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $authorization) {
// passwordUpdate will be empty if the user has never set a password
$passwordUpdate = $user->getAttribute('passwordUpdate');
@ -2710,7 +2710,7 @@ Http::patch('/v1/account/phone')
$hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]);
$target = $auth->skip(fn () => $dbForProject->findOne('targets', [
$target = $authorization->skip(fn () => $dbForProject->findOne('targets', [
Query::equal('identifier', [$phone]),
]));
@ -2741,7 +2741,7 @@ Http::patch('/v1/account/phone')
$oldTarget = $user->find('identifier', $oldPhone, 'targets');
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
$auth->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone)));
$authorization->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone)));
}
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Duplicate $th) {
@ -2856,14 +2856,14 @@ Http::post('/v1/account/recovery')
->inject('locale')
->inject('queueForMails')
->inject('queueForEvents')
->inject('auth')
->action(function (string $email, string $url, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents, Authorization $auth) {
->inject('authorization')
->action(function (string $email, string $url, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents, Authorization $authorization) {
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
}
$roles = $auth->getRoles();
$roles = $authorization->getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
@ -2897,7 +2897,7 @@ Http::post('/v1/account/recovery')
'ip' => $request->getIP(),
]);
$auth->addRole(Role::user($profile->getId())->toString());
$authorization->addRole(Role::user($profile->getId())->toString());
$recovery = $dbForProject->createDocument('tokens', $recovery
->setAttribute('$permissions', [
@ -3034,8 +3034,8 @@ Http::put('/v1/account/recovery')
->inject('project')
->inject('queueForEvents')
->inject('hooks')
->inject('auth')
->action(function (string $userId, string $secret, string $password, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks, Authorization $auth) {
->inject('authorization')
->action(function (string $userId, string $secret, string $password, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks, Authorization $authorization) {
$profile = $dbForProject->getDocument('users', $userId);
if ($profile->isEmpty()) {
@ -3049,7 +3049,7 @@ Http::put('/v1/account/recovery')
throw new Exception(Exception::USER_INVALID_TOKEN);
}
$auth->addRole(Role::user($profile->getId())->toString());
$authorization->addRole(Role::user($profile->getId())->toString());
$newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
@ -3119,8 +3119,8 @@ Http::post('/v1/account/verification')
->inject('locale')
->inject('queueForEvents')
->inject('queueForMails')
->inject('auth')
->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $auth) {
->inject('authorization')
->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) {
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
@ -3130,7 +3130,7 @@ Http::post('/v1/account/verification')
throw new Exception(Exception::USER_EMAIL_ALREADY_VERIFIED);
}
$roles = $auth->getRoles();
$roles = $authorization->getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$verificationSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_VERIFICATION);
@ -3147,7 +3147,7 @@ Http::post('/v1/account/verification')
'ip' => $request->getIP(),
]);
$auth->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$verification = $dbForProject->createDocument('tokens', $verification
->setAttribute('$permissions', [
@ -3278,10 +3278,10 @@ Http::put('/v1/account/verification')
->inject('user')
->inject('dbForProject')
->inject('queueForEvents')
->inject('auth')
->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $auth) {
->inject('authorization')
->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) {
$profile = $auth->skip(fn () => $dbForProject->getDocument('users', $userId));
$profile = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId));
if ($profile->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
@ -3294,7 +3294,7 @@ Http::put('/v1/account/verification')
throw new Exception(Exception::USER_INVALID_TOKEN);
}
$auth->addRole(Role::user($profile->getId())->toString());
$authorization->addRole(Role::user($profile->getId())->toString());
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true));
@ -3342,8 +3342,8 @@ Http::post('/v1/account/verification/phone')
->inject('queueForMessaging')
->inject('project')
->inject('locale')
->inject('auth')
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, Authorization $auth) {
->inject('authorization')
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, Authorization $authorization) {
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
@ -3356,7 +3356,7 @@ Http::post('/v1/account/verification/phone')
throw new Exception(Exception::USER_PHONE_ALREADY_VERIFIED);
}
$roles = $auth->getRoles();
$roles = $authorization->getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$secret = Auth::codeGenerator();
@ -3373,7 +3373,7 @@ Http::post('/v1/account/verification/phone')
'ip' => $request->getIP(),
]);
$auth->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$verification = $dbForProject->createDocument('tokens', $verification
->setAttribute('$permissions', [
@ -3452,10 +3452,10 @@ Http::put('/v1/account/verification/phone')
->inject('user')
->inject('dbForProject')
->inject('queueForEvents')
->inject('auth')
->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $auth) {
->inject('authorization')
->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) {
$profile = $auth->skip(fn () => $dbForProject->getDocument('users', $userId));
$profile = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId));
if ($profile->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
@ -3467,7 +3467,7 @@ Http::put('/v1/account/verification/phone')
throw new Exception(Exception::USER_INVALID_TOKEN);
}
$auth->addRole(Role::user($profile->getId())->toString());
$authorization->addRole(Role::user($profile->getId())->toString());
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('phoneVerification', true));
@ -4153,13 +4153,13 @@ Http::post('/v1/account/targets/push')
->inject('request')
->inject('response')
->inject('dbForProject')
->inject('auth')
->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $auth) {
->inject('authorization')
->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) {
$targetId = $targetId == 'unique()' ? ID::unique() : $targetId;
$provider = $auth->skip(fn () => $dbForProject->getDocument('providers', $providerId));
$provider = $authorization->skip(fn () => $dbForProject->getDocument('providers', $providerId));
$target = $auth->skip(fn () => $dbForProject->getDocument('targets', $targetId));
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId));
if (!$target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
@ -4226,10 +4226,10 @@ Http::put('/v1/account/targets/:targetId/push')
->inject('request')
->inject('response')
->inject('dbForProject')
->inject('auth')
->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $auth) {
->inject('authorization')
->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) {
$target = $auth->skip(fn () => $dbForProject->getDocument('targets', $targetId));
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId));
if ($target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
@ -4282,9 +4282,9 @@ Http::delete('/v1/account/targets/:targetId/push')
->inject('request')
->inject('response')
->inject('dbForProject')
->inject('auth')
->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $auth) {
$target = $auth->skip(fn () => $dbForProject->getDocument('targets', $targetId));
->inject('authorization')
->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) {
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId));
if ($target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_NOT_FOUND);

View file

@ -17,6 +17,8 @@ use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\Audit\Audit;
use Utopia\Cache\Cache;
use Utopia\Config\Config;
use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Adapter\MySQL;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
@ -79,9 +81,9 @@ Http::post('/v1/projects')
->inject('dbForConsole')
->inject('cache')
->inject('pools')
->inject('auth')
->inject('authorization')
->inject('connections')
->action(function (string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForConsole, Cache $cache, Group $pools, Authorization $auth, Connections $connections) {
->action(function (string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForConsole, Cache $cache, array $pools, Authorization $authorization, Connections $connections) {
$team = $dbForConsole->getDocument('teams', $teamId);
@ -181,19 +183,27 @@ Http::post('/v1/projects')
throw new Exception(Exception::PROJECT_ALREADY_EXISTS);
}
$connection = $pools->get($database)->pop();
$connections->add($connection);
$dbForProject = new Database($connection->getResource(), $cache);
$dbForProject->setAuthorization($auth);
$pool = $pools['pools-database-'.$project->getAttribute('database')]['pool'];
$dsn = $pools['pools-database-'.$project->getAttribute('database')]['dsn'];
$connection = $pool->get();
$connections->add($connection, $pool);
$adapter = match ($dsn->getScheme()) {
'mariadb' => new MariaDB($connection),
'mysql' => new MySQL($connection),
default => null
};
$adapter->setDatabase($dsn->getPath());
$dbForProject = new Database($adapter, $cache);
$dbForProject->setAuthorization($authorization);
$dbForProject->setNamespace("_{$project->getInternalId()}");
$dbForProject->create();
$audit = new Audit($dbForProject, $auth);
$audit = new Audit($dbForProject, $authorization);
$audit->setup();
$adapter = new TimeLimit('', 0, 1, $dbForProject, $auth);
$adapter = new TimeLimit('', 0, 1, $dbForProject, $authorization);
$adapter->setup();
/** @var array $collections */
$collections = Config::getParam('collections', [])['projects'] ?? [];

View file

@ -64,16 +64,16 @@ Http::post('/v1/teams')
->inject('user')
->inject('dbForProject')
->inject('queueForEvents')
->inject('auth')
->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $auth) {
->inject('authorization')
->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) {
$isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles());
$isAppUser = Auth::isAppUser($auth->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
$isAppUser = Auth::isAppUser($authorization->getRoles());
$teamId = $teamId == 'unique()' ? ID::unique() : $teamId;
try {
$team = $auth->skip(fn () => $dbForProject->createDocument('teams', new Document([
$team = $authorization->skip(fn () => $dbForProject->createDocument('teams', new Document([
'$id' => $teamId,
'$permissions' => [
Permission::read(Role::team($teamId)),
@ -398,10 +398,10 @@ Http::post('/v1/teams/:teamId/memberships')
->inject('queueForMails')
->inject('queueForMessaging')
->inject('queueForEvents')
->inject('auth')
->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, Authorization $auth) {
$isAPIKey = Auth::isAppUser($auth->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles());
->inject('authorization')
->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, Authorization $authorization) {
$isAPIKey = Auth::isAppUser($authorization->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
if (empty($url)) {
if (!$isAPIKey && !$isPrivilegedUser) {
@ -412,8 +412,8 @@ Http::post('/v1/teams/:teamId/memberships')
if (empty($userId) && empty($email) && empty($phone)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'At least one of userId, email, or phone is required');
}
$isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles());
$isAppUser = Auth::isAppUser($auth->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
$isAppUser = Auth::isAppUser($authorization->getRoles());
if (!$isPrivilegedUser && !$isAppUser && empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED);
@ -473,7 +473,7 @@ Http::post('/v1/teams/:teamId/memberships')
try {
$userId = ID::unique();
$invitee = $auth->skip(fn () => $dbForProject->createDocument('users', new Document([
$invitee = $authorization->skip(fn () => $dbForProject->createDocument('users', new Document([
'$id' => $userId,
'$permissions' => [
Permission::read(Role::any()),
@ -509,7 +509,7 @@ Http::post('/v1/teams/:teamId/memberships')
}
}
$isOwner = $auth->isRole('team:' . $team->getId() . '/owner');
$isOwner = $authorization->isRole('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');
@ -541,12 +541,12 @@ Http::post('/v1/teams/:teamId/memberships')
if ($isPrivilegedUser || $isAppUser) { // Allow admin to create membership
try {
$membership = $auth->skip(fn () => $dbForProject->createDocument('memberships', $membership));
$membership = $authorization->skip(fn () => $dbForProject->createDocument('memberships', $membership));
} catch (Duplicate $th) {
throw new Exception(Exception::TEAM_INVITE_ALREADY_EXISTS);
}
$auth->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
$authorization->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
$dbForProject->purgeCachedDocument('users', $invitee->getId());
} else {
@ -866,8 +866,8 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId')
->inject('user')
->inject('dbForProject')
->inject('queueForEvents')
->inject('auth')
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $auth) {
->inject('authorization')
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@ -884,9 +884,9 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId')
throw new Exception(Exception::USER_NOT_FOUND);
}
$isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles());
$isAppUser = Auth::isAppUser($auth->getRoles());
$isOwner = $auth->isRole('team:' . $team->getId() . '/owner');
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
$isAppUser = Auth::isAppUser($authorization->getRoles());
$isOwner = $authorization->isRole('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 modify roles');
@ -942,8 +942,8 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->inject('project')
->inject('geodb')
->inject('queueForEvents')
->inject('auth')
->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents, Authorization $auth) {
->inject('authorization')
->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents, Authorization $authorization) {
$protocol = $request->getProtocol();
$membership = $dbForProject->getDocument('memberships', $membershipId);
@ -952,7 +952,7 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status')
throw new Exception(Exception::MEMBERSHIP_NOT_FOUND);
}
$team = $auth->skip(fn () => $dbForProject->getDocument('teams', $teamId));
$team = $authorization->skip(fn () => $dbForProject->getDocument('teams', $teamId));
if ($team->isEmpty()) {
throw new Exception(Exception::TEAM_NOT_FOUND);
@ -987,11 +987,11 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->setAttribute('confirm', true)
;
$auth->skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true)));
$authorization->skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true)));
// Log user in
$auth->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$detector = new Detector($request->getUserAgent('UNKNOWN'));
$record = $geodb->get($request->getIP());
@ -1021,13 +1021,13 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status')
$dbForProject->purgeCachedDocument('users', $user->getId());
$auth->addRole(Role::user($userId)->toString());
$authorization->addRole(Role::user($userId)->toString());
$membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership);
$dbForProject->purgeCachedDocument('users', $user->getId());
$auth->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
$authorization->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
$queueForEvents
->setParam('teamId', $team->getId())
@ -1072,8 +1072,8 @@ Http::delete('/v1/teams/:teamId/memberships/:membershipId')
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('auth')
->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) {
->inject('authorization')
->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) {
$membership = $dbForProject->getDocument('memberships', $membershipId);
@ -1108,7 +1108,7 @@ Http::delete('/v1/teams/:teamId/memberships/:membershipId')
$dbForProject->purgeCachedDocument('users', $user->getId());
if ($membership->getAttribute('confirm')) { // Count only confirmed members
$auth->skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0));
$authorization->skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0));
}
$queueForEvents
@ -1137,8 +1137,8 @@ Http::get('/v1/teams/:teamId/logs')
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->inject('auth')
->action(function (string $teamId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) {
->inject('authorization')
->action(function (string $teamId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) {
$team = $dbForProject->getDocument('teams', $teamId);
@ -1156,7 +1156,7 @@ Http::get('/v1/teams/:teamId/logs')
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject, $auth);
$audit = new Audit($dbForProject, $authorization);
$resource = 'team/' . $team->getId();
$logs = $audit->getLogsByResource($resource, $limit, $offset);

View file

@ -769,8 +769,8 @@ Http::get('/v1/users/:userId/logs')
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->inject('auth')
->action(function (string $userId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) {
->inject('authorization')
->action(function (string $userId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) {
$user = $dbForProject->getDocument('users', $userId);
@ -788,7 +788,7 @@ Http::get('/v1/users/:userId/logs')
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject, $auth);
$audit = new Audit($dbForProject, $authorization);
$logs = $audit->getLogsByUser($user->getInternalId(), $limit, $offset);
@ -2105,8 +2105,8 @@ Http::get('/v1/users/usage')
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
->inject('response')
->inject('dbForProject')
->inject('auth')
->action(function (string $range, Response $response, Database $dbForProject, Authorization $auth) {
->inject('authorization')
->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) {
$periods = Config::getParam('usage', []);
$stats = $usage = [];
@ -2116,7 +2116,7 @@ Http::get('/v1/users/usage')
METRIC_SESSIONS,
];
$auth->skip(function () use ($dbForProject, $days, $metrics, &$stats) {
$authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $count => $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),

View file

@ -5,6 +5,7 @@ use Appwrite\Event\Event;
use Appwrite\Event\Usage;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Network\Validator\Origin;
use Appwrite\Utopia\Queue\Connections;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Request\Filters\V16 as RequestV16;
use Appwrite\Utopia\Request\Filters\V17 as RequestV17;
@ -635,7 +636,8 @@ Http::error()
->inject('logger')
->inject('log')
->inject('authorization')
->action(function (Throwable $error, Document $user, ?Route $route, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $authorization) {
->inject('connections')
->action(function (Throwable $error, Document $user, ?Route $route, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $authorization, Connections $connections) {
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
if(is_null($route)) {
@ -691,6 +693,7 @@ Http::error()
$trace = $error->getTrace();
if (php_sapi_name() === 'cli') {
Console::error('[Error] ------------------');
Console::error('[Error] Timestamp: ' . date('c', time()));
if ($route) {
@ -728,7 +731,7 @@ Http::error()
/** Wrap all exceptions inside Appwrite\Extend\Exception */
if (!($error instanceof AppwriteException)) {
$error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error);
$error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, (int)$code, $error);
}
switch ($code) { // Don't show 500 errors!
@ -748,7 +751,7 @@ Http::error()
break;
default:
$code = 500; // All other errors get the generic 500 server error status code
$message = 'Server Error';
$message = (Http::getMode() === Http::MODE_TYPE_DEVELOPMENT) ? $message : 'Server Error';
}
//$_SERVER = []; // Reset before reporting to error log to avoid keys being compromised
@ -794,6 +797,8 @@ Http::error()
$response->html($layout->render());
}
$connections->reclaim();
$response->dynamic(
new Document($output),
Http::isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR
@ -884,4 +889,20 @@ foreach (Config::getParam('services', []) as $service) {
//include_once $service['controller'];
}
include_once 'shared/api.php';
include_once 'shared/api/auth.php';
include_once 'api/account.php';
include_once 'api/avatars.php';
//include_once 'api/database.php';
//include_once 'api/functions.php';
//include_once 'api/graphql.php';
//include_once 'api/health.php';
include_once 'api/locale.php';
//include_once 'api/messaging.php';
//include_once 'api/migrations.php';
include_once 'api/projects.php';
//include_once 'api/proxy.php';
//include_once 'api/storage.php';
include_once 'api/teams.php';
include_once 'api/users.php';
//include_once 'api/vcs.php';

View file

@ -29,6 +29,7 @@ use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Authorization\Input;
use Utopia\System\System;
use Utopia\Http\Http;
use Utopia\Http\Route;
use Utopia\Http\Validator\WhiteList;
$parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) {
@ -152,7 +153,7 @@ $databaseListener = function (string $event, Document $document, Document $proje
Http::init()
->groups(['api'])
->inject('utopia')
->inject('route')
->inject('request')
->inject('dbForConsole')
->inject('project')
@ -160,10 +161,8 @@ Http::init()
->inject('session')
->inject('servers')
->inject('mode')
->inject('auth')
->action(function (Http $utopia, Request $request, Database $dbForConsole, Document $project, Document $user, ?Document $session, array $servers, string $mode, Authorization $auth) {
$route = $utopia->getRoute();
->inject('authorization')
->action(function (Route $route, Request $request, Database $dbForConsole, Document $project, Document $user, ?Document $session, array $servers, string $mode, Authorization $authorization) {
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
@ -225,8 +224,8 @@ Http::init()
throw new Exception(Exception::PROJECT_KEY_EXPIRED);
}
$auth->addRole(Auth::USER_ROLE_APPS);
$auth->setDefaultStatus(false); // Cancel security segmentation for API keys.
$authorization->addRole(Auth::USER_ROLE_APPS);
$authorization->setDefaultStatus(false); // Cancel security segmentation for API keys.
$accessedAt = $key->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCCESS)) > $accessedAt) {
@ -252,10 +251,10 @@ Http::init()
}
}
$auth->addRole($role);
$authorization->addRole($role);
foreach (Auth::getRoles($user, $auth) as $authRole) {
$auth->addRole($authRole);
foreach (Auth::getRoles($user, $authorization) as $authRole) {
$authorization->addRole($authRole);
}
$service = $route->getLabel('sdk.namespace', '');
@ -263,7 +262,7 @@ Http::init()
if (
array_key_exists($service, $project->getAttribute('services', []))
&& !$project->getAttribute('services', [])[$service]
&& !(Auth::isPrivilegedUser($auth->getRoles()) || Auth::isAppUser($auth->getRoles()))
&& !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles()))
) {
throw new Exception(Exception::GENERAL_SERVICE_DISABLED);
}
@ -302,7 +301,7 @@ Http::init()
Http::init()
->groups(['api'])
->inject('utopia')
->inject('route')
->inject('request')
->inject('response')
->inject('project')
@ -316,15 +315,12 @@ Http::init()
->inject('queueForUsage')
->inject('dbForProject')
->inject('mode')
->inject('auth')
->action(function (Http $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, string $mode, Authorization $auth) use ($databaseListener) {
$route = $utopia->getRoute();
->inject('authorization')
->action(function (Route $route, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, string $mode, Authorization $authorization) use ($databaseListener) {
if (
array_key_exists('rest', $project->getAttribute('apis', []))
&& !$project->getAttribute('apis', [])['rest']
&& !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles()))
&& !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles()))
) {
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
}
@ -340,7 +336,7 @@ Http::init()
foreach ($abuseKeyLabel as $abuseKey) {
$start = $request->getContentRangeStart();
$end = $request->getContentRangeEnd();
$timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject, $auth);
$timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject, $authorization);
$timeLimit
->setParam('{projectId}', $project->getId())
->setParam('{userId}', $user->getId())
@ -354,7 +350,7 @@ Http::init()
$closestLimit = null;
$roles = $auth->getRoles();
$roles = $authorization->getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
@ -420,7 +416,7 @@ Http::init()
$useCache = $route->getLabel('cache', false);
if ($useCache) {
$key = md5($request->getURI() . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$cacheLog = $authorization->skip(fn () => $dbForProject->getDocument('cache', $key));
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
);
@ -434,17 +430,17 @@ Http::init()
if ($type === 'bucket') {
$bucketId = $parts[1] ?? null;
$bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser($auth->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles());
$isAPIKey = Auth::isAppUser($authorization->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$valid = $auth->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
$valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
if (!$fileSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
@ -455,7 +451,7 @@ Http::init()
if ($fileSecurity && !$valid) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
} else {
$file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
$file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
}
if ($file->isEmpty()) {
@ -529,7 +525,7 @@ Http::shutdown()
Http::shutdown()
->groups(['api'])
->inject('utopia')
->inject('route')
->inject('request')
->inject('response')
->inject('project')
@ -545,9 +541,9 @@ Http::shutdown()
->inject('queueForFunctions')
->inject('mode')
->inject('dbForConsole')
->inject('auth')
->inject('authorization')
->action(function (
Http $utopia,
Route $route,
Request $request,
Response $response,
Document $project,
@ -563,10 +559,10 @@ Http::shutdown()
Func $queueForFunctions,
string $mode,
Database $dbForConsole,
Authorization $auth,
Authorization $authorization,
) use ($parseLabel) {
if (!empty($user) && !$user->isEmpty() && empty($user->getInternalId())) {
$user = $auth->skip(fn () => $dbForProject->getDocument('users', $user->getId()));
$user = $authorization->skip(fn () => $dbForProject->getDocument('users', $user->getId()));
}
$responsePayload = $response->getPayload();
@ -626,7 +622,6 @@ Http::shutdown()
}
}
$route = $utopia->getRoute();
$requestParams = $route->getParamsValues();
/**
@ -696,11 +691,11 @@ Http::shutdown()
$key = md5($request->getURI() . '*' . implode('*', $request->getParams())) . '*' . APP_CACHE_BUSTER;
$signature = md5($data['payload']);
$cacheLog = $auth->skip(fn () => $dbForProject->getDocument('cache', $key));
$cacheLog = $authorization->skip(fn () => $dbForProject->getDocument('cache', $key));
$accessedAt = $cacheLog->getAttribute('accessedAt', '');
$now = DateTime::now();
if ($cacheLog->isEmpty()) {
$auth->skip(fn () => $dbForProject->createDocument('cache', new Document([
$authorization->skip(fn () => $dbForProject->createDocument('cache', new Document([
'$id' => $key,
'resource' => $resource,
'resourceType' => $resourceType,
@ -710,7 +705,7 @@ Http::shutdown()
])));
} elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) {
$cacheLog->setAttribute('accessedAt', $now);
$auth->skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog));
$authorization->skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog));
}
if ($signature !== $cacheLog->getAttribute('signature')) {

View file

@ -8,6 +8,7 @@ use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Http\Http;
use Utopia\Http\Route;
use Utopia\System\System;
Http::init()
@ -31,12 +32,12 @@ Http::init()
Http::init()
->groups(['auth'])
->inject('utopia')
->inject('route')
->inject('request')
->inject('project')
->inject('geodb')
->inject('auth')
->action(function (Http $utopia, Request $request, Document $project, Reader $geodb, Authorization $auth) {
->inject('authorization')
->action(function (Route $route, Request $request, Document $project, Reader $geodb, Authorization $authorization) {
$denylist = System::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', '');
if (!empty($denylist && $project->getId() === 'console')) {
$countries = explode(',', $denylist);
@ -47,10 +48,8 @@ Http::init()
}
}
$route = $utopia->match($request);
$isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles());
$isAppUser = Auth::isAppUser($auth->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
$isAppUser = Auth::isAppUser($authorization->getRoles());
if ($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs
return;

View file

@ -78,39 +78,12 @@ use Utopia\System\System;
use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub;
use Utopia\Cache\Adapter\None;
Http::setMode(System::getEnv('_APP_ENV', Http::MODE_TYPE_PRODUCTION));
function getAdapter($type, $scheme, $resource) {
switch ($type) {
case 'database':
$adapter = match ($scheme) {
'mariadb' => new MariaDB($resource),
'mysql' => new MySQL($resource),
default => null
};
$adapter->setDatabase($scheme);
break;
case 'pubsub':
$adapter = $resource();
break;
case 'queue':
$adapter = match ($scheme) {
//'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()),
default => null
};
break;
case 'cache':
$adapter = match ($scheme) {
'redis' => new RedisCache($resource),
default => null
};
break;
default:
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation.");
}
return $adapter;
if (!Http::isProduction()) {
// Allow specific domains to skip public domain validation in dev environment
// Useful for existing tests involving webhooks
PublicDomain::allow(['request-catcher']);
}
$global = new Registry();
@ -140,6 +113,10 @@ $global->set('geodb', function () {
return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-02.mmdb');
});
$global->set('hooks', function () {
return new Hooks();
});
$global->set('pools', (function () {
$fallbackForDB = 'db_main=' . URL::unparse([
'scheme' => 'mariadb',
@ -199,9 +176,12 @@ $global->set('pools', (function () {
$multipe = $connection['multiple'] ?? false;
$schemes = $connection['schemes'] ?? [];
$dsns = explode(',', $connection['dsns'] ?? '');
$config = [];
foreach ($dsns as &$dsn) {
$dsn = explode('=', $dsn);
$name = ($multipe) ? $dsn[0] : 'main';
$config[] = $name;
$dsn = $dsn[1] ?? '';
if (empty($dsn)) {
@ -267,6 +247,8 @@ $global->set('pools', (function () {
'dsn' => $dsn,
];
}
Config::setParam('pools-' . $key, $config);
}
return function () use ($pools): array {
@ -401,6 +383,34 @@ $user
});
$container->set($user);
$session = new Dependency();
$session
->setName('session')
->inject('user')
->inject('project')
->setCallback(function (Document $user, Document $project) {
if ($user->isEmpty()) {
return;
}
$sessions = $user->getAttribute('sessions', []);
$authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret, $authDuration);
if (!$sessionId) {
return;
}
foreach ($sessions as $session) {
if ($sessionId === $session->getId()) {
return $session;
}
}
return;
});
$container->set($session);
$console = new Dependency();
$console
->setName('console')
@ -498,6 +508,8 @@ $dbForProject
'mysql' => new MySQL($connection),
default => null
};
$adapter->setDatabase($dsn->getPath());
$database = new Database($adapter, $cache);
$database->setAuthorization($authorization);
@ -531,7 +543,7 @@ $dbForConsole
default => null
};
$adapter->setDatabase('appwrite');
$adapter->setDatabase($dsn->getPath());
$database = new Database($adapter, $cache);
$database->setAuthorization($authorization);
@ -788,6 +800,21 @@ $clients
});
$container->set($clients);
$servers = new Dependency();
$servers
->setName('servers')
->setCallback(function () {
$platforms = Config::getParam('platforms');
$server = $platforms[APP_PLATFORM_SERVER];
$languages = array_map(function ($language) {
return strtolower($language['name']);
}, $server['sdks']);
return $languages;
});
$container->set($servers);
$geodb = new Dependency();
$geodb
->setName('geodb')
@ -795,4 +822,44 @@ $geodb
->setCallback(function (Registry $register) {
return $register->get('geodb');
});
$container->set($geodb);
$container->set($geodb);
$passwordsDictionary = new Dependency();
$passwordsDictionary
->setName('passwordsDictionary')
->setCallback(function () {
$content = file_get_contents(__DIR__ . '/assets/security/10k-common-passwords');
$content = explode("\n", $content);
$content = array_flip($content);
return $content;
});
$container->set($passwordsDictionary);
$hooks = new Dependency();
$hooks
->setName('hooks')
->inject('registry')
->setCallback(function (Registry $registry) {
return $registry->get('hooks');
});
$container->set($hooks);
$requestTimestamp = new Dependency();
$requestTimestamp
->setName('requestTimestamp')
->inject('request')
->setCallback(function ($request) {
$timestampHeader = $request->getHeader('x-appwrite-timestamp');
$requestTimestamp = null;
if (!empty($timestampHeader)) {
try {
$requestTimestamp = new \DateTime($timestampHeader);
} catch (\Throwable $e) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid X-Appwrite-Timestamp header value');
}
}
return $requestTimestamp;
});
$container->set($requestTimestamp);

8
composer.lock generated
View file

@ -1825,12 +1825,12 @@
"source": {
"type": "git",
"url": "https://github.com/utopia-php/http.git",
"reference": "aef4e9a7bcb3ba21b993f2daad0364657c2fe1aa"
"reference": "d600ae234780e083c600ab62a8348b7ea506cd1d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/http/zipball/aef4e9a7bcb3ba21b993f2daad0364657c2fe1aa",
"reference": "aef4e9a7bcb3ba21b993f2daad0364657c2fe1aa",
"url": "https://api.github.com/repos/utopia-php/http/zipball/d600ae234780e083c600ab62a8348b7ea506cd1d",
"reference": "d600ae234780e083c600ab62a8348b7ea506cd1d",
"shasum": ""
},
"require": {
@ -1867,7 +1867,7 @@
"issues": "https://github.com/utopia-php/http/issues",
"source": "https://github.com/utopia-php/http/tree/feat-di-upgrade"
},
"time": "2024-04-13T17:20:36+00:00"
"time": "2024-04-14T16:41:37+00:00"
},
{
"name": "utopia-php/image",

View file

@ -2,41 +2,20 @@
namespace Appwrite\Utopia\Queue;
use Utopia\Pools\Connection;
class Connections
{
/**
* @var Connection[]
* @var array
*/
protected array $connections = [];
/**
* @param Connection $pool
* @param mixed $connection
* @return self
*/
public function add(Connection $connection): self
public function add(mixed $connection, $pool): self
{
$this->connections[$connection->getID()] = $connection;
return $this;
}
/**
* @param string $id
* @return Connection
*/
public function get(string $id): Connection
{
return $this->connections[$id] ?? throw new \Exception("Connection '{$id}' not found");
}
/**
* @param string $id
* @return self
*/
public function remove(string $id): self
{
unset($this->connections[$id]);
$this->connections[] = ['connection' => $connection, 'pool' => $pool];
return $this;
}
@ -45,8 +24,11 @@ class Connections
*/
public function reclaim(): self
{
foreach ($this->connections as $connection) {
$connection->reclaim();
foreach ($this->connections as $id => $resource) {
$pool = $resource['pool'];
$connection = $resource['connection'];
$pool->put($connection);
unset($this->connections[$id]);
}
return $this;