diff --git a/.env b/.env index 30bacb2a8..1c260bc3f 100644 --- a/.env +++ b/.env @@ -1,5 +1,6 @@ _APP_ENV=production _APP_ENV=development +_APP_LOCALE=en _APP_SYSTEM_EMAIL_NAME=Appwrite _APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io diff --git a/CHANGES.md b/CHANGES.md index 7593857d6..5b93da5ac 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,11 @@ - Added Anonymous Login ([RFC-010](https://github.com/appwrite/rfc/blob/main/010-anonymous-login.md), #914) - Added new Environment Variable to enable or disable Anonymous Login - Added events for functions and executions (#971) +- Splited token & session models to become 2 different internal entities (#922) +- Added Dart 2.12 as a new Cloud Functions runtime (#989) +- ClamAV is now disabled by default to allow lower min requirments for Appwrite (#1064) +- Added a new env var named `_APP_LOCALE` that allow to change the default `en` locale value (#1056) +- Updated all the console bottom control to be consistent. Dropped the `+` icon (#1062) ## Bugs @@ -14,6 +19,7 @@ - Only logged in users can execute functions (for guests, use anonymous login) - Only the user who has triggered the execution get access to the relevant execution logs +- Function execution env `APPWRITE_FUNCTION_EVENT_PAYLOAD` renamed to `APPWRITE_FUNCTION_EVENT_DATA` # Version 0.7.2 diff --git a/Dockerfile b/Dockerfile index bc3c4db04..1684b00a2 100755 --- a/Dockerfile +++ b/Dockerfile @@ -69,6 +69,7 @@ ARG VERSION=dev ENV _APP_SERVER=swoole \ _APP_ENV=production \ + _APP_LOCALE=en \ _APP_DOMAIN=localhost \ _APP_DOMAIN_TARGET=localhost \ _APP_HOME=https://appwrite.io \ diff --git a/app/config/collections.php b/app/config/collections.php index 3307e7a12..d023cae51 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -271,6 +271,16 @@ $collections = [ 'required' => true, 'array' => false, ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Sessions', + 'key' => 'sessions', + 'type' => Database::SYSTEM_VAR_TYPE_DOCUMENT, + 'default' => [], + 'required' => false, + 'array' => true, + 'list' => [Database::SYSTEM_COLLECTION_SESSIONS], + ], [ '$collection' => Database::SYSTEM_COLLECTION_RULES, 'label' => 'Tokens', @@ -293,11 +303,11 @@ $collections = [ ], ], ], - Database::SYSTEM_COLLECTION_TOKENS => [ + Database::SYSTEM_COLLECTION_SESSIONS => [ '$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS, - '$id' => Database::SYSTEM_COLLECTION_TOKENS, + '$id' => Database::SYSTEM_COLLECTION_SESSIONS, '$permissions' => ['read' => ['*']], - 'name' => 'Token', + 'name' => 'Session', 'structure' => true, 'rules' => [ [ @@ -306,16 +316,34 @@ $collections = [ 'key' => 'userId', 'type' => Database::SYSTEM_VAR_TYPE_TEXT, 'default' => null, + 'required' => true, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Provider', + 'key' => 'provider', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => true, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Provider User Identifier', + 'key' => 'providerUid', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', 'required' => false, 'array' => false, ], [ '$collection' => Database::SYSTEM_COLLECTION_RULES, - 'label' => 'Type', - 'key' => 'type', - 'type' => Database::SYSTEM_VAR_TYPE_NUMERIC, - 'default' => null, - 'required' => true, + 'label' => 'Provider Token', + 'key' => 'providerToken', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, 'array' => false, ], [ @@ -473,6 +501,69 @@ $collections = [ ], ], ], + Database::SYSTEM_COLLECTION_TOKENS => [ + '$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS, + '$id' => Database::SYSTEM_COLLECTION_TOKENS, + '$permissions' => ['read' => ['*']], + 'name' => 'Token', + 'structure' => true, + 'rules' => [ + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'User ID', + 'key' => 'userId', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => null, + 'required' => false, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Type', + 'key' => 'type', + 'type' => Database::SYSTEM_VAR_TYPE_NUMERIC, + 'default' => null, + 'required' => true, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Secret', + 'key' => 'secret', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => true, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Expire', + 'key' => 'expire', + 'type' => Database::SYSTEM_VAR_TYPE_NUMERIC, + 'default' => 0, + 'required' => true, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'User Agent', + 'key' => 'userAgent', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => true, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'IP', + 'key' => 'ip', + 'type' => Database::SYSTEM_VAR_TYPE_IP, + 'default' => '', + 'required' => true, + 'array' => false, + ], + ], + ], Database::SYSTEM_COLLECTION_MEMBERSHIPS => [ '$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS, '$id' => Database::SYSTEM_COLLECTION_MEMBERSHIPS, @@ -1617,26 +1708,6 @@ foreach ($providers as $index => $provider) { 'array' => false, 'filter' => ['encrypt'], ]; - - $collections[Database::SYSTEM_COLLECTION_USERS]['rules'][] = [ - '$collection' => Database::SYSTEM_COLLECTION_RULES, - 'label' => 'OAuth2 '.\ucfirst($index).' ID', - 'key' => 'oauth2'.\ucfirst($index), - 'type' => Database::SYSTEM_VAR_TYPE_TEXT, - 'default' => '', - 'required' => false, - 'array' => false, - ]; - - $collections[Database::SYSTEM_COLLECTION_USERS]['rules'][] = [ - '$collection' => Database::SYSTEM_COLLECTION_RULES, - 'label' => 'OAuth2 '.\ucfirst($index).' Access Token', - 'key' => 'oauth2'.\ucfirst($index).'AccessToken', - 'type' => Database::SYSTEM_VAR_TYPE_TEXT, - 'default' => '', - 'required' => false, - 'array' => false, - ]; } return $collections; \ No newline at end of file diff --git a/app/config/environments.php b/app/config/environments.php index 9f1c5638e..473da1b76 100644 --- a/app/config/environments.php +++ b/app/config/environments.php @@ -70,6 +70,15 @@ $environments = [ 'logo' => 'python.png', 'supports' => [System::X86, System::PPC, System::ARM], ], + 'python-3.9' => [ + 'name' => 'Python', + 'version' => '3.9', + 'base' => 'python:3.9-alpine', + 'image' => 'appwrite/env-python-3.9:1.0.0', + 'build' => '/usr/src/code/docker/environments/python-3.9', + 'logo' => 'python.png', + 'supports' => [System::X86, System::PPC, System::ARM], + ], 'deno-1.2' => [ 'name' => 'Deno', 'version' => '1.2', @@ -106,6 +115,15 @@ $environments = [ 'logo' => 'dart.png', 'supports' => [System::X86], ], + 'dart-2.12' => [ + 'name' => 'Dart', + 'version' => '2.12', + 'base' => 'google/dart:2.12', + 'image' => 'appwrite/env-dart-2.12:1.0.0', + 'build' => '/usr/src/code/docker/environments/dart-2.12', + 'logo' => 'dart.png', + 'supports' => [System::X86], + ], 'dotnet-3.1' => [ 'name' => '.NET', 'version' => '3.1', diff --git a/app/config/variables.php b/app/config/variables.php index 8aa226b8b..24df1cc8d 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -15,6 +15,14 @@ return [ 'required' => false, 'question' => '', ], + [ + 'name' => '_APP_LOCALE', + 'description' => 'Set your Appwrite\'s locale. By default, the locale is set to \'en\'.', + 'introduction' => '', + 'default' => 'en', + 'required' => false, + 'question' => '', + ], [ 'name' => '_APP_OPTIONS_ABUSE', 'description' => 'Allows you to disable abuse checks and API rate limiting. By default, set to \'enabled\'. To cancel the abuse checking, set to \'disabled\'. It is not recommended to disable this check-in a production environment.', @@ -309,9 +317,9 @@ return [ ], [ 'name' => '_APP_STORAGE_ANTIVIRUS', - 'description' => 'This variable allows you to disable the internal anti-virus scans. This value is set to \'enabled\' by default, to cancel the scans set the value to \'disabled\'. When disabled, it\'s recommended to turn off the ClamAV container for better resource usage.', + 'description' => 'This variable allows you to disable the internal anti-virus scans. This value is set to \'disabled\' by default, to enable the scans set the value to \'enabled\'. Before enabling, you must add the ClamAV service and depend on it on main Appwrite service.', 'introduction' => '', - 'default' => 'enabled', + 'default' => 'disabled', 'required' => false, 'question' => '', ], @@ -381,7 +389,7 @@ return [ 'name' => '_APP_FUNCTIONS_ENVS', 'description' => 'This option allows you to limit the available environments for cloud functions. This option is very useful for low-cost servers to safe disk space.\n\nTo enable/activate this option, pass a list of allowed environments separated by a comma.\n\nCurrently, supported environments are: ' . \implode(', ', \array_keys(Config::getParam('providers'))), 'introduction' => '0.7.0', - 'default' => 'node-14.5,deno-1.6,php-7.4,python-3.8,ruby-3.0,dotnet-5.0', + 'default' => 'node-14.5,deno-1.6,php-7.4,python-3.9,ruby-3.0,dotnet-5.0', 'required' => false, 'question' => '', ], diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 067fe5876..414432ccd 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -6,10 +6,10 @@ use Utopia\Exception; use Utopia\Config\Config; use Utopia\Validator\Assoc; use Utopia\Validator\Text; -use Utopia\Validator\Email; +use Appwrite\Network\Validator\Email; use Utopia\Validator\WhiteList; -use Utopia\Validator\Host; -use Utopia\Validator\URL; +use Appwrite\Network\Validator\Host; +use Appwrite\Network\Validator\URL; use Utopia\Audit\Audit; use Utopia\Audit\Adapters\MySQL as AuditAdapter; use Appwrite\Auth\Auth; @@ -190,10 +190,11 @@ App::post('/v1/account/sessions') $secret = Auth::tokenGenerator(); $session = new Document(array_merge( [ - '$collection' => Database::SYSTEM_COLLECTION_TOKENS, + '$collection' => Database::SYSTEM_COLLECTION_SESSIONS, '$permissions' => ['read' => ['user:'.$profile->getId()], 'write' => ['user:'.$profile->getId()]], 'userId' => $profile->getId(), - 'type' => Auth::TOKEN_TYPE_LOGIN, + 'provider' => Auth::SESSION_PROVIDER_EMAIL, + 'providerUid' => $email, 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'expire' => $expiry, 'userAgent' => $request->getUserAgent('UNKNOWN'), @@ -210,7 +211,7 @@ App::post('/v1/account/sessions') throw new Exception('Failed saving session to DB', 500); } - $profile->setAttribute('tokens', $session, Document::SET_TYPE_APPEND); + $profile->setAttribute('sessions', $session, Document::SET_TYPE_APPEND); $profile = $projectDB->updateDocument($profile->getArrayCopy()); @@ -441,7 +442,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') throw new Exception('Missing ID from OAuth2 provider', 400); } - $current = Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_LOGIN, Auth::$secret); + $current = Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret); if ($current) { $projectDB->deleteDocument($current); //throw new Exception('User already logged in', 401); @@ -451,7 +452,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'limit' => 1, 'filters' => [ '$collection='.Database::SYSTEM_COLLECTION_USERS, - 'oauth2'.\ucfirst($provider).'='.$oauth2ID, + 'sessions.provider='.$provider, + 'sessions.providerUid='.$oauth2ID ], ]) : $user; @@ -506,10 +508,12 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $secret = Auth::tokenGenerator(); $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG; $session = new Document(array_merge([ - '$collection' => Database::SYSTEM_COLLECTION_TOKENS, + '$collection' => Database::SYSTEM_COLLECTION_SESSIONS, '$permissions' => ['read' => ['user:'.$user['$id']], 'write' => ['user:'.$user['$id']]], 'userId' => $user->getId(), - 'type' => Auth::TOKEN_TYPE_LOGIN, + 'provider' => $provider, + 'providerUid' => $oauth2ID, + 'providerToken' => $accessToken, 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'expire' => $expiry, 'userAgent' => $request->getUserAgent('UNKNOWN'), @@ -527,10 +531,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') } $user - ->setAttribute('oauth2'.\ucfirst($provider), $oauth2ID) - ->setAttribute('oauth2'.\ucfirst($provider).'AccessToken', $accessToken) ->setAttribute('status', Auth::USER_STATUS_ACTIVATED) - ->setAttribute('tokens', $session, Document::SET_TYPE_APPEND) + ->setAttribute('sessions', $session, Document::SET_TYPE_APPEND) ; Authorization::setRole('user:'.$user->getId()); @@ -648,10 +650,10 @@ App::post('/v1/account/sessions/anonymous') $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG; $session = new Document(array_merge( [ - '$collection' => Database::SYSTEM_COLLECTION_TOKENS, + '$collection' => Database::SYSTEM_COLLECTION_SESSIONS, '$permissions' => ['read' => ['user:' . $user['$id']], 'write' => ['user:' . $user['$id']]], 'userId' => $user->getId(), - 'type' => Auth::TOKEN_TYPE_LOGIN, + 'provider' => Auth::SESSION_PROVIDER_ANONYMOUS, 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'expire' => $expiry, 'userAgent' => $request->getUserAgent('UNKNOWN'), @@ -663,7 +665,7 @@ App::post('/v1/account/sessions/anonymous') $detector->getDevice() )); - $user->setAttribute('tokens', $session, Document::SET_TYPE_APPEND); + $user->setAttribute('sessions', $session, Document::SET_TYPE_APPEND); Authorization::setRole('user:'.$user->getId()); @@ -716,16 +718,18 @@ App::post('/v1/account/jwt') /** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Database\Document $user */ - $tokens = $user->getAttribute('tokens', []); - $session = new Document(); + $sessions = $user->getAttribute('sessions', []); + $current = new Document(); - foreach ($tokens as $token) { /** @var Appwrite\Database\Document $token */ - if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too - $session = $token; + foreach ($sessions as $session) { + /** @var Appwrite\Database\Document $session */ + + if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too + $current = $session; } } - if($session->isEmpty()) { + if($current->isEmpty()) { throw new Exception('No valid session found', 401); } @@ -739,7 +743,7 @@ App::post('/v1/account/jwt') // 'scopes' => ['user'], // 'iss' => 'http://api.mysite.com', 'userId' => $user->getId(), - 'sessionId' => $session->getId(), + 'sessionId' => $current->getId(), ])]), Response::MODEL_JWT); }); @@ -804,22 +808,19 @@ App::get('/v1/account/sessions') /** @var Appwrite\Database\Document $user */ /** @var Utopia\Locale\Locale $locale */ - $tokens = $user->getAttribute('tokens', []); - $sessions = []; + $sessions = $user->getAttribute('sessions', []); $countries = $locale->getText('countries'); - $current = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_LOGIN, Auth::$secret); + $current = Auth::sessionVerify($sessions, Auth::$secret); - foreach ($tokens as $token) { /* @var $token Document */ - if (Auth::TOKEN_TYPE_LOGIN != $token->getAttribute('type')) { - continue; - } + foreach ($sessions as $key => $session) { + /** @var Document $session */ - $token->setAttribute('countryName', (isset($countries[strtoupper($token->getAttribute('countryCode'))])) - ? $countries[strtoupper($token->getAttribute('countryCode'))] + $session->setAttribute('countryName', (isset($countries[strtoupper($session->getAttribute('countryCode'))])) + ? $countries[strtoupper($session->getAttribute('countryCode'))] : $locale->getText('locale.country.unknown')); - $token->setAttribute('current', ($current == $token->getId()) ? true : false); + $session->setAttribute('current', ($current == $session->getId()) ? true : false); - $sessions[] = $token; + $sessions[$key] = $session; } $response->dynamic(new Document([ @@ -1147,7 +1148,7 @@ App::delete('/v1/account') ; $events - ->setParam('payload', $response->output($user, Response::MODEL_USER)) + ->setParam('eventData', $response->output($user, Response::MODEL_USER)) ; if (!Config::getParam('domainVerification')) { @@ -1192,14 +1193,16 @@ App::delete('/v1/account/sessions/:sessionId') $protocol = $request->getProtocol(); $sessionId = ($sessionId === 'current') - ? Auth::tokenVerify($user->getAttribute('tokens'), Auth::TOKEN_TYPE_LOGIN, Auth::$secret) + ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) : $sessionId; - $tokens = $user->getAttribute('tokens', []); + $sessions = $user->getAttribute('sessions', []); - foreach ($tokens as $token) { /* @var $token Document */ - if (($sessionId == $token->getId()) && Auth::TOKEN_TYPE_LOGIN == $token->getAttribute('type')) { - if (!$projectDB->deleteDocument($token->getId())) { + foreach ($sessions as $session) { + /** @var Document $session */ + + if (($sessionId == $session->getId())) { + if (!$projectDB->deleteDocument($session->getId())) { throw new Exception('Failed to remove token from DB', 500); } @@ -1215,10 +1218,10 @@ App::delete('/v1/account/sessions/:sessionId') ; } - $token->setAttribute('current', false); + $session->setAttribute('current', false); - if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too - $token->setAttribute('current', true); + if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too + $session->setAttribute('current', true); $response ->addCookie(Auth::$cookieName.'_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) @@ -1227,7 +1230,7 @@ App::delete('/v1/account/sessions/:sessionId') } $events - ->setParam('payload', $response->output($token, Response::MODEL_SESSION)) + ->setParam('eventData', $response->output($session, Response::MODEL_SESSION)) ; return $response->noContent(); @@ -1264,10 +1267,12 @@ App::delete('/v1/account/sessions') /** @var Appwrite\Event\Event $events */ $protocol = $request->getProtocol(); - $tokens = $user->getAttribute('tokens', []); + $sessions = $user->getAttribute('sessions', []); - foreach ($tokens as $token) { /* @var $token Document */ - if (!$projectDB->deleteDocument($token->getId())) { + foreach ($sessions as $session) { + /** @var Document $session */ + + if (!$projectDB->deleteDocument($session->getId())) { throw new Exception('Failed to remove token from DB', 500); } @@ -1283,10 +1288,10 @@ App::delete('/v1/account/sessions') ; } - $token->setAttribute('current', false); + $session->setAttribute('current', false); - if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too - $token->setAttribute('current', true); + if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too + $session->setAttribute('current', true); $response ->addCookie(Auth::$cookieName.'_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) @@ -1295,9 +1300,9 @@ App::delete('/v1/account/sessions') } $events - ->setParam('payload', $response->output(new Document([ - 'sum' => count($tokens), - 'sessions' => $tokens + ->setParam('eventData', $response->output(new Document([ + 'sum' => count($sessions), + 'sessions' => $sessions ]), Response::MODEL_SESSION_LIST)) ; @@ -1420,7 +1425,7 @@ App::post('/v1/account/recovery') ; $events - ->setParam('payload', + ->setParam('eventData', $response->output($recovery->setAttribute('secret', $secret), Response::MODEL_TOKEN )) @@ -1623,7 +1628,7 @@ App::post('/v1/account/verification') ; $events - ->setParam('payload', + ->setParam('eventData', $response->output($verification->setAttribute('secret', $verificationSecret), Response::MODEL_TOKEN )) diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index a094dc672..b5c47c198 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -14,7 +14,7 @@ use Utopia\Validator\Boolean; use Utopia\Validator\HexColor; use Utopia\Validator\Range; use Utopia\Validator\Text; -use Utopia\Validator\URL; +use Appwrite\Network\Validator\URL; use Utopia\Validator\WhiteList; $avatarCallback = function ($type, $code, $width, $height, $quality, $response) { diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 2c32ae981..e86812e7e 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -271,7 +271,7 @@ App::delete('/v1/database/collections/:collectionId') ; $events - ->setParam('payload', $response->output($collection, Response::MODEL_COLLECTION)) + ->setParam('eventData', $response->output($collection, Response::MODEL_COLLECTION)) ; $audits @@ -614,7 +614,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId') } $events - ->setParam('payload', $response->output($document, Response::MODEL_ANY)) + ->setParam('eventData', $response->output($document, Response::MODEL_ANY)) ; $audits diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 63c99ffae..573771461 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -6,7 +6,7 @@ use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; -use Utopia\Validator\URL; +use Appwrite\Network\Validator\URL; use Utopia\Validator\Range; use Utopia\Config\Config; use Utopia\Domains\Domain; diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index f0ce2e405..50570cb50 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -581,7 +581,7 @@ App::delete('/v1/storage/files/:fileId') ; $events - ->setParam('payload', $response->output($file, Response::MODEL_FILE)) + ->setParam('eventData', $response->output($file, Response::MODEL_FILE)) ; $response->noContent(); diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index c4a9e4875..e6ee69cb1 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -3,9 +3,9 @@ use Utopia\App; use Utopia\Exception; use Utopia\Config\Config; -use Utopia\Validator\Email; +use Appwrite\Network\Validator\Email; use Utopia\Validator\Text; -use Utopia\Validator\Host; +use Appwrite\Network\Validator\Host; use Utopia\Validator\Range; use Utopia\Validator\ArrayList; use Utopia\Validator\WhiteList; @@ -243,7 +243,7 @@ App::delete('/v1/teams/:teamId') } $events - ->setParam('payload', $response->output($team, Response::MODEL_TEAM)) + ->setParam('eventData', $response->output($team, Response::MODEL_TEAM)) ; $response->noContent(); @@ -327,6 +327,7 @@ App::post('/v1/teams/:teamId/memberships') 'registration' => \time(), 'reset' => false, 'name' => $name, + 'sessions' => [], 'tokens' => [], ], ['email' => $email]); } catch (Duplicate $th) { @@ -595,10 +596,11 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status') $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG; $secret = Auth::tokenGenerator(); $session = new Document(array_merge([ - '$collection' => Database::SYSTEM_COLLECTION_TOKENS, + '$collection' => Database::SYSTEM_COLLECTION_SESSIONS, '$permissions' => ['read' => ['user:'.$user->getId()], 'write' => ['user:'.$user->getId()]], 'userId' => $user->getId(), - 'type' => Auth::TOKEN_TYPE_LOGIN, + 'provider' => Auth::SESSION_PROVIDER_EMAIL, + 'providerUid' => $user->getAttribute('email'), 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'expire' => $expiry, 'userAgent' => $request->getUserAgent('UNKNOWN'), @@ -606,7 +608,7 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status') 'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--', ], $detector->getOS(), $detector->getClient(), $detector->getDevice())); - $user->setAttribute('tokens', $session, Document::SET_TYPE_APPEND); + $user->setAttribute('sessions', $session, Document::SET_TYPE_APPEND); Authorization::setRole('user:'.$userId); @@ -711,7 +713,7 @@ App::delete('/v1/teams/:teamId/memberships/:inviteId') ; $events - ->setParam('payload', $response->output($membership, Response::MODEL_MEMBERSHIP)) + ->setParam('eventData', $response->output($membership, Response::MODEL_MEMBERSHIP)) ; $response->noContent(); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 958dfc148..44b64bd94 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -4,7 +4,7 @@ use Utopia\App; use Utopia\Exception; use Utopia\Validator\Assoc; use Utopia\Validator\WhiteList; -use Utopia\Validator\Email; +use Appwrite\Network\Validator\Email; use Utopia\Validator\Text; use Utopia\Validator\Range; use Utopia\Audit\Audit; @@ -196,21 +196,18 @@ App::get('/v1/users/:userId/sessions') throw new Exception('User not found', 404); } - $tokens = $user->getAttribute('tokens', []); - $sessions = []; + $sessions = $user->getAttribute('sessions', []); $countries = $locale->getText('countries'); - foreach ($tokens as $token) { /* @var $token Document */ - if (Auth::TOKEN_TYPE_LOGIN != $token->getAttribute('type')) { - continue; - } + foreach ($sessions as $key => $session) { + /** @var Document $session */ - $token->setAttribute('countryName', (isset($countries[strtoupper($token->getAttribute('countryCode'))])) - ? $countries[strtoupper($token->getAttribute('countryCode'))] + $session->setAttribute('countryName', (isset($countries[strtoupper($session->getAttribute('countryCode'))])) + ? $countries[strtoupper($session->getAttribute('countryCode'))] : $locale->getText('locale.country.unknown')); - $token->setAttribute('current', false); + $session->setAttribute('current', false); - $sessions[] = $token; + $sessions[$key] = $session; } $response->dynamic(new Document([ @@ -434,16 +431,18 @@ App::delete('/v1/users/:userId/sessions/:sessionId') throw new Exception('User not found', 404); } - $tokens = $user->getAttribute('tokens', []); + $sessions = $user->getAttribute('sessions', []); - foreach ($tokens as $token) { /* @var $token Document */ - if ($sessionId == $token->getId()) { - if (!$projectDB->deleteDocument($token->getId())) { + foreach ($sessions as $session) { + /** @var Document $session */ + + if ($sessionId == $session->getId()) { + if (!$projectDB->deleteDocument($session->getId())) { throw new Exception('Failed to remove token from DB', 500); } $events - ->setParam('payload', $response->output($user, Response::MODEL_USER)) + ->setParam('eventData', $response->output($user, Response::MODEL_USER)) ; } } @@ -478,16 +477,18 @@ App::delete('/v1/users/:userId/sessions') throw new Exception('User not found', 404); } - $tokens = $user->getAttribute('tokens', []); + $sessions = $user->getAttribute('sessions', []); - foreach ($tokens as $token) { /* @var $token Document */ - if (!$projectDB->deleteDocument($token->getId())) { + foreach ($sessions as $session) { + /** @var Document $session */ + + if (!$projectDB->deleteDocument($session->getId())) { throw new Exception('Failed to remove token from DB', 500); } } $events - ->setParam('payload', $response->output($user, Response::MODEL_USER)) + ->setParam('eventData', $response->output($user, Response::MODEL_USER)) ; // TODO : Response filter implementation @@ -547,7 +548,7 @@ App::delete('/v1/users/:userId') ; $events - ->setParam('payload', $response->output($user, Response::MODEL_USER)) + ->setParam('eventData', $response->output($user, Response::MODEL_USER)) ; // TODO : Response filter implementation diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index f165b4b34..88cd339df 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -78,7 +78,7 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e ->setParam('projectId', $project->getId()) ->setParam('userId', $user->getId()) ->setParam('event', $route->getLabel('event', '')) - ->setParam('payload', []) + ->setParam('eventData', []) ->setParam('functionId', null) ->setParam('executionId', null) ->setParam('trigger', 'event') @@ -124,8 +124,8 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits /** @var bool $mode */ if (!empty($events->getParam('event'))) { - if(empty($events->getParam('payload'))) { - $events->setParam('payload', $response->getPayload()); + if(empty($events->getParam('eventData'))) { + $events->setParam('eventData', $response->getPayload()); } $webhooks = clone $events; diff --git a/app/init.php b/app/init.php index 2620abe7e..57e540572 100644 --- a/app/init.php +++ b/app/init.php @@ -308,7 +308,7 @@ App::setResource('layout', function($locale) { }, ['locale']); App::setResource('locale', function() { - return new Locale('en'); + return new Locale(App::getEnv('_APP_LOCALE', 'en')); }); // Queues @@ -418,7 +418,7 @@ App::setResource('user', function($mode, $project, $console, $request, $response if (empty($user->getId()) // Check a document has been found in the DB || Database::SYSTEM_COLLECTION_USERS !== $user->getCollection() // Validate returned document is really a user document - || !Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_LOGIN, Auth::$secret)) { // Validate user has valid login token + || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret)) { // Validate user has valid login token $user = new Document(['$id' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]); } diff --git a/app/views/console/database/collection.phtml b/app/views/console/database/collection.phtml index 45d3287cf..f10e97e1f 100644 --- a/app/views/console/database/collection.phtml +++ b/app/views/console/database/collection.phtml @@ -36,9 +36,6 @@ $maxCells = 10;
-
+
+ + + Add Document +
  • diff --git a/app/views/console/database/index.phtml b/app/views/console/database/index.phtml index 8ec46a13a..2760a1ac1 100644 --- a/app/views/console/database/index.phtml +++ b/app/views/console/database/index.phtml @@ -12,40 +12,6 @@
  • Collections

    - -
    -
    +
    + + +