From c21402733faa22daaf2bc45a7793e16ea8b7b00d Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 10 Jul 2024 07:16:45 +0000 Subject: [PATCH 001/110] development keys module setup --- app/controllers/api/projects.php | 201 ++++++++++++++++++ app/controllers/general.php | 8 + src/Appwrite/Platform/Appwrite.php | 2 + .../Platform/Modules/DevelopmentKeys.php | 14 ++ .../Modules/DevelopmentKeys/Http/Create.php | 80 +++++++ .../Modules/DevelopmentKeys/Services/Http.php | 15 ++ 6 files changed, 320 insertions(+) create mode 100644 src/Appwrite/Platform/Modules/DevelopmentKeys.php create mode 100644 src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php create mode 100644 src/Appwrite/Platform/Modules/DevelopmentKeys/Services/Http.php diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 265efbbfae..058fc7c569 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -1383,6 +1383,207 @@ App::delete('/v1/projects/:projectId/keys/:keyId') $response->noContent(); }); + +// Development keys + +App::post('/v1/projects/:projectId/development-keys') + ->desc('Create key') + ->groups(['api', 'projects']) + ->label('scope', 'projects.write') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'projects') + ->label('sdk.method', 'createKey') + ->label('sdk.response.code', Response::STATUS_CODE_CREATED) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_KEY) + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') + ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true) + ->inject('response') + ->inject('dbForConsole') + ->action(function (string $projectId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) { + + $project = $dbForConsole->getDocument('projects', $projectId); + + if ($project->isEmpty()) { + throw new Exception(Exception::PROJECT_NOT_FOUND); + } + + $key = new Document([ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'projectInternalId' => $project->getInternalId(), + 'projectId' => $project->getId(), + 'name' => $name, + 'scopes' => $scopes, + 'expire' => $expire, + 'sdks' => [], + 'accessedAt' => null, + 'secret' => API_KEY_STANDARD . '_' . \bin2hex(\random_bytes(128)), + ]); + + $key = $dbForConsole->createDocument('keys', $key); + + $dbForConsole->purgeCachedDocument('projects', $project->getId()); + + $response + ->setStatusCode(Response::STATUS_CODE_CREATED) + ->dynamic($key, Response::MODEL_KEY); + }); + +App::get('/v1/projects/:projectId/keys') + ->desc('List keys') + ->groups(['api', 'projects']) + ->label('scope', 'projects.read') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'projects') + ->label('sdk.method', 'listKeys') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_KEY_LIST) + ->param('projectId', '', new UID(), 'Project unique ID.') + ->inject('response') + ->inject('dbForConsole') + ->action(function (string $projectId, Response $response, Database $dbForConsole) { + + $project = $dbForConsole->getDocument('projects', $projectId); + + if ($project->isEmpty()) { + throw new Exception(Exception::PROJECT_NOT_FOUND); + } + + $keys = $dbForConsole->find('keys', [ + Query::equal('projectInternalId', [$project->getInternalId()]), + Query::limit(5000), + ]); + + $response->dynamic(new Document([ + 'keys' => $keys, + 'total' => count($keys), + ]), Response::MODEL_KEY_LIST); + }); + +App::get('/v1/projects/:projectId/keys/:keyId') + ->desc('Get key') + ->groups(['api', 'projects']) + ->label('scope', 'projects.read') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'projects') + ->label('sdk.method', 'getKey') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_KEY) + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('keyId', '', new UID(), 'Key unique ID.') + ->inject('response') + ->inject('dbForConsole') + ->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) { + + $project = $dbForConsole->getDocument('projects', $projectId); + + if ($project->isEmpty()) { + throw new Exception(Exception::PROJECT_NOT_FOUND); + } + + $key = $dbForConsole->findOne('keys', [ + Query::equal('$id', [$keyId]), + Query::equal('projectInternalId', [$project->getInternalId()]), + ]); + + if ($key === false || $key->isEmpty()) { + throw new Exception(Exception::KEY_NOT_FOUND); + } + + $response->dynamic($key, Response::MODEL_KEY); + }); + +App::put('/v1/projects/:projectId/keys/:keyId') + ->desc('Update key') + ->groups(['api', 'projects']) + ->label('scope', 'projects.write') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'projects') + ->label('sdk.method', 'updateKey') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_KEY) + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('keyId', '', new UID(), 'Key unique ID.') + ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') + ->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.') + ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true) + ->inject('response') + ->inject('dbForConsole') + ->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) { + + $project = $dbForConsole->getDocument('projects', $projectId); + + if ($project->isEmpty()) { + throw new Exception(Exception::PROJECT_NOT_FOUND); + } + + $key = $dbForConsole->findOne('keys', [ + Query::equal('$id', [$keyId]), + Query::equal('projectInternalId', [$project->getInternalId()]), + ]); + + if ($key === false || $key->isEmpty()) { + throw new Exception(Exception::KEY_NOT_FOUND); + } + + $key + ->setAttribute('name', $name) + ->setAttribute('scopes', $scopes) + ->setAttribute('expire', $expire); + + $dbForConsole->updateDocument('keys', $key->getId(), $key); + + $dbForConsole->purgeCachedDocument('projects', $project->getId()); + + $response->dynamic($key, Response::MODEL_KEY); + }); + +App::delete('/v1/projects/:projectId/keys/:keyId') + ->desc('Delete key') + ->groups(['api', 'projects']) + ->label('scope', 'projects.write') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'projects') + ->label('sdk.method', 'deleteKey') + ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) + ->label('sdk.response.model', Response::MODEL_NONE) + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('keyId', '', new UID(), 'Key unique ID.') + ->inject('response') + ->inject('dbForConsole') + ->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) { + + $project = $dbForConsole->getDocument('projects', $projectId); + + if ($project->isEmpty()) { + throw new Exception(Exception::PROJECT_NOT_FOUND); + } + + $key = $dbForConsole->findOne('keys', [ + Query::equal('$id', [$keyId]), + Query::equal('projectInternalId', [$project->getInternalId()]), + ]); + + if ($key === false || $key->isEmpty()) { + throw new Exception(Exception::KEY_NOT_FOUND); + } + + $dbForConsole->deleteDocument('keys', $key->getId()); + + $dbForConsole->purgeCachedDocument('projects', $project->getId()); + + $response->noContent(); + }); + // JWT Keys App::post('/v1/projects/:projectId/jwts') diff --git a/app/controllers/general.php b/app/controllers/general.php index 10c9eb8e18..0ed7d9ff40 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -8,6 +8,7 @@ use Appwrite\Event\Event; use Appwrite\Event\Usage; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Network\Validator\Origin; +use Appwrite\Platform\Appwrite; use Appwrite\Utopia\Request; use Appwrite\Utopia\Request\Filters\V16 as RequestV16; use Appwrite\Utopia\Request\Filters\V17 as RequestV17; @@ -32,6 +33,7 @@ use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Logger\Log\User; use Utopia\Logger\Logger; +use Utopia\Platform\Service; use Utopia\System\System; use Utopia\Validator\Hostname; use Utopia\Validator\Text; @@ -992,3 +994,9 @@ App::wildcard() foreach (Config::getParam('services', []) as $service) { include_once $service['controller']; } + + +// Modules + +$platform = new Appwrite(); +$platform->init(Service::TYPE_HTTP); diff --git a/src/Appwrite/Platform/Appwrite.php b/src/Appwrite/Platform/Appwrite.php index 6b3eb077fa..e1a27f1b0a 100644 --- a/src/Appwrite/Platform/Appwrite.php +++ b/src/Appwrite/Platform/Appwrite.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform; use Appwrite\Platform\Modules\Core; +use Appwrite\Platform\Modules\DevelopmentKeys; use Utopia\Platform\Platform; class Appwrite extends Platform @@ -10,5 +11,6 @@ class Appwrite extends Platform public function __construct() { parent::__construct(new Core()); + $this->addModule(new DevelopmentKeys()); } } diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys.php b/src/Appwrite/Platform/Modules/DevelopmentKeys.php new file mode 100644 index 0000000000..c351ad92f1 --- /dev/null +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys.php @@ -0,0 +1,14 @@ +addService('http', new Http()); + } +} diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php new file mode 100644 index 0000000000..12c60546b4 --- /dev/null +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php @@ -0,0 +1,80 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/projects/:projectId/development-keys') + ->desc('Create key') + ->groups(['api', 'projects']) + ->label('scope', 'projects.write') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'projects') + ->label('sdk.method', 'createKey') + ->label('sdk.response.code', Response::STATUS_CODE_CREATED) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_KEY) + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') + ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true) + ->inject('response') + ->inject('dbForConsole') + ->callback(fn ($projectId, $name, $expire, $response, $dbForConsole) => $this->action($projectId, $name, $expire, $response, $dbForConsole)); + } + + public function action(string $projectId, string $name, ?string $expire, Response $response, Database $dbForConsole) + { + $project = $dbForConsole->getDocument('projects', $projectId); + + if ($project->isEmpty()) { + throw new Exception(Exception::PROJECT_NOT_FOUND); + } + + $key = new Document([ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'projectInternalId' => $project->getInternalId(), + 'projectId' => $project->getId(), + 'name' => $name, + 'expire' => $expire, + 'sdks' => [], + 'accessedAt' => null, + 'secret' => API_KEY_STANDARD . '_' . \bin2hex(\random_bytes(128)), + ]); + + $key = $dbForConsole->createDocument('development_keys', $key); + + $dbForConsole->purgeCachedDocument('projects', $project->getId()); + + $response + ->setStatusCode(Response::STATUS_CODE_CREATED) + ->dynamic($key, Response::MODEL_KEY); + } +} diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Services/Http.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Services/Http.php new file mode 100644 index 0000000000..f0a5e20605 --- /dev/null +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Services/Http.php @@ -0,0 +1,15 @@ +type = Service::TYPE_HTTP; + $this->addAction(Create::getName(), new Create()); + } +} From bcde2ea2233613515745b6ea7f37e43c1e26a24c Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 10 Jul 2024 07:32:57 +0000 Subject: [PATCH 002/110] remove duplicates --- app/controllers/api/projects.php | 201 ------------------------------- 1 file changed, 201 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 058fc7c569..265efbbfae 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -1383,207 +1383,6 @@ App::delete('/v1/projects/:projectId/keys/:keyId') $response->noContent(); }); - -// Development keys - -App::post('/v1/projects/:projectId/development-keys') - ->desc('Create key') - ->groups(['api', 'projects']) - ->label('scope', 'projects.write') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'createKey') - ->label('sdk.response.code', Response::STATUS_CODE_CREATED) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_KEY) - ->param('projectId', '', new UID(), 'Project unique ID.') - ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') - ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true) - ->inject('response') - ->inject('dbForConsole') - ->action(function (string $projectId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) { - - $project = $dbForConsole->getDocument('projects', $projectId); - - if ($project->isEmpty()) { - throw new Exception(Exception::PROJECT_NOT_FOUND); - } - - $key = new Document([ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'projectInternalId' => $project->getInternalId(), - 'projectId' => $project->getId(), - 'name' => $name, - 'scopes' => $scopes, - 'expire' => $expire, - 'sdks' => [], - 'accessedAt' => null, - 'secret' => API_KEY_STANDARD . '_' . \bin2hex(\random_bytes(128)), - ]); - - $key = $dbForConsole->createDocument('keys', $key); - - $dbForConsole->purgeCachedDocument('projects', $project->getId()); - - $response - ->setStatusCode(Response::STATUS_CODE_CREATED) - ->dynamic($key, Response::MODEL_KEY); - }); - -App::get('/v1/projects/:projectId/keys') - ->desc('List keys') - ->groups(['api', 'projects']) - ->label('scope', 'projects.read') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'listKeys') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_KEY_LIST) - ->param('projectId', '', new UID(), 'Project unique ID.') - ->inject('response') - ->inject('dbForConsole') - ->action(function (string $projectId, Response $response, Database $dbForConsole) { - - $project = $dbForConsole->getDocument('projects', $projectId); - - if ($project->isEmpty()) { - throw new Exception(Exception::PROJECT_NOT_FOUND); - } - - $keys = $dbForConsole->find('keys', [ - Query::equal('projectInternalId', [$project->getInternalId()]), - Query::limit(5000), - ]); - - $response->dynamic(new Document([ - 'keys' => $keys, - 'total' => count($keys), - ]), Response::MODEL_KEY_LIST); - }); - -App::get('/v1/projects/:projectId/keys/:keyId') - ->desc('Get key') - ->groups(['api', 'projects']) - ->label('scope', 'projects.read') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'getKey') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_KEY) - ->param('projectId', '', new UID(), 'Project unique ID.') - ->param('keyId', '', new UID(), 'Key unique ID.') - ->inject('response') - ->inject('dbForConsole') - ->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) { - - $project = $dbForConsole->getDocument('projects', $projectId); - - if ($project->isEmpty()) { - throw new Exception(Exception::PROJECT_NOT_FOUND); - } - - $key = $dbForConsole->findOne('keys', [ - Query::equal('$id', [$keyId]), - Query::equal('projectInternalId', [$project->getInternalId()]), - ]); - - if ($key === false || $key->isEmpty()) { - throw new Exception(Exception::KEY_NOT_FOUND); - } - - $response->dynamic($key, Response::MODEL_KEY); - }); - -App::put('/v1/projects/:projectId/keys/:keyId') - ->desc('Update key') - ->groups(['api', 'projects']) - ->label('scope', 'projects.write') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'updateKey') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_KEY) - ->param('projectId', '', new UID(), 'Project unique ID.') - ->param('keyId', '', new UID(), 'Key unique ID.') - ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') - ->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.') - ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true) - ->inject('response') - ->inject('dbForConsole') - ->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) { - - $project = $dbForConsole->getDocument('projects', $projectId); - - if ($project->isEmpty()) { - throw new Exception(Exception::PROJECT_NOT_FOUND); - } - - $key = $dbForConsole->findOne('keys', [ - Query::equal('$id', [$keyId]), - Query::equal('projectInternalId', [$project->getInternalId()]), - ]); - - if ($key === false || $key->isEmpty()) { - throw new Exception(Exception::KEY_NOT_FOUND); - } - - $key - ->setAttribute('name', $name) - ->setAttribute('scopes', $scopes) - ->setAttribute('expire', $expire); - - $dbForConsole->updateDocument('keys', $key->getId(), $key); - - $dbForConsole->purgeCachedDocument('projects', $project->getId()); - - $response->dynamic($key, Response::MODEL_KEY); - }); - -App::delete('/v1/projects/:projectId/keys/:keyId') - ->desc('Delete key') - ->groups(['api', 'projects']) - ->label('scope', 'projects.write') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'deleteKey') - ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) - ->label('sdk.response.model', Response::MODEL_NONE) - ->param('projectId', '', new UID(), 'Project unique ID.') - ->param('keyId', '', new UID(), 'Key unique ID.') - ->inject('response') - ->inject('dbForConsole') - ->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) { - - $project = $dbForConsole->getDocument('projects', $projectId); - - if ($project->isEmpty()) { - throw new Exception(Exception::PROJECT_NOT_FOUND); - } - - $key = $dbForConsole->findOne('keys', [ - Query::equal('$id', [$keyId]), - Query::equal('projectInternalId', [$project->getInternalId()]), - ]); - - if ($key === false || $key->isEmpty()) { - throw new Exception(Exception::KEY_NOT_FOUND); - } - - $dbForConsole->deleteDocument('keys', $key->getId()); - - $dbForConsole->purgeCachedDocument('projects', $project->getId()); - - $response->noContent(); - }); - // JWT Keys App::post('/v1/projects/:projectId/jwts') From c058d30d0a1f2fc4949a7c3d0c1db8f4e4624f5e Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 16 Jul 2024 05:20:56 +0000 Subject: [PATCH 003/110] all endpoints for development keys --- .../Modules/DevelopmentKeys/Http/Create.php | 28 ++++---- .../Modules/DevelopmentKeys/Http/Delete.php | 63 +++++++++++++++++ .../Modules/DevelopmentKeys/Http/Get.php | 60 ++++++++++++++++ .../Modules/DevelopmentKeys/Http/Update.php | 70 +++++++++++++++++++ .../Modules/DevelopmentKeys/Http/XList.php | 59 ++++++++++++++++ 5 files changed, 266 insertions(+), 14 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Delete.php create mode 100644 src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Get.php create mode 100644 src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Update.php create mode 100644 src/Appwrite/Platform/Modules/DevelopmentKeys/Http/XList.php diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php index 12c60546b4..f8b4f75ea2 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php @@ -29,20 +29,20 @@ class Create extends Action ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/projects/:projectId/development-keys') ->desc('Create key') - ->groups(['api', 'projects']) - ->label('scope', 'projects.write') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'createKey') - ->label('sdk.response.code', Response::STATUS_CODE_CREATED) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_KEY) - ->param('projectId', '', new UID(), 'Project unique ID.') - ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') - ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true) - ->inject('response') - ->inject('dbForConsole') - ->callback(fn ($projectId, $name, $expire, $response, $dbForConsole) => $this->action($projectId, $name, $expire, $response, $dbForConsole)); + ->groups(['api', 'projects']) + ->label('scope', 'projects.write') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'projects') + ->label('sdk.method', 'createKey') + ->label('sdk.response.code', Response::STATUS_CODE_CREATED) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_KEY) + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') + ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true) + ->inject('response') + ->inject('dbForConsole') + ->callback(fn ($projectId, $name, $expire, $response, $dbForConsole) => $this->action($projectId, $name, $expire, $response, $dbForConsole)); } public function action(string $projectId, string $name, ?string $expire, Response $response, Database $dbForConsole) diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Delete.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Delete.php new file mode 100644 index 0000000000..414f8d59a7 --- /dev/null +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Delete.php @@ -0,0 +1,63 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_DELETE) + ->setHttpPath('/v1/projects/:projectId/development-keys/:keyId') + ->desc('Delete key') + ->groups(['api', 'projects']) + ->label('scope', 'projects.write') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'projects') + ->label('sdk.method', 'deleteDevelopmentKey') + ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) + ->label('sdk.response.model', Response::MODEL_NONE) + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('keyId', '', new UID(), 'Key unique ID.') + ->inject('response') + ->inject('dbForConsole') + ->callback(fn ($projectId, $keyId, $response, $dbForConsole) => $this->action($projectId, $keyId, $response, $dbForConsole)); + } + + public function action(string $projectId, string $keyId, Response $response, Database $dbForConsole) + { + + $project = $dbForConsole->getDocument('projects', $projectId); + + if ($project->isEmpty()) { + throw new Exception(Exception::PROJECT_NOT_FOUND); + } + + $key = $dbForConsole->findOne('development_keys', [ + Query::equal('$id', [$keyId]), + Query::equal('projectInternalId', [$project->getInternalId()]), + ]); + + if ($key === false || $key->isEmpty()) { + throw new Exception(Exception::KEY_NOT_FOUND); + } + + $dbForConsole->deleteDocument('keys', $key->getId()); + + $dbForConsole->purgeCachedDocument('projects', $project->getId()); + + $response->noContent(); + } +} diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Get.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Get.php new file mode 100644 index 0000000000..ed481182a1 --- /dev/null +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Get.php @@ -0,0 +1,60 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/projects/:projectId/development-keys/:keyId') + ->desc('Get key') + ->groups(['api', 'projects']) + ->label('scope', 'projects.read') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'projects') + ->label('sdk.method', 'getDevelopmentKey') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_KEY) + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('keyId', '', new UID(), 'Key unique ID.') + ->inject('response') + ->inject('dbForConsole') + ->callback(fn ($projectId, $keyId, $response, $dbForConsole) => $this->action($projectId, $keyId, $response, $dbForConsole)); + } + + public function action(string $projectId, string $keyId, Response $response, Database $dbForConsole) + { + + $project = $dbForConsole->getDocument('projects', $projectId); + + if ($project->isEmpty()) { + throw new Exception(Exception::PROJECT_NOT_FOUND); + } + + $key = $dbForConsole->findOne('development_keys', [ + Query::equal('$id', [$keyId]), + Query::equal('projectInternalId', [$project->getInternalId()]), + ]); + + if ($key === false || $key->isEmpty()) { + throw new Exception(Exception::KEY_NOT_FOUND); + } + + $response->dynamic($key, Response::MODEL_KEY); + } +} diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Update.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Update.php new file mode 100644 index 0000000000..5ab8af6321 --- /dev/null +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Update.php @@ -0,0 +1,70 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_PUT) + ->setHttpPath('/v1/projects/:projectId/development-keys/:keyId') + ->desc('Update key') + ->groups(['api', 'projects']) + ->label('scope', 'projects.write') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'projects') + ->label('sdk.method', 'updateDevelopmentKey') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_KEY) + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('keyId', '', new UID(), 'Key unique ID.') + ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') + ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true) + ->inject('response') + ->inject('dbForConsole') + ->callback(fn ($projectId, $keyId, $name, $expire, $response, $dbForConsole) => $this->action($projectId, $keyId, $name, $expire, $response, $dbForConsole)); + } + public function action(string $projectId, string $keyId, string $name, ?string $expire, Response $response, Database $dbForConsole) + { + + $project = $dbForConsole->getDocument('projects', $projectId); + + if ($project->isEmpty()) { + throw new Exception(Exception::PROJECT_NOT_FOUND); + } + + $key = $dbForConsole->findOne('development_keys', [ + Query::equal('$id', [$keyId]), + Query::equal('projectInternalId', [$project->getInternalId()]), + ]); + + if ($key === false || $key->isEmpty()) { + throw new Exception(Exception::KEY_NOT_FOUND); + } + + $key + ->setAttribute('name', $name) + ->setAttribute('expire', $expire); + + $dbForConsole->updateDocument('keys', $key->getId(), $key); + + $dbForConsole->purgeCachedDocument('projects', $project->getId()); + + $response->dynamic($key, Response::MODEL_KEY); + } +} diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/XList.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/XList.php new file mode 100644 index 0000000000..4304e5ef7e --- /dev/null +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/XList.php @@ -0,0 +1,59 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/projects/:projectId/development-keys') + ->desc('List keys') + ->groups(['api', 'projects']) + ->label('scope', 'projects.read') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'projects') + ->label('sdk.method', 'listKeys') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_KEY_LIST) + ->param('projectId', '', new UID(), 'Project unique ID.') + ->inject('response') + ->inject('dbForConsole') + ->callback(fn ($projectId, $response, $dbForConsole) => $this->action($projectId, $response, $dbForConsole)); + } + + public function action(string $projectId, Response $response, Database $dbForConsole) + { + + $project = $dbForConsole->getDocument('projects', $projectId); + + if ($project->isEmpty()) { + throw new Exception(Exception::PROJECT_NOT_FOUND); + } + + $keys = $dbForConsole->find('development_keys', [ + Query::equal('projectInternalId', [$project->getInternalId()]), + Query::limit(5000), + ]); + + $response->dynamic(new Document([ + 'keys' => $keys, + 'total' => count($keys), + ]), Response::MODEL_KEY_LIST); + } +} From af3dc25e2c41afa5519f9287142726096e5c7f60 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 16 Jul 2024 07:48:21 +0000 Subject: [PATCH 004/110] add new endpoints to module --- .../Platform/Modules/DevelopmentKeys/Http/Delete.php | 1 + .../Platform/Modules/DevelopmentKeys/Http/Get.php | 1 + .../Platform/Modules/DevelopmentKeys/Http/Update.php | 1 + .../Platform/Modules/DevelopmentKeys/Http/XList.php | 1 + .../Platform/Modules/DevelopmentKeys/Services/Http.php | 8 ++++++++ 5 files changed, 12 insertions(+) diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Delete.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Delete.php index 414f8d59a7..a599b660d9 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Delete.php +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Delete.php @@ -1,4 +1,5 @@ type = Service::TYPE_HTTP; $this->addAction(Create::getName(), new Create()); + $this->addAction(Update::getName(), new Update()); + $this->addAction(Get::getName(), new Get()); + $this->addAction(XList::getName(), new XList()); + $this->addAction(Delete::getName(), new Delete()); } } From 03000c3ad09a624a80fbb18fbcf00a30740a6c7d Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 16 Jul 2024 07:50:42 +0000 Subject: [PATCH 005/110] add collection for development keys --- app/config/collections.php | 101 ++++++++++++++++++ .../Modules/DevelopmentKeys/Http/Create.php | 2 +- .../Modules/DevelopmentKeys/Http/Delete.php | 2 +- .../Modules/DevelopmentKeys/Http/Get.php | 2 +- .../Modules/DevelopmentKeys/Http/Update.php | 2 +- .../Modules/DevelopmentKeys/Http/XList.php | 2 +- 6 files changed, 106 insertions(+), 5 deletions(-) diff --git a/app/config/collections.php b/app/config/collections.php index e02e25829f..c2b450b353 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -4808,6 +4808,107 @@ $consoleCollections = array_merge([ ], ], + 'developmentKeys' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('developmentKeys'), + 'name' => 'keys', + 'attributes' => [ + [ + '$id' => ID::custom('projectInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('projectId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => 0, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('name'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('secret'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 512, // var_dump of \bin2hex(\random_bytes(128)) => string(256) doubling for encryption + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => ['encrypt'], + ], + [ + '$id' => ID::custom('expire'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('accessedAt'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('sdks'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => true, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_project'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['projectInternalId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => '_key_accessedAt', + 'type' => Database::INDEX_KEY, + 'attributes' => ['accessedAt'], + 'lengths' => [], + 'orders' => [], + ], + ], + ], + 'webhooks' => [ '$collection' => ID::custom(Database::METADATA), '$id' => ID::custom('webhooks'), diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php index f8b4f75ea2..cb588d3696 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php @@ -69,7 +69,7 @@ class Create extends Action 'secret' => API_KEY_STANDARD . '_' . \bin2hex(\random_bytes(128)), ]); - $key = $dbForConsole->createDocument('development_keys', $key); + $key = $dbForConsole->createDocument('developmentKeys', $key); $dbForConsole->purgeCachedDocument('projects', $project->getId()); diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Delete.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Delete.php index a599b660d9..a1e0dad5b0 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Delete.php +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Delete.php @@ -46,7 +46,7 @@ class Delete extends Action throw new Exception(Exception::PROJECT_NOT_FOUND); } - $key = $dbForConsole->findOne('development_keys', [ + $key = $dbForConsole->findOne('developmentKeys', [ Query::equal('$id', [$keyId]), Query::equal('projectInternalId', [$project->getInternalId()]), ]); diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Get.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Get.php index c8cf672b61..6f9f2d73b2 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Get.php +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Get.php @@ -47,7 +47,7 @@ class Get extends Action throw new Exception(Exception::PROJECT_NOT_FOUND); } - $key = $dbForConsole->findOne('development_keys', [ + $key = $dbForConsole->findOne('developmentKeys', [ Query::equal('$id', [$keyId]), Query::equal('projectInternalId', [$project->getInternalId()]), ]); diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Update.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Update.php index da9d9c65b8..553f28b41a 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Update.php +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Update.php @@ -49,7 +49,7 @@ class Update extends Action throw new Exception(Exception::PROJECT_NOT_FOUND); } - $key = $dbForConsole->findOne('development_keys', [ + $key = $dbForConsole->findOne('developmentKeys', [ Query::equal('$id', [$keyId]), Query::equal('projectInternalId', [$project->getInternalId()]), ]); diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/XList.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/XList.php index 725ceda826..49605b0fdd 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/XList.php +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/XList.php @@ -47,7 +47,7 @@ class XList extends Action throw new Exception(Exception::PROJECT_NOT_FOUND); } - $keys = $dbForConsole->find('development_keys', [ + $keys = $dbForConsole->find('developmentKeys', [ Query::equal('projectInternalId', [$project->getInternalId()]), Query::limit(5000), ]); From 760c552ffb45a9f5070262c5d597e7298bd62fc1 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 16 Jul 2024 08:28:22 +0000 Subject: [PATCH 006/110] support for api test keys --- app/controllers/shared/api.php | 30 ++++++++++++++++++++++++++++++ app/init.php | 1 + 2 files changed, 31 insertions(+) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index bcf7c47d0b..2fff0f10a7 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -291,6 +291,36 @@ App::init() } } } + } elseif($keyType === API_KEY_TEST) { + // Check if given key match project Test API keys + $key = $project->find('secret', $apiKey, 'developmentKeys'); + if ($key) { + $user = new Document([ + '$id' => '', + 'status' => true, + 'email' => 'app-test.' . $project->getId() . '@service.' . $request->getHostname(), + 'password' => '', + 'name' => $project->getAttribute('name', 'Untitled'), + ]); + + $role = Auth::USER_ROLE_ADMIN; + $scopes = $roles[$role]['scopes']; + + $expire = $key->getAttribute('expire'); + if (!empty($expire) && $expire < DateTime::formatTz(DateTime::now())) { + throw new Exception(Exception::PROJECT_KEY_EXPIRED); + } + + Authorization::setRole(Auth::USER_ROLE_ADMIN); + Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys. + + $accessedAt = $key->getAttribute('accessedAt', ''); + if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCCESS)) > $accessedAt) { + $key->setAttribute('accessedAt', DateTime::now()); + $dbForConsole->updateDocument('keys', $key->getId(), $key); + $dbForConsole->purgeCachedDocument('projects', $project->getId()); + } + } } } diff --git a/app/init.php b/app/init.php index a0bd3b837b..b75e1bdd99 100644 --- a/app/init.php +++ b/app/init.php @@ -215,6 +215,7 @@ const MESSAGE_TYPE_PUSH = 'push'; // API key types const API_KEY_STANDARD = 'standard'; const API_KEY_DYNAMIC = 'dynamic'; +const API_KEY_TEST = 'test'; // Usage metrics const METRIC_TEAMS = 'teams'; const METRIC_USERS = 'users'; From ae5a60db89675f0aeebe2904fe18fe72808bcb98 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 16 Jul 2024 08:33:38 +0000 Subject: [PATCH 007/110] composer update --- composer.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/composer.lock b/composer.lock index a32159f79a..cf3aaec13b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "06f391b62842a79736fe3fe77ec82adf", + "content-hash": "f5f5f624d7edf2e0a405f4669ae8f672", "packages": [ { "name": "adhocore/jwt", @@ -1720,16 +1720,16 @@ }, { "name": "utopia-php/database", - "version": "0.49.14", + "version": "0.50.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "415588c0b98edee9d72cdfe269ff79b14cd8f56d" + "reference": "ce3eaccb2f3bbd34b2b97419836fec633b26b8f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/415588c0b98edee9d72cdfe269ff79b14cd8f56d", - "reference": "415588c0b98edee9d72cdfe269ff79b14cd8f56d", + "url": "https://api.github.com/repos/utopia-php/database/zipball/ce3eaccb2f3bbd34b2b97419836fec633b26b8f7", + "reference": "ce3eaccb2f3bbd34b2b97419836fec633b26b8f7", "shasum": "" }, "require": { @@ -1770,9 +1770,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.49.14" + "source": "https://github.com/utopia-php/database/tree/0.50.0" }, - "time": "2024-06-20T02:39:23+00:00" + "time": "2024-06-21T03:21:42+00:00" }, { "name": "utopia-php/domains", @@ -3157,16 +3157,16 @@ }, { "name": "laravel/pint", - "version": "v1.16.1", + "version": "v1.16.2", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "9266a47f1b9231b83e0cfd849009547329d871b1" + "reference": "51f1ba679a6afe0315621ad143d788bd7ded0eca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/9266a47f1b9231b83e0cfd849009547329d871b1", - "reference": "9266a47f1b9231b83e0cfd849009547329d871b1", + "url": "https://api.github.com/repos/laravel/pint/zipball/51f1ba679a6afe0315621ad143d788bd7ded0eca", + "reference": "51f1ba679a6afe0315621ad143d788bd7ded0eca", "shasum": "" }, "require": { @@ -3219,7 +3219,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-06-18T16:50:05+00:00" + "time": "2024-07-09T15:58:08+00:00" }, { "name": "matthiasmullie/minify", From ca0c38b3c0758c57edacffa3b5513de310a31601 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 29 Jul 2024 10:12:50 +0000 Subject: [PATCH 008/110] subquery for development keys --- app/config/collections.php | 11 +++++++++++ app/controllers/shared/api.php | 3 ++- app/init.php | 14 ++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/app/config/collections.php b/app/config/collections.php index 5e7de4da81..d3f7c0c84b 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -4486,6 +4486,17 @@ $consoleCollections = array_merge([ 'array' => false, 'filters' => ['subQueryKeys'], ], + [ + '$id' => ID::custom('developmentKeys'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['subQueryDevelopmentKeys'], + ], [ '$id' => ID::custom('search'), 'type' => Database::VAR_STRING, diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 158cec98e2..c93a3cc1ab 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -293,6 +293,7 @@ App::init() } } } elseif($keyType === API_KEY_TEST) { + var_dump('keys matches test'); // Check if given key match project Test API keys $key = $project->find('secret', $apiKey, 'developmentKeys'); if ($key) { @@ -316,7 +317,7 @@ App::init() Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys. $accessedAt = $key->getAttribute('accessedAt', ''); - if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCCESS)) > $accessedAt) { + if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) { $key->setAttribute('accessedAt', DateTime::now()); $dbForConsole->updateDocument('keys', $key->getId(), $key); $dbForConsole->purgeCachedDocument('projects', $project->getId()); diff --git a/app/init.php b/app/init.php index 0ddeecc341..07c4a6b501 100644 --- a/app/init.php +++ b/app/init.php @@ -434,6 +434,20 @@ Database::addFilter( } ); +Database::addFilter( + 'subQueryDevelopmentKeys', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return $database + ->find('developmentKeys', [ + Query::equal('projectInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ]); + } +); + Database::addFilter( 'subQueryWebhooks', function (mixed $value) { From 55fc9d7fbdecfabe29c05e0c7b31571bd1516e7f Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 29 Jul 2024 10:12:57 +0000 Subject: [PATCH 009/110] tests and fixes --- app/console | 1 + .../Modules/DevelopmentKeys/Http/Create.php | 2 +- .../Modules/DevelopmentKeys/Http/Delete.php | 3 +- .../Modules/DevelopmentKeys/Http/Get.php | 1 + .../Modules/DevelopmentKeys/Http/Update.php | 3 +- .../Modules/DevelopmentKeys/Http/XList.php | 1 + .../Modules/DevelopmentKeys/Services/Http.php | 6 +- .../Projects/ProjectsConsoleClientTest.php | 2 + .../Projects/ProjectsDevelopmentKeys.php | 247 ++++++++++++++++++ 9 files changed, 260 insertions(+), 6 deletions(-) create mode 160000 app/console create mode 100644 tests/e2e/Services/Projects/ProjectsDevelopmentKeys.php diff --git a/app/console b/app/console new file mode 160000 index 0000000000..f978cecfaf --- /dev/null +++ b/app/console @@ -0,0 +1 @@ +Subproject commit f978cecfafe55e85fcd20fe90fc020e78a7c7952 diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php index cb588d3696..c1f164987f 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php @@ -66,7 +66,7 @@ class Create extends Action 'expire' => $expire, 'sdks' => [], 'accessedAt' => null, - 'secret' => API_KEY_STANDARD . '_' . \bin2hex(\random_bytes(128)), + 'secret' => API_KEY_TEST . '_' . \bin2hex(\random_bytes(128)), ]); $key = $dbForConsole->createDocument('developmentKeys', $key); diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Delete.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Delete.php index a1e0dad5b0..c088e1059e 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Delete.php +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Delete.php @@ -1,4 +1,5 @@ deleteDocument('keys', $key->getId()); + $dbForConsole->deleteDocument('developmentKeys', $key->getId()); $dbForConsole->purgeCachedDocument('projects', $project->getId()); diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Get.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Get.php index 6f9f2d73b2..43435a6a27 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Get.php +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Get.php @@ -1,4 +1,5 @@ setAttribute('name', $name) ->setAttribute('expire', $expire); - $dbForConsole->updateDocument('keys', $key->getId(), $key); + $dbForConsole->updateDocument('developmentKeys', $key->getId(), $key); $dbForConsole->purgeCachedDocument('projects', $project->getId()); diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/XList.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/XList.php index 49605b0fdd..65198037b2 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/XList.php +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/XList.php @@ -1,4 +1,5 @@ client->call(Client::METHOD_POST, '/projects/' . $id . '/development-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Key Test' + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals('Key Test', $response['body']['name']); + $this->assertNotEmpty($response['body']['secret']); + $this->assertArrayHasKey('sdks', $response['body']); + $this->assertEmpty($response['body']['sdks']); + $this->assertArrayHasKey('accessedAt', $response['body']); + $this->assertEmpty($response['body']['accessedAt']); + + $data = array_merge($data, [ + 'keyId' => $response['body']['$id'], + 'secret' => $response['body']['secret'] + ]); + + return $data; + } + + + /** + * @depends testCreateProjectDevelopmentKey + * @group developmentKeys + */ + public function testListProjectDevelopmentKey($data): array + { + $id = $data['projectId'] ?? ''; + + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/development-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(1, $response['body']['total']); + + return $data; + } + + + /** + * @depends testCreateProjectDevelopmentKey + * @group developmentKeys + */ + public function testGetProjectDevelopmentKey($data): array + { + $id = $data['projectId'] ?? ''; + $keyId = $data['keyId'] ?? ''; + + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/development-keys/' . $keyId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals($keyId, $response['body']['$id']); + $this->assertEquals('Key Test', $response['body']['name']); + $this->assertNotEmpty($response['body']['secret']); + $this->assertArrayHasKey('sdks', $response['body']); + $this->assertEmpty($response['body']['sdks']); + $this->assertArrayHasKey('accessedAt', $response['body']); + $this->assertEmpty($response['body']['accessedAt']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/development-keys/error', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(404, $response['headers']['status-code']); + + return $data; + } + + /** + * @depends testCreateProject + * @group developmentKeys + */ + public function testValidateProjectDevelopmentKey($data): void + { + $id = $data['projectId'] ?? ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/development-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 3600), + ]); + var_dump($response['body']['secret']); + $response = $this->client->call(Client::METHOD_GET, '/health', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $id, + 'x-appwrite-key' => $response['body']['secret'] + ], []); + + $this->assertEquals(200, $response['headers']['status-code']); + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/development-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Key Test', + 'expire' => null, + ]); + + $response = $this->client->call(Client::METHOD_GET, '/health', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $id, + 'x-appwrite-key' => $response['body']['secret'] + ], []); + + $this->assertEquals(200, $response['headers']['status-code']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/development-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), -3600), + ]); + + $response = $this->client->call(Client::METHOD_GET, '/health', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $id, + 'x-appwrite-key' => $response['body']['secret'] + ], []); + + $this->assertEquals(401, $response['headers']['status-code']); + } + + + /** + * @depends testCreateProjectDevelopmentKey + * @group developmentKeys + */ + public function testUpdateProjectDevelopmentKey($data): array + { + $id = $data['projectId'] ?? ''; + $keyId = $data['keyId'] ?? ''; + + $response = $this->client->call(Client::METHOD_PUT, '/projects/' . $id . '/development-keys/' . $keyId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Key Test Update', + 'expire' => DateTime::addSeconds(new \DateTime(), 360), + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals($keyId, $response['body']['$id']); + $this->assertEquals('Key Test Update', $response['body']['name']); + $this->assertArrayHasKey('sdks', $response['body']); + $this->assertEmpty($response['body']['sdks']); + $this->assertArrayHasKey('accessedAt', $response['body']); + $this->assertEmpty($response['body']['accessedAt']); + + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/development-keys/' . $keyId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals($keyId, $response['body']['$id']); + $this->assertEquals('Key Test Update', $response['body']['name']); + $this->assertArrayHasKey('sdks', $response['body']); + $this->assertEmpty($response['body']['sdks']); + $this->assertArrayHasKey('accessedAt', $response['body']); + $this->assertEmpty($response['body']['accessedAt']); + + return $data; + } + + /** + * @depends testCreateProjectDevelopmentKey + * @group developmentKeys + */ + public function testDeleteProjectDevelopmentKey($data): array + { + $id = $data['projectId'] ?? ''; + $keyId = $data['keyId'] ?? ''; + + $response = $this->client->call(Client::METHOD_DELETE, '/projects/' . $id . '/development-keys/' . $keyId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(204, $response['headers']['status-code']); + $this->assertEmpty($response['body']); + + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/development-keys/' . $keyId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(404, $response['headers']['status-code']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_DELETE, '/projects/' . $id . '/development-keys/error', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(404, $response['headers']['status-code']); + + return $data; + } +} From d9556ba603eca43a061892f36d8671c33feb1480 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 29 Jul 2024 10:18:06 +0000 Subject: [PATCH 010/110] make expiry date required --- .../Modules/DevelopmentKeys/Http/Create.php | 2 +- .../Modules/DevelopmentKeys/Http/Update.php | 4 ++-- .../Services/Projects/ProjectsDevelopmentKeys.php | 15 +++++++++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php index c1f164987f..a00f3f146d 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php @@ -39,7 +39,7 @@ class Create extends Action ->label('sdk.response.model', Response::MODEL_KEY) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') - ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true) + ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.', false) ->inject('response') ->inject('dbForConsole') ->callback(fn ($projectId, $name, $expire, $response, $dbForConsole) => $this->action($projectId, $name, $expire, $response, $dbForConsole)); diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Update.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Update.php index fd09bdead8..9379c147f2 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Update.php +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Update.php @@ -36,7 +36,7 @@ class Update extends Action ->param('projectId', '', new UID(), 'Project unique ID.') ->param('keyId', '', new UID(), 'Key unique ID.') ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') - ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true) + ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.', true) ->inject('response') ->inject('dbForConsole') ->callback(fn ($projectId, $keyId, $name, $expire, $response, $dbForConsole) => $this->action($projectId, $keyId, $name, $expire, $response, $dbForConsole)); @@ -61,7 +61,7 @@ class Update extends Action $key ->setAttribute('name', $name) - ->setAttribute('expire', $expire); + ->setAttribute('expire', $expire ?? $key->getAttribute('expire')); $dbForConsole->updateDocument('developmentKeys', $key->getId(), $key); diff --git a/tests/e2e/Services/Projects/ProjectsDevelopmentKeys.php b/tests/e2e/Services/Projects/ProjectsDevelopmentKeys.php index de9f4db3fd..15ccfa0949 100644 --- a/tests/e2e/Services/Projects/ProjectsDevelopmentKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevelopmentKeys.php @@ -19,7 +19,8 @@ trait ProjectsDevelopmentKeys 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'name' => 'Key Test' + 'name' => 'Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 36000) ]); $this->assertEquals(201, $response['headers']['status-code']); @@ -31,6 +32,16 @@ trait ProjectsDevelopmentKeys $this->assertArrayHasKey('accessedAt', $response['body']); $this->assertEmpty($response['body']['accessedAt']); + /** TEST expiry date is required */ + $res = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/development-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Key Test' + ]); + + $this->assertEquals(400, $res['headers']['status-code']); + $data = array_merge($data, [ 'keyId' => $response['body']['$id'], 'secret' => $response['body']['secret'] @@ -133,7 +144,7 @@ trait ProjectsDevelopmentKeys 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'name' => 'Key Test', - 'expire' => null, + 'expire' => DateTime::addSeconds(new \DateTime(), 3600), ]); $response = $this->client->call(Client::METHOD_GET, '/health', [ From 61c5848be4c2f85cc27d69c27db816744c278c80 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 12 Aug 2024 05:34:25 +0000 Subject: [PATCH 011/110] refactor development keys --- app/controllers/shared/api.php | 31 ------------------- app/init.php | 24 +++++++++++++- .../Modules/DevelopmentKeys/Http/Create.php | 2 +- 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index c93a3cc1ab..672366fa01 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -292,37 +292,6 @@ App::init() } } } - } elseif($keyType === API_KEY_TEST) { - var_dump('keys matches test'); - // Check if given key match project Test API keys - $key = $project->find('secret', $apiKey, 'developmentKeys'); - if ($key) { - $user = new Document([ - '$id' => '', - 'status' => true, - 'email' => 'app-test.' . $project->getId() . '@service.' . $request->getHostname(), - 'password' => '', - 'name' => $project->getAttribute('name', 'Untitled'), - ]); - - $role = Auth::USER_ROLE_ADMIN; - $scopes = $roles[$role]['scopes']; - - $expire = $key->getAttribute('expire'); - if (!empty($expire) && $expire < DateTime::formatTz(DateTime::now())) { - throw new Exception(Exception::PROJECT_KEY_EXPIRED); - } - - Authorization::setRole(Auth::USER_ROLE_ADMIN); - Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys. - - $accessedAt = $key->getAttribute('accessedAt', ''); - if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) { - $key->setAttribute('accessedAt', DateTime::now()); - $dbForConsole->updateDocument('keys', $key->getId(), $key); - $dbForConsole->purgeCachedDocument('projects', $project->getId()); - } - } } } diff --git a/app/init.php b/app/init.php index e453046821..cb5eb55558 100644 --- a/app/init.php +++ b/app/init.php @@ -53,6 +53,7 @@ use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Adapter\MySQL; use Utopia\Database\Adapter\SQL; use Utopia\Database\Database; +use Utopia\Database\DateTime as DatabaseDateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; @@ -216,7 +217,6 @@ const MESSAGE_TYPE_PUSH = 'push'; // API key types const API_KEY_STANDARD = 'standard'; const API_KEY_DYNAMIC = 'dynamic'; -const API_KEY_TEST = 'test'; // Usage metrics const METRIC_TEAMS = 'teams'; const METRIC_USERS = 'users'; @@ -1749,3 +1749,25 @@ App::setResource('requestTimestamp', function ($request) { App::setResource('plan', function (array $plan = []) { return []; }); + +App::setResource('hasDevelopmentKey', function ($request, $project, $dbForConsole) { + $developmentKey = $request->getHeader('x-appwrite-development-key', ''); + // Check if given key match project Test API keys + $key = $project->find('secret', $developmentKey, 'developmentKeys'); + if ($key) { + + $expire = $key->getAttribute('expire'); + if (!empty($expire) && $expire < DatabaseDateTime::formatTz(DatabaseDateTime::now())) { + return false; + } + + $accessedAt = $key->getAttribute('accessedAt', ''); + if (DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) { + $key->setAttribute('accessedAt', DatabaseDateTime::now()); + Authorization::skip(fn () => $dbForConsole->updateDocument('keys', $key->getId(), $key)); + $dbForConsole->purgeCachedDocument('projects', $project->getId()); + } + return true; + } + return false; +}, ['request', 'project', 'dbForConsole']); diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php index a00f3f146d..ec898cde7d 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php @@ -66,7 +66,7 @@ class Create extends Action 'expire' => $expire, 'sdks' => [], 'accessedAt' => null, - 'secret' => API_KEY_TEST . '_' . \bin2hex(\random_bytes(128)), + 'secret' => \bin2hex(\random_bytes(128)), ]); $key = $dbForConsole->createDocument('developmentKeys', $key); From 0a2eee01180a16f3a911e76307ab3e6137fe41c9 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 12 Aug 2024 05:44:52 +0000 Subject: [PATCH 012/110] detailed error logs if development key is provided --- app/controllers/general.php | 7 ++++--- app/controllers/shared/api.php | 4 +++- app/init.php | 3 +-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 143eb15684..13a1516627 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -671,7 +671,8 @@ App::error() ->inject('logger') ->inject('log') ->inject('queueForUsage') - ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Usage $queueForUsage) { + ->inject('hasDevelopmentKey') + ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Usage $queueForUsage, bool $hasDevelopmentKey) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); $route = $utopia->getRoute(); $class = \get_class($error); @@ -859,7 +860,7 @@ App::error() $type = $error->getType(); - $output = ((App::isDevelopment())) ? [ + $output = ((App::isDevelopment()) || $hasDevelopmentKey) ? [ 'message' => $message, 'code' => $code, 'file' => $file, @@ -900,7 +901,7 @@ App::error() $response->dynamic( new Document($output), - $utopia->isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR + $utopia->isDevelopment() || $hasDevelopmentKey ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR ); }); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 672366fa01..068c7e2f43 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -357,7 +357,8 @@ App::init() ->inject('queueForUsage') ->inject('dbForProject') ->inject('mode') - ->action(function (App $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) use ($databaseListener) { + ->inject('hasDevelopmentKey') + ->action(function (App $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, bool $hasDevelopmentKey) use ($databaseListener) { $route = $utopia->getRoute(); @@ -424,6 +425,7 @@ App::init() $enabled // Abuse is enabled && !$isAppUser // User is not API key && !$isPrivilegedUser // User is not an admin + && !$hasDevelopmentKey // request doesn't not contain development key && $abuse->check() // Route is rate-limited ) { throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED); diff --git a/app/init.php b/app/init.php index cb5eb55558..de958ec213 100644 --- a/app/init.php +++ b/app/init.php @@ -1752,10 +1752,9 @@ App::setResource('plan', function (array $plan = []) { App::setResource('hasDevelopmentKey', function ($request, $project, $dbForConsole) { $developmentKey = $request->getHeader('x-appwrite-development-key', ''); - // Check if given key match project Test API keys + // Check if given key match project's development keys $key = $project->find('secret', $developmentKey, 'developmentKeys'); if ($key) { - $expire = $key->getAttribute('expire'); if (!empty($expire) && $expire < DatabaseDateTime::formatTz(DatabaseDateTime::now())) { return false; From 383cff5eb8c003374095b34d32f6029222538998 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 12 Aug 2024 08:39:03 +0000 Subject: [PATCH 013/110] enable abuse to test development keys --- .env | 2 +- .../Projects/ProjectsDevelopmentKeys.php | 59 +++++++++++-------- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/.env b/.env index 9cccf5ee7e..3e666fc2f9 100644 --- a/.env +++ b/.env @@ -13,7 +13,7 @@ _APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io _APP_EMAIL_SECURITY=security@appwrite.io _APP_EMAIL_CERTIFICATES=certificates@appwrite.io _APP_SYSTEM_RESPONSE_FORMAT= -_APP_OPTIONS_ABUSE=disabled +_APP_OPTIONS_ABUSE=enabled _APP_OPTIONS_ROUTER_PROTECTION=disabled _APP_OPTIONS_FORCE_HTTPS=disabled _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled diff --git a/tests/e2e/Services/Projects/ProjectsDevelopmentKeys.php b/tests/e2e/Services/Projects/ProjectsDevelopmentKeys.php index 15ccfa0949..cbc677a7e1 100644 --- a/tests/e2e/Services/Projects/ProjectsDevelopmentKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevelopmentKeys.php @@ -113,7 +113,7 @@ trait ProjectsDevelopmentKeys * @depends testCreateProject * @group developmentKeys */ - public function testValidateProjectDevelopmentKey($data): void + public function testNoRateLimitWithDevelopmentKey($data): void { $id = $data['projectId'] ?? ''; @@ -127,33 +127,38 @@ trait ProjectsDevelopmentKeys 'name' => 'Key Test', 'expire' => DateTime::addSeconds(new \DateTime(), 3600), ]); - var_dump($response['body']['secret']); - $response = $this->client->call(Client::METHOD_GET, '/health', [ + + $developmentKey = $response['body']['secret']; + + // + for($i = 0; $i < 11; $i++) { + $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $id, + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + } + $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', 'x-appwrite-project' => $id, - 'x-appwrite-key' => $response['body']['secret'] - ], []); - - $this->assertEquals(200, $response['headers']['status-code']); - - /** - * Test for SUCCESS - */ - $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/development-keys', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'name' => 'Key Test', - 'expire' => DateTime::addSeconds(new \DateTime(), 3600), + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' ]); + $this->assertEquals('429', $res['headers']['status-code']); - $response = $this->client->call(Client::METHOD_GET, '/health', [ + $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', 'x-appwrite-project' => $id, - 'x-appwrite-key' => $response['body']['secret'] - ], []); + 'x-appwrite-development-key' => $developmentKey + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals('401', $res['headers']['status-code']); - $this->assertEquals(200, $response['headers']['status-code']); /** * Test for FAILURE @@ -166,13 +171,15 @@ trait ProjectsDevelopmentKeys 'expire' => DateTime::addSeconds(new \DateTime(), -3600), ]); - $response = $this->client->call(Client::METHOD_GET, '/health', [ + $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', 'x-appwrite-project' => $id, - 'x-appwrite-key' => $response['body']['secret'] - ], []); - - $this->assertEquals(401, $response['headers']['status-code']); + 'x-appwrite-development-key' => $response['body']['secret'] + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals('429', $res['headers']['status-code']); } From f2377ce5d2cee42d9a7edfac470b82dd229c4784 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 12 Aug 2024 08:55:14 +0000 Subject: [PATCH 014/110] remove submodule --- app/console | 1 - 1 file changed, 1 deletion(-) delete mode 160000 app/console diff --git a/app/console b/app/console deleted file mode 160000 index f978cecfaf..0000000000 --- a/app/console +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f978cecfafe55e85fcd20fe90fc020e78a7c7952 From 179684e8b2b69b67bcd48c4280de6f0da84409d3 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 14 Nov 2024 04:48:46 +0000 Subject: [PATCH 015/110] improve dependency --- app/controllers/general.php | 8 ++++---- app/controllers/shared/api.php | 6 +++--- app/init.php | 9 ++++----- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 2079ee6555..cecbfdd7f5 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -710,8 +710,8 @@ App::error() ->inject('logger') ->inject('log') ->inject('queueForUsage') - ->inject('hasDevelopmentKey') - ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Usage $queueForUsage, bool $hasDevelopmentKey) { + ->inject('developmentKey') + ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Usage $queueForUsage, Document $developmentKey) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); $route = $utopia->getRoute(); $class = \get_class($error); @@ -912,7 +912,7 @@ App::error() $type = $error->getType(); - $output = ((App::isDevelopment()) || $hasDevelopmentKey) ? [ + $output = ((App::isDevelopment()) || (!$developmentKey->isEmpty())) ? [ 'message' => $message, 'code' => $code, 'file' => $file, @@ -953,7 +953,7 @@ App::error() $response->dynamic( new Document($output), - $utopia->isDevelopment() || $hasDevelopmentKey ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR + $utopia->isDevelopment() || !$developmentKey->isEmpty() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR ); }); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 42c5035318..70a97099b2 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -393,8 +393,8 @@ App::init() ->inject('queueForUsage') ->inject('dbForProject') ->inject('mode') - ->inject('hasDevelopmentKey') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Connection $queue, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, string $mode, bool $hasDevelopmentKey) use ($usageDatabaseListener, $eventDatabaseListener) { + ->inject('developmentKey') + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Connection $queue, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, string $mode, Document $developmentKey) use ($usageDatabaseListener, $eventDatabaseListener) { $route = $utopia->getRoute(); @@ -461,7 +461,7 @@ App::init() $enabled // Abuse is enabled && !$isAppUser // User is not API key && !$isPrivilegedUser // User is not an admin - && !$hasDevelopmentKey // request doesn't not contain development key + && $developmentKey->isEmpty() // request doesn't not contain development key && $abuse->check() // Route is rate-limited ) { throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED); diff --git a/app/init.php b/app/init.php index 1de0b14740..c93ffdae53 100644 --- a/app/init.php +++ b/app/init.php @@ -1814,14 +1814,14 @@ App::setResource('plan', function (array $plan = []) { return []; }); -App::setResource('hasDevelopmentKey', function ($request, $project, $dbForConsole) { +App::setResource('developmentKey', function ($request, $project, $dbForConsole) { $developmentKey = $request->getHeader('x-appwrite-development-key', ''); // Check if given key match project's development keys $key = $project->find('secret', $developmentKey, 'developmentKeys'); if ($key) { $expire = $key->getAttribute('expire'); if (!empty($expire) && $expire < DatabaseDateTime::formatTz(DatabaseDateTime::now())) { - return false; + return new Document([]); } $accessedAt = $key->getAttribute('accessedAt', ''); @@ -1830,9 +1830,9 @@ App::setResource('hasDevelopmentKey', function ($request, $project, $dbForConsol Authorization::skip(fn () => $dbForConsole->updateDocument('keys', $key->getId(), $key)); $dbForConsole->purgeCachedDocument('projects', $project->getId()); } - return true; + return $key; } - return false; + return new Document([]); }, ['request', 'project', 'dbForConsole']); App::setResource('team', function (Document $project, Database $dbForConsole, App $utopia, Request $request) { @@ -1867,4 +1867,3 @@ App::setResource( 'isResourceBlocked', fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false ); - From 10dab10cc5e4e8622a02c0097afdd94d1fa0857b Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 17 Nov 2024 07:01:20 +0000 Subject: [PATCH 016/110] refactor module namings --- src/Appwrite/Platform/Appwrite.php | 2 +- .../CreateKey.php} | 6 +- .../DeleteKey.php} | 6 +- .../{Get.php => DevelopmentKeys/GetKey.php} | 6 +- .../ListKeys.php} | 6 +- .../Http/{ => DevelopmentKeys}/Update.php | 4 +- .../Http/DevelopmentKeys/UpdateKey.php | 72 +++++++++++++++++++ .../Module.php} | 6 +- .../Modules/DevelopmentKeys/Services/Http.php | 20 +++--- .../Projects/ProjectsDevelopmentKeys.php | 2 +- 10 files changed, 101 insertions(+), 29 deletions(-) rename src/Appwrite/Platform/Modules/DevelopmentKeys/Http/{Create.php => DevelopmentKeys/CreateKey.php} (95%) rename src/Appwrite/Platform/Modules/DevelopmentKeys/Http/{Delete.php => DevelopmentKeys/DeleteKey.php} (93%) rename src/Appwrite/Platform/Modules/DevelopmentKeys/Http/{Get.php => DevelopmentKeys/GetKey.php} (93%) rename src/Appwrite/Platform/Modules/DevelopmentKeys/Http/{XList.php => DevelopmentKeys/ListKeys.php} (93%) rename src/Appwrite/Platform/Modules/DevelopmentKeys/Http/{ => DevelopmentKeys}/Update.php (97%) create mode 100644 src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/UpdateKey.php rename src/Appwrite/Platform/Modules/{DevelopmentKeys.php => DevelopmentKeys/Module.php} (59%) diff --git a/src/Appwrite/Platform/Appwrite.php b/src/Appwrite/Platform/Appwrite.php index e1a27f1b0a..153b22c02c 100644 --- a/src/Appwrite/Platform/Appwrite.php +++ b/src/Appwrite/Platform/Appwrite.php @@ -11,6 +11,6 @@ class Appwrite extends Platform public function __construct() { parent::__construct(new Core()); - $this->addModule(new DevelopmentKeys()); + $this->addModule(new DevelopmentKeys\Module()); } } diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/CreateKey.php similarity index 95% rename from src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php rename to src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/CreateKey.php index ec898cde7d..c6eee58adb 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/Create.php +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/CreateKey.php @@ -1,6 +1,6 @@ setHttpMethod(Action::HTTP_REQUEST_METHOD_PUT) + ->setHttpPath('/v1/projects/:projectId/development-keys/:keyId') + ->desc('Update key') + ->groups(['api', 'projects']) + ->label('scope', 'projects.write') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'projects') + ->label('sdk.method', 'updateDevelopmentKey') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_KEY) + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('keyId', '', new UID(), 'Key unique ID.') + ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') + ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.', true) + ->inject('response') + ->inject('dbForConsole') + ->callback(fn ($projectId, $keyId, $name, $expire, $response, $dbForConsole) => $this->action($projectId, $keyId, $name, $expire, $response, $dbForConsole)); + } + public function action(string $projectId, string $keyId, string $name, ?string $expire, Response $response, Database $dbForConsole) + { + + $project = $dbForConsole->getDocument('projects', $projectId); + + if ($project->isEmpty()) { + throw new Exception(Exception::PROJECT_NOT_FOUND); + } + + $key = $dbForConsole->findOne('developmentKeys', [ + Query::equal('$id', [$keyId]), + Query::equal('projectInternalId', [$project->getInternalId()]), + ]); + + if ($key === false || $key->isEmpty()) { + throw new Exception(Exception::KEY_NOT_FOUND); + } + + $key + ->setAttribute('name', $name) + ->setAttribute('expire', $expire ?? $key->getAttribute('expire')); + + $dbForConsole->updateDocument('developmentKeys', $key->getId(), $key); + + $dbForConsole->purgeCachedDocument('projects', $project->getId()); + + $response->dynamic($key, Response::MODEL_KEY); + } +} diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys.php b/src/Appwrite/Platform/Modules/DevelopmentKeys/Module.php similarity index 59% rename from src/Appwrite/Platform/Modules/DevelopmentKeys.php rename to src/Appwrite/Platform/Modules/DevelopmentKeys/Module.php index c351ad92f1..299868cb57 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys.php +++ b/src/Appwrite/Platform/Modules/DevelopmentKeys/Module.php @@ -1,11 +1,11 @@ type = Service::TYPE_HTTP; - $this->addAction(Create::getName(), new Create()); - $this->addAction(Update::getName(), new Update()); - $this->addAction(Get::getName(), new Get()); - $this->addAction(XList::getName(), new XList()); - $this->addAction(Delete::getName(), new Delete()); + $this->addAction(CreateKey::getName(), new CreateKey()); + $this->addAction(UpdateKey::getName(), new UpdateKey()); + $this->addAction(GetKey::getName(), new GetKey()); + $this->addAction(ListKeys::getName(), new ListKeys()); + $this->addAction(DeleteKey::getName(), new DeleteKey()); } } diff --git a/tests/e2e/Services/Projects/ProjectsDevelopmentKeys.php b/tests/e2e/Services/Projects/ProjectsDevelopmentKeys.php index cbc677a7e1..f781aad6b2 100644 --- a/tests/e2e/Services/Projects/ProjectsDevelopmentKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevelopmentKeys.php @@ -131,7 +131,7 @@ trait ProjectsDevelopmentKeys $developmentKey = $response['body']['secret']; // - for($i = 0; $i < 11; $i++) { + for ($i = 0; $i < 11; $i++) { $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', 'x-appwrite-project' => $id, From cc3edd7b2bd443f5165b26007b84254b3269c744 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Fri, 22 Nov 2024 04:21:03 +0000 Subject: [PATCH 017/110] Refactor naming to Dev keys --- app/config/collections.php | 10 ++--- app/controllers/general.php | 8 ++-- app/controllers/shared/api.php | 6 +-- app/init.php | 10 ++--- src/Appwrite/Platform/Appwrite.php | 4 +- .../Http/DevKeys}/CreateKey.php | 4 +- .../Http/DevKeys}/DeleteKey.php | 8 ++-- .../Http/DevKeys}/GetKey.php | 6 +-- .../Http/DevKeys}/ListKeys.php | 4 +- .../Http/DevKeys}/Update.php | 8 ++-- .../Http/DevKeys}/UpdateKey.php | 8 ++-- .../{DevelopmentKeys => DevKeys}/Module.php | 4 +- .../Services/Http.php | 12 +++--- .../Projects/ProjectsConsoleClientTest.php | 4 +- ...evelopmentKeys.php => ProjectsDevKeys.php} | 38 +++++++++---------- 15 files changed, 67 insertions(+), 67 deletions(-) rename src/Appwrite/Platform/Modules/{DevelopmentKeys/Http/DevelopmentKeys => DevKeys/Http/DevKeys}/CreateKey.php (95%) rename src/Appwrite/Platform/Modules/{DevelopmentKeys/Http/DevelopmentKeys => DevKeys/Http/DevKeys}/DeleteKey.php (87%) rename src/Appwrite/Platform/Modules/{DevelopmentKeys/Http/DevelopmentKeys => DevKeys/Http/DevKeys}/GetKey.php (91%) rename src/Appwrite/Platform/Modules/{DevelopmentKeys/Http/DevelopmentKeys => DevKeys/Http/DevKeys}/ListKeys.php (93%) rename src/Appwrite/Platform/Modules/{DevelopmentKeys/Http/DevelopmentKeys => DevKeys/Http/DevKeys}/Update.php (90%) rename src/Appwrite/Platform/Modules/{DevelopmentKeys/Http/DevelopmentKeys => DevKeys/Http/DevKeys}/UpdateKey.php (90%) rename src/Appwrite/Platform/Modules/{DevelopmentKeys => DevKeys}/Module.php (59%) rename src/Appwrite/Platform/Modules/{DevelopmentKeys => DevKeys}/Services/Http.php (51%) rename tests/e2e/Services/Projects/{ProjectsDevelopmentKeys.php => ProjectsDevKeys.php} (90%) diff --git a/app/config/collections.php b/app/config/collections.php index f7403c7054..45e4501fe4 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -4520,7 +4520,7 @@ $consoleCollections = array_merge([ 'filters' => ['subQueryKeys'], ], [ - '$id' => ID::custom('developmentKeys'), + '$id' => ID::custom('devKeys'), 'type' => Database::VAR_STRING, 'format' => '', 'size' => 16384, @@ -4528,7 +4528,7 @@ $consoleCollections = array_merge([ 'required' => false, 'default' => null, 'array' => false, - 'filters' => ['subQueryDevelopmentKeys'], + 'filters' => ['subQueryDevKeys'], ], [ '$id' => ID::custom('search'), @@ -4932,10 +4932,10 @@ $consoleCollections = array_merge([ ], ], - 'developmentKeys' => [ + 'devKeys' => [ '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('developmentKeys'), - 'name' => 'keys', + '$id' => ID::custom('devKeys'), + 'name' => 'Dev keys', 'attributes' => [ [ '$id' => ID::custom('projectInternalId'), diff --git a/app/controllers/general.php b/app/controllers/general.php index cecbfdd7f5..e1da716d4c 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -710,8 +710,8 @@ App::error() ->inject('logger') ->inject('log') ->inject('queueForUsage') - ->inject('developmentKey') - ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Usage $queueForUsage, Document $developmentKey) { + ->inject('devKey') + ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Usage $queueForUsage, Document $devKey) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); $route = $utopia->getRoute(); $class = \get_class($error); @@ -912,7 +912,7 @@ App::error() $type = $error->getType(); - $output = ((App::isDevelopment()) || (!$developmentKey->isEmpty())) ? [ + $output = ((App::isDevelopment()) || (!$devKey->isEmpty())) ? [ 'message' => $message, 'code' => $code, 'file' => $file, @@ -953,7 +953,7 @@ App::error() $response->dynamic( new Document($output), - $utopia->isDevelopment() || !$developmentKey->isEmpty() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR + $utopia->isDevelopment() || !$devKey->isEmpty() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR ); }); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 70a97099b2..6b2abd228f 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -393,8 +393,8 @@ App::init() ->inject('queueForUsage') ->inject('dbForProject') ->inject('mode') - ->inject('developmentKey') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Connection $queue, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, string $mode, Document $developmentKey) use ($usageDatabaseListener, $eventDatabaseListener) { + ->inject('devKey') + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Connection $queue, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, string $mode, Document $devKey) use ($usageDatabaseListener, $eventDatabaseListener) { $route = $utopia->getRoute(); @@ -461,7 +461,7 @@ App::init() $enabled // Abuse is enabled && !$isAppUser // User is not API key && !$isPrivilegedUser // User is not an admin - && $developmentKey->isEmpty() // request doesn't not contain development key + && $devKey->isEmpty() // request doesn't not contain development key && $abuse->check() // Route is rate-limited ) { throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED); diff --git a/app/init.php b/app/init.php index c93ffdae53..2056cdca20 100644 --- a/app/init.php +++ b/app/init.php @@ -477,13 +477,13 @@ Database::addFilter( ); Database::addFilter( - 'subQueryDevelopmentKeys', + 'subQueryDevKeys', function (mixed $value) { return; }, function (mixed $value, Document $document, Database $database) { return $database - ->find('developmentKeys', [ + ->find('devKeys', [ Query::equal('projectInternalId', [$document->getInternalId()]), Query::limit(APP_LIMIT_SUBQUERY), ]); @@ -1814,10 +1814,10 @@ App::setResource('plan', function (array $plan = []) { return []; }); -App::setResource('developmentKey', function ($request, $project, $dbForConsole) { - $developmentKey = $request->getHeader('x-appwrite-development-key', ''); +App::setResource('devKey', function ($request, $project, $dbForConsole) { + $devKey = $request->getHeader('x-appwrite-development-key', ''); // Check if given key match project's development keys - $key = $project->find('secret', $developmentKey, 'developmentKeys'); + $key = $project->find('secret', $devKey, 'devKeys'); if ($key) { $expire = $key->getAttribute('expire'); if (!empty($expire) && $expire < DatabaseDateTime::formatTz(DatabaseDateTime::now())) { diff --git a/src/Appwrite/Platform/Appwrite.php b/src/Appwrite/Platform/Appwrite.php index 153b22c02c..ec19b3248c 100644 --- a/src/Appwrite/Platform/Appwrite.php +++ b/src/Appwrite/Platform/Appwrite.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform; use Appwrite\Platform\Modules\Core; -use Appwrite\Platform\Modules\DevelopmentKeys; +use Appwrite\Platform\Modules\DevKeys; use Utopia\Platform\Platform; class Appwrite extends Platform @@ -11,6 +11,6 @@ class Appwrite extends Platform public function __construct() { parent::__construct(new Core()); - $this->addModule(new DevelopmentKeys\Module()); + $this->addModule(new DevKeys\Module()); } } diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/CreateKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php similarity index 95% rename from src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/CreateKey.php rename to src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php index c6eee58adb..2e1c4db7f2 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/CreateKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php @@ -1,6 +1,6 @@ \bin2hex(\random_bytes(128)), ]); - $key = $dbForConsole->createDocument('developmentKeys', $key); + $key = $dbForConsole->createDocument('devKeys', $key); $dbForConsole->purgeCachedDocument('projects', $project->getId()); diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/DeleteKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php similarity index 87% rename from src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/DeleteKey.php rename to src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php index 493f3e8457..1d5b2a324d 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/DeleteKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php @@ -1,6 +1,6 @@ label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'deleteDevelopmentKey') + ->label('sdk.method', 'deleteDevKey') ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) ->param('projectId', '', new UID(), 'Project unique ID.') @@ -47,7 +47,7 @@ class DeleteKey extends Action throw new Exception(Exception::PROJECT_NOT_FOUND); } - $key = $dbForConsole->findOne('developmentKeys', [ + $key = $dbForConsole->findOne('devKeys', [ Query::equal('$id', [$keyId]), Query::equal('projectInternalId', [$project->getInternalId()]), ]); @@ -56,7 +56,7 @@ class DeleteKey extends Action throw new Exception(Exception::KEY_NOT_FOUND); } - $dbForConsole->deleteDocument('developmentKeys', $key->getId()); + $dbForConsole->deleteDocument('devKeys', $key->getId()); $dbForConsole->purgeCachedDocument('projects', $project->getId()); diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/GetKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php similarity index 91% rename from src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/GetKey.php rename to src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php index 218c657ea4..4dc95bf091 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/GetKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php @@ -1,6 +1,6 @@ label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'getDevelopmentKey') + ->label('sdk.method', 'getDevKey') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_KEY) @@ -48,7 +48,7 @@ class GetKey extends Action throw new Exception(Exception::PROJECT_NOT_FOUND); } - $key = $dbForConsole->findOne('developmentKeys', [ + $key = $dbForConsole->findOne('devKeys', [ Query::equal('$id', [$keyId]), Query::equal('projectInternalId', [$project->getInternalId()]), ]); diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/ListKeys.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php similarity index 93% rename from src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/ListKeys.php rename to src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php index 5dc6872471..a6461014c8 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/ListKeys.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php @@ -1,6 +1,6 @@ find('developmentKeys', [ + $keys = $dbForConsole->find('devKeys', [ Query::equal('projectInternalId', [$project->getInternalId()]), Query::limit(5000), ]); diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/Update.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/Update.php similarity index 90% rename from src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/Update.php rename to src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/Update.php index ee61bcb087..ef2da333f9 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/Update.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/Update.php @@ -1,6 +1,6 @@ label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'updateDevelopmentKey') + ->label('sdk.method', 'updateDevKey') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_KEY) @@ -50,7 +50,7 @@ class UpdateKey extends Action throw new Exception(Exception::PROJECT_NOT_FOUND); } - $key = $dbForConsole->findOne('developmentKeys', [ + $key = $dbForConsole->findOne('devKeys', [ Query::equal('$id', [$keyId]), Query::equal('projectInternalId', [$project->getInternalId()]), ]); @@ -63,7 +63,7 @@ class UpdateKey extends Action ->setAttribute('name', $name) ->setAttribute('expire', $expire ?? $key->getAttribute('expire')); - $dbForConsole->updateDocument('developmentKeys', $key->getId(), $key); + $dbForConsole->updateDocument('devKeys', $key->getId(), $key); $dbForConsole->purgeCachedDocument('projects', $project->getId()); diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/UpdateKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php similarity index 90% rename from src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/UpdateKey.php rename to src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php index 4d6a0785af..16906e92b3 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Http/DevelopmentKeys/UpdateKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php @@ -1,6 +1,6 @@ label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'updateDevelopmentKey') + ->label('sdk.method', 'updateDevKey') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_KEY) @@ -50,7 +50,7 @@ class UpdateKey extends Action throw new Exception(Exception::PROJECT_NOT_FOUND); } - $key = $dbForConsole->findOne('developmentKeys', [ + $key = $dbForConsole->findOne('devKeys', [ Query::equal('$id', [$keyId]), Query::equal('projectInternalId', [$project->getInternalId()]), ]); @@ -63,7 +63,7 @@ class UpdateKey extends Action ->setAttribute('name', $name) ->setAttribute('expire', $expire ?? $key->getAttribute('expire')); - $dbForConsole->updateDocument('developmentKeys', $key->getId(), $key); + $dbForConsole->updateDocument('devKeys', $key->getId(), $key); $dbForConsole->purgeCachedDocument('projects', $project->getId()); diff --git a/src/Appwrite/Platform/Modules/DevelopmentKeys/Module.php b/src/Appwrite/Platform/Modules/DevKeys/Module.php similarity index 59% rename from src/Appwrite/Platform/Modules/DevelopmentKeys/Module.php rename to src/Appwrite/Platform/Modules/DevKeys/Module.php index 299868cb57..796ec50186 100644 --- a/src/Appwrite/Platform/Modules/DevelopmentKeys/Module.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Module.php @@ -1,8 +1,8 @@ DateTime::addSeconds(new \DateTime(), 3600), ]); - $developmentKey = $response['body']['secret']; + $devKey = $response['body']['secret']; // for ($i = 0; $i < 11; $i++) { @@ -152,7 +152,7 @@ trait ProjectsDevelopmentKeys $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', 'x-appwrite-project' => $id, - 'x-appwrite-development-key' => $developmentKey + 'x-appwrite-development-key' => $devKey ], [ 'email' => 'user@appwrite.io', 'password' => 'password' @@ -184,10 +184,10 @@ trait ProjectsDevelopmentKeys /** - * @depends testCreateProjectDevelopmentKey - * @group developmentKeys + * @depends testCreateProjectDevKey + * @group devKeys */ - public function testUpdateProjectDevelopmentKey($data): array + public function testUpdateProjectDevKey($data): array { $id = $data['projectId'] ?? ''; $keyId = $data['keyId'] ?? ''; @@ -227,10 +227,10 @@ trait ProjectsDevelopmentKeys } /** - * @depends testCreateProjectDevelopmentKey - * @group developmentKeys + * @depends testCreateProjectDevKey + * @group devKeys */ - public function testDeleteProjectDevelopmentKey($data): array + public function testDeleteProjectDevKey($data): array { $id = $data['projectId'] ?? ''; $keyId = $data['keyId'] ?? ''; From b3c2f8a0247c65d333840dcc8268caddab5888a6 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Fri, 22 Nov 2024 05:55:39 +0000 Subject: [PATCH 018/110] disable CORS --- app/controllers/general.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index e1da716d4c..0479ceb083 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -458,7 +458,8 @@ App::init() ->inject('queueForCertificates') ->inject('queueForFunctions') ->inject('isResourceBlocked') - ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, callable $isResourceBlocked) { + ->inject('devKey') + ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, callable $isResourceBlocked, Document $devKey) { /* * Appwrite Router */ @@ -656,6 +657,7 @@ App::init() if ( !$originValidator->isValid($origin) + && $devKey->isEmpty() && \in_array($request->getMethod(), [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH, Request::METHOD_DELETE]) && $route->getLabel('origin', false) !== '*' && empty($request->getHeader('x-appwrite-key', '')) @@ -676,7 +678,8 @@ App::options() ->inject('queueForFunctions') ->inject('geodb') ->inject('isResourceBlocked') - ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked) { + ->inject('devKey') + ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, Document $devKey) { /* * Appwrite Router */ @@ -696,7 +699,7 @@ App::options() ->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE') ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent') ->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies') - ->addHeader('Access-Control-Allow-Origin', $origin) + ->addHeader('Access-Control-Allow-Origin', $devKey->isEmpty() ? $origin : '*') ->addHeader('Access-Control-Allow-Credentials', 'true') ->noContent(); }); From 010ace3b57ab3abfbee866325958f4ee57b24bda Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Fri, 22 Nov 2024 06:56:36 +0000 Subject: [PATCH 019/110] update redirect validator --- app/controllers/api/account.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 76a3ef8b61..82b3d918a6 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1109,8 +1109,8 @@ App::get('/v1/account/sessions/oauth2/:provider') ->label('abuse-limit', 50) ->label('abuse-key', 'ip:{ip}') ->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('oAuthProviders'), fn ($node) => (!$node['mock'])))) . '.') - ->param('success', '', fn ($clients) => new Host($clients), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) - ->param('failure', '', fn ($clients) => new Host($clients), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) + ->param('success', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey']) + ->param('failure', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey']) ->param('scopes', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) ->inject('request') ->inject('response') From 2c6e5d0bea5463d322f7d77f2680f368eebd8de9 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 28 Nov 2024 01:43:21 +0000 Subject: [PATCH 020/110] remove old file --- .../Modules/DevKeys/Http/DevKeys/Update.php | 72 ------------------- 1 file changed, 72 deletions(-) delete mode 100644 src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/Update.php diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/Update.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/Update.php deleted file mode 100644 index ef2da333f9..0000000000 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/Update.php +++ /dev/null @@ -1,72 +0,0 @@ -setHttpMethod(Action::HTTP_REQUEST_METHOD_PUT) - ->setHttpPath('/v1/projects/:projectId/development-keys/:keyId') - ->desc('Update key') - ->groups(['api', 'projects']) - ->label('scope', 'projects.write') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'updateDevKey') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_KEY) - ->param('projectId', '', new UID(), 'Project unique ID.') - ->param('keyId', '', new UID(), 'Key unique ID.') - ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') - ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.', true) - ->inject('response') - ->inject('dbForConsole') - ->callback(fn ($projectId, $keyId, $name, $expire, $response, $dbForConsole) => $this->action($projectId, $keyId, $name, $expire, $response, $dbForConsole)); - } - public function action(string $projectId, string $keyId, string $name, ?string $expire, Response $response, Database $dbForConsole) - { - - $project = $dbForConsole->getDocument('projects', $projectId); - - if ($project->isEmpty()) { - throw new Exception(Exception::PROJECT_NOT_FOUND); - } - - $key = $dbForConsole->findOne('devKeys', [ - Query::equal('$id', [$keyId]), - Query::equal('projectInternalId', [$project->getInternalId()]), - ]); - - if ($key === false || $key->isEmpty()) { - throw new Exception(Exception::KEY_NOT_FOUND); - } - - $key - ->setAttribute('name', $name) - ->setAttribute('expire', $expire ?? $key->getAttribute('expire')); - - $dbForConsole->updateDocument('devKeys', $key->getId(), $key); - - $dbForConsole->purgeCachedDocument('projects', $project->getId()); - - $response->dynamic($key, Response::MODEL_KEY); - } -} From 8206cee44928ffdb1e885fd9ec6cfd3903e32297 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 4 Dec 2024 10:57:49 +0545 Subject: [PATCH 021/110] Update tests/e2e/Services/Projects/ProjectsDevKeys.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matej Bačo --- tests/e2e/Services/Projects/ProjectsDevKeys.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index 97eb9297a4..7c8149bbcc 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -157,7 +157,7 @@ trait ProjectsDevKeys 'email' => 'user@appwrite.io', 'password' => 'password' ]); - $this->assertEquals('401', $res['headers']['status-code']); + $this->assertEquals(401, $res['headers']['status-code']); /** From b8917bc8115d07a45a2333082547f0c5ed8b5da6 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 4 Dec 2024 10:58:05 +0545 Subject: [PATCH 022/110] Update tests/e2e/Services/Projects/ProjectsDevKeys.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matej Bačo --- tests/e2e/Services/Projects/ProjectsDevKeys.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index 7c8149bbcc..3fbe569d29 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -147,7 +147,7 @@ trait ProjectsDevKeys 'email' => 'user@appwrite.io', 'password' => 'password' ]); - $this->assertEquals('429', $res['headers']['status-code']); + $this->assertEquals(429, $res['headers']['status-code']); $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', From 8a59a22c2e0a25af066833a830198dda95c1676d Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 4 Dec 2024 05:25:16 +0000 Subject: [PATCH 023/110] update suggested changes --- app/controllers/api/account.php | 10 +++++----- app/controllers/api/teams.php | 2 +- app/controllers/general.php | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 82b3d918a6..8349142c14 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1698,8 +1698,8 @@ App::get('/v1/account/tokens/oauth2/:provider') ->label('abuse-limit', 50) ->label('abuse-key', 'ip:{ip}') ->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('oAuthProviders'), fn ($node) => (!$node['mock'])))) . '.') - ->param('success', '', fn ($clients) => new Host($clients), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) - ->param('failure', '', fn ($clients) => new Host($clients), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) + ->param('success', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey']) + ->param('failure', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey']) ->param('scopes', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) ->inject('request') ->inject('response') @@ -1772,7 +1772,7 @@ App::post('/v1/account/tokens/magic-url') ->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}']) ->param('userId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('email', '', new Email(), 'User email.') - ->param('url', '', fn ($clients) => new Host($clients), 'URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) + ->param('url', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey']) ->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true) ->inject('request') ->inject('response') @@ -2969,7 +2969,7 @@ App::post('/v1/account/recovery') ->label('abuse-limit', 10) ->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}']) ->param('email', '', new Email(), 'User email.') - ->param('url', '', fn ($clients) => new Host($clients), 'URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) + ->param('url', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients', 'devKey']) ->inject('request') ->inject('response') ->inject('user') @@ -3232,7 +3232,7 @@ App::post('/v1/account/verification') ->label('sdk.response.model', Response::MODEL_TOKEN) ->label('abuse-limit', 10) ->label('abuse-key', 'url:{url},userId:{userId}') - ->param('url', '', fn ($clients) => new Host($clients), 'URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) // TODO add built-in confirm page + ->param('url', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients', 'devKey']) // TODO add built-in confirm page ->inject('request') ->inject('response') ->inject('project') diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index f829800b98..92e4a2b1dd 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -413,7 +413,7 @@ App::post('/v1/teams/:teamId/memberships') } return new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE); }, 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project']) - ->param('url', '', fn ($clients) => new Host($clients), 'URL to redirect the user back to your app from the invitation email. This parameter is not required when an API key is supplied. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) // TODO add our own built-in confirm page + ->param('url', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect the user back to your app from the invitation email. This parameter is not required when an API key is supplied. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey']) // TODO add our own built-in confirm page ->param('name', '', new Text(128), 'Name of the new team member. Max length: 128 chars.', true) ->inject('response') ->inject('project') diff --git a/app/controllers/general.php b/app/controllers/general.php index dbe58d1bcb..23d1b75b1b 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -672,7 +672,7 @@ App::init() ->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE') ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Forwarded-For, X-Forwarded-User-Agent') ->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies') - ->addHeader('Access-Control-Allow-Origin', $refDomain) + ->addHeader('Access-Control-Allow-Origin', $devKey->isEmpty() ? $refDomain : "*") ->addHeader('Access-Control-Allow-Credentials', 'true'); /* From fb0da77d2f5f9749bb600cc1968f5062f7ca12b1 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 4 Dec 2024 05:26:52 +0000 Subject: [PATCH 024/110] add todo --- app/controllers/general.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/general.php b/app/controllers/general.php index 23d1b75b1b..56d9c3f6f2 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -947,6 +947,8 @@ App::error() $type = $error->getType(); + // TODO filter out secrets and server details when not in development + // but devKey is provided $output = ((App::isDevelopment()) || (!$devKey->isEmpty())) ? [ 'message' => $message, 'code' => $code, From e536e6ea745d3e7a1adec900de6cc4a581f4b76f Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 4 Dec 2024 05:29:13 +0000 Subject: [PATCH 025/110] support dev key from param as well --- app/init.php | 4 ++-- tests/e2e/Services/Projects/ProjectsDevKeys.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/init.php b/app/init.php index 9dc5e28e80..c16b47961b 100644 --- a/app/init.php +++ b/app/init.php @@ -1811,8 +1811,8 @@ App::setResource('plan', function (array $plan = []) { return []; }); -App::setResource('devKey', function ($request, $project, $dbForConsole) { - $devKey = $request->getHeader('x-appwrite-development-key', ''); +App::setResource('devKey', function (Request $request, Document $project, Database $dbForConsole) { + $devKey = $request->getHeader('x-appwrite-dev-key', $request->getParam('devKey', '')); // Check if given key match project's development keys $key = $project->find('secret', $devKey, 'devKeys'); if ($key) { diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index 3fbe569d29..86eff481ae 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -152,7 +152,7 @@ trait ProjectsDevKeys $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', 'x-appwrite-project' => $id, - 'x-appwrite-development-key' => $devKey + 'x-appwrite-dev-key' => $devKey ], [ 'email' => 'user@appwrite.io', 'password' => 'password' @@ -174,7 +174,7 @@ trait ProjectsDevKeys $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', 'x-appwrite-project' => $id, - 'x-appwrite-development-key' => $response['body']['secret'] + 'x-appwrite-dev-key' => $response['body']['secret'] ], [ 'email' => 'user@appwrite.io', 'password' => 'password' From ec14cc170fbf7d14136f73c41976c2e36f7c7621 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 4 Dec 2024 05:40:09 +0000 Subject: [PATCH 026/110] fixing response model --- .../DevKeys/Http/DevKeys/CreateKey.php | 8 +- .../DevKeys/Http/DevKeys/DeleteKey.php | 2 +- .../Modules/DevKeys/Http/DevKeys/GetKey.php | 6 +- .../Modules/DevKeys/Http/DevKeys/ListKeys.php | 8 +- .../DevKeys/Http/DevKeys/UpdateKey.php | 6 +- src/Appwrite/Utopia/Response.php | 5 ++ src/Appwrite/Utopia/Response/Model/DevKey.php | 82 +++++++++++++++++++ 7 files changed, 102 insertions(+), 15 deletions(-) create mode 100644 src/Appwrite/Utopia/Response/Model/DevKey.php diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php index 2e1c4db7f2..68ca88a58a 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php @@ -28,15 +28,15 @@ class CreateKey extends Action $this ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/projects/:projectId/development-keys') - ->desc('Create key') + ->desc('Create dev key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'createKey') + ->label('sdk.method', 'createDevKey') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_KEY) + ->label('sdk.response.model', Response::MODEL_DEV_KEY) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.', false) @@ -75,6 +75,6 @@ class CreateKey extends Action $response ->setStatusCode(Response::STATUS_CODE_CREATED) - ->dynamic($key, Response::MODEL_KEY); + ->dynamic($key, Response::MODEL_DEV_KEY); } } diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php index 1d5b2a324d..fcc6083340 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php @@ -23,7 +23,7 @@ class DeleteKey extends Action $this ->setHttpMethod(Action::HTTP_REQUEST_METHOD_DELETE) ->setHttpPath('/v1/projects/:projectId/development-keys/:keyId') - ->desc('Delete key') + ->desc('Delete dev key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php index 4dc95bf091..f9c542c6b2 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php @@ -23,7 +23,7 @@ class GetKey extends Action $this ->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/projects/:projectId/development-keys/:keyId') - ->desc('Get key') + ->desc('Get dev key') ->groups(['api', 'projects']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -31,7 +31,7 @@ class GetKey extends Action ->label('sdk.method', 'getDevKey') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_KEY) + ->label('sdk.response.model', Response::MODEL_DEV_KEY) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('keyId', '', new UID(), 'Key unique ID.') ->inject('response') @@ -57,6 +57,6 @@ class GetKey extends Action throw new Exception(Exception::KEY_NOT_FOUND); } - $response->dynamic($key, Response::MODEL_KEY); + $response->dynamic($key, Response::MODEL_DEV_KEY); } } diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php index a6461014c8..a4b4083080 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php @@ -24,15 +24,15 @@ class ListKeys extends Action $this ->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/projects/:projectId/development-keys') - ->desc('List keys') + ->desc('List dev keys') ->groups(['api', 'projects']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'listKeys') + ->label('sdk.method', 'listDevKeys') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_KEY_LIST) + ->label('sdk.response.model', Response::MODEL_DEV_KEY_LIST) ->param('projectId', '', new UID(), 'Project unique ID.') ->inject('response') ->inject('dbForConsole') @@ -56,6 +56,6 @@ class ListKeys extends Action $response->dynamic(new Document([ 'keys' => $keys, 'total' => count($keys), - ]), Response::MODEL_KEY_LIST); + ]), Response::MODEL_DEV_KEY_LIST); } } diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php index 16906e92b3..5c6ded23c0 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php @@ -24,7 +24,7 @@ class UpdateKey extends Action { $this->setHttpMethod(Action::HTTP_REQUEST_METHOD_PUT) ->setHttpPath('/v1/projects/:projectId/development-keys/:keyId') - ->desc('Update key') + ->desc('Update dev key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -32,7 +32,7 @@ class UpdateKey extends Action ->label('sdk.method', 'updateDevKey') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_KEY) + ->label('sdk.response.model', Response::MODEL_DEV_KEY) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('keyId', '', new UID(), 'Key unique ID.') ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') @@ -67,6 +67,6 @@ class UpdateKey extends Action $dbForConsole->purgeCachedDocument('projects', $project->getId()); - $response->dynamic($key, Response::MODEL_KEY); + $response->dynamic($key, Response::MODEL_DEV_KEY); } } diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 6cc2639f51..c196331346 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -39,6 +39,7 @@ use Appwrite\Utopia\Response\Model\Currency; use Appwrite\Utopia\Response\Model\Database; use Appwrite\Utopia\Response\Model\Deployment; use Appwrite\Utopia\Response\Model\Detection; +use Appwrite\Utopia\Response\Model\DevKey; use Appwrite\Utopia\Response\Model\Document as ModelDocument; use Appwrite\Utopia\Response\Model\Error; use Appwrite\Utopia\Response\Model\ErrorDev; @@ -282,6 +283,8 @@ class Response extends SwooleResponse public const MODEL_WEBHOOK_LIST = 'webhookList'; public const MODEL_KEY = 'key'; public const MODEL_KEY_LIST = 'keyList'; + public const MODEL_DEV_KEY = 'devKey'; + public const MODEL_DEV_KEY_LIST = 'devKeyList'; public const MODEL_MOCK_NUMBER = 'mockNumber'; public const MODEL_AUTH_PROVIDER = 'authProvider'; public const MODEL_AUTH_PROVIDER_LIST = 'authProviderList'; @@ -363,6 +366,7 @@ class Response extends SwooleResponse ->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT, true, false)) ->setModel(new BaseList('Webhooks List', self::MODEL_WEBHOOK_LIST, 'webhooks', self::MODEL_WEBHOOK, true, false)) ->setModel(new BaseList('API Keys List', self::MODEL_KEY_LIST, 'keys', self::MODEL_KEY, true, false)) + ->setModel(new BaseList('Dev Keys List', self::MODEL_DEV_KEY_LIST, 'keys', self::MODEL_DEV_KEY, true, false)) ->setModel(new BaseList('Auth Providers List', self::MODEL_AUTH_PROVIDER_LIST, 'platforms', self::MODEL_AUTH_PROVIDER, true, false)) ->setModel(new BaseList('Platforms List', self::MODEL_PLATFORM_LIST, 'platforms', self::MODEL_PLATFORM, true, false)) ->setModel(new BaseList('Countries List', self::MODEL_COUNTRY_LIST, 'countries', self::MODEL_COUNTRY)) @@ -438,6 +442,7 @@ class Response extends SwooleResponse ->setModel(new Project()) ->setModel(new Webhook()) ->setModel(new Key()) + ->setModel(new DevKey()) ->setModel(new MockNumber()) ->setModel(new AuthProvider()) ->setModel(new Platform()) diff --git a/src/Appwrite/Utopia/Response/Model/DevKey.php b/src/Appwrite/Utopia/Response/Model/DevKey.php new file mode 100644 index 0000000000..50ce750b6a --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/DevKey.php @@ -0,0 +1,82 @@ +addRule('$id', [ + 'type' => self::TYPE_STRING, + 'description' => 'Key ID.', + 'default' => '', + 'example' => '5e5ea5c16897e', + ]) + ->addRule('$createdAt', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Key creation date in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE, + ]) + ->addRule('$updatedAt', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Key update date in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE, + ]) + ->addRule('name', [ + 'type' => self::TYPE_STRING, + 'description' => 'Key name.', + 'default' => '', + 'example' => 'My API Key', + ]) + ->addRule('expire', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Key expiration date in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE, + ]) + ->addRule('secret', [ + 'type' => self::TYPE_STRING, + 'description' => 'Secret key.', + 'default' => '', + 'example' => '919c2d18fb5d4...a2ae413da83346ad2', + ]) + ->addRule('accessedAt', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Most recent access date in ISO 8601 format. This attribute is only updated again after ' . APP_KEY_ACCESS / 60 / 60 . ' hours.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName(): string + { + return 'DevKey'; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_DEV_KEY; + } +} From 01f579151de5eb82f21989f05849714e4dc9cec1 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 4 Dec 2024 05:43:15 +0000 Subject: [PATCH 027/110] updated project model --- src/Appwrite/Utopia/Response/Model/Project.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Appwrite/Utopia/Response/Model/Project.php b/src/Appwrite/Utopia/Response/Model/Project.php index 6e01baee84..055fb07737 100644 --- a/src/Appwrite/Utopia/Response/Model/Project.php +++ b/src/Appwrite/Utopia/Response/Model/Project.php @@ -197,6 +197,13 @@ class Project extends Model 'example' => new \stdClass(), 'array' => true, ]) + ->addRule('devKeys', [ + 'type' => Response::MODEL_DEV_KEY, + 'description' => 'List of dev keys.', + 'default' => [], + 'example' => new \stdClass(), + 'array' => true, + ]) ->addRule('smtpEnabled', [ 'type' => self::TYPE_BOOLEAN, 'description' => 'Status for custom SMTP', From 136cc5fe46ccaf622973b9211ad6c28dc50ebfb3 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 4 Dec 2024 05:45:43 +0000 Subject: [PATCH 028/110] update put request --- .../Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php index 5c6ded23c0..de29b40960 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php @@ -36,7 +36,7 @@ class UpdateKey extends Action ->param('projectId', '', new UID(), 'Project unique ID.') ->param('keyId', '', new UID(), 'Key unique ID.') ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') - ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.', true) + ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.') ->inject('response') ->inject('dbForConsole') ->callback(fn ($projectId, $keyId, $name, $expire, $response, $dbForConsole) => $this->action($projectId, $keyId, $name, $expire, $response, $dbForConsole)); @@ -61,7 +61,7 @@ class UpdateKey extends Action $key ->setAttribute('name', $name) - ->setAttribute('expire', $expire ?? $key->getAttribute('expire')); + ->setAttribute('expire', $expire); $dbForConsole->updateDocument('devKeys', $key->getId(), $key); From e7943e8490f2ada8f520c0443b8367572e0b3441 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 4 Dec 2024 05:57:08 +0000 Subject: [PATCH 029/110] update test --- .../e2e/Services/Projects/ProjectsDevKeys.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index 86eff481ae..ff5e033cd2 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -130,7 +130,6 @@ trait ProjectsDevKeys $devKey = $response['body']['secret']; - // for ($i = 0; $i < 11; $i++) { $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', @@ -139,6 +138,7 @@ trait ProjectsDevKeys 'email' => 'user@appwrite.io', 'password' => 'password' ]); + $this->assertEquals(200, $res['headers']['status-code']); } $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', @@ -179,7 +179,7 @@ trait ProjectsDevKeys 'email' => 'user@appwrite.io', 'password' => 'password' ]); - $this->assertEquals('429', $res['headers']['status-code']); + $this->assertEquals(429, $res['headers']['status-code']); } @@ -243,12 +243,26 @@ trait ProjectsDevKeys $this->assertEquals(204, $response['headers']['status-code']); $this->assertEmpty($response['body']); + /** + * Get rate limit trying to use the deleted key + */ + $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $id, + 'x-appwrite-dev-key' => $response['body']['secret'] + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals(429, $res['headers']['status-code']); + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/development-keys/' . $keyId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); $this->assertEquals(404, $response['headers']['status-code']); + /** * Test for FAILURE From cb1aff42c58cc56b25a36b2655564a1988033ecb Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 4 Dec 2024 05:59:03 +0000 Subject: [PATCH 030/110] make 10 requests --- tests/e2e/Services/Projects/ProjectsDevKeys.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index ff5e033cd2..9c6e062f03 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -130,7 +130,7 @@ trait ProjectsDevKeys $devKey = $response['body']['secret']; - for ($i = 0; $i < 11; $i++) { + for ($i = 0; $i < 10; $i++) { $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', 'x-appwrite-project' => $id, From f21281ebf531c19e35fc39fd94f035e4d1643f6f Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 4 Dec 2024 06:02:21 +0000 Subject: [PATCH 031/110] format --- tests/e2e/Services/Projects/ProjectsDevKeys.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index 9c6e062f03..e1eddc8957 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -255,14 +255,14 @@ trait ProjectsDevKeys 'password' => 'password' ]); $this->assertEquals(429, $res['headers']['status-code']); - + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/development-keys/' . $keyId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); $this->assertEquals(404, $response['headers']['status-code']); - + /** * Test for FAILURE From 82d64c8daf12834031875225229ab1748faa1d97 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 8 Dec 2024 08:45:03 +0000 Subject: [PATCH 032/110] fix test --- tests/e2e/Services/Projects/ProjectsDevKeys.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index e1eddc8957..4a561fdf76 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -27,8 +27,6 @@ trait ProjectsDevKeys $this->assertNotEmpty($response['body']['$id']); $this->assertEquals('Key Test', $response['body']['name']); $this->assertNotEmpty($response['body']['secret']); - $this->assertArrayHasKey('sdks', $response['body']); - $this->assertEmpty($response['body']['sdks']); $this->assertArrayHasKey('accessedAt', $response['body']); $this->assertEmpty($response['body']['accessedAt']); @@ -204,8 +202,6 @@ trait ProjectsDevKeys $this->assertNotEmpty($response['body']['$id']); $this->assertEquals($keyId, $response['body']['$id']); $this->assertEquals('Key Test Update', $response['body']['name']); - $this->assertArrayHasKey('sdks', $response['body']); - $this->assertEmpty($response['body']['sdks']); $this->assertArrayHasKey('accessedAt', $response['body']); $this->assertEmpty($response['body']['accessedAt']); @@ -218,8 +214,6 @@ trait ProjectsDevKeys $this->assertNotEmpty($response['body']['$id']); $this->assertEquals($keyId, $response['body']['$id']); $this->assertEquals('Key Test Update', $response['body']['name']); - $this->assertArrayHasKey('sdks', $response['body']); - $this->assertEmpty($response['body']['sdks']); $this->assertArrayHasKey('accessedAt', $response['body']); $this->assertEmpty($response['body']['accessedAt']); From 30a079a6b991de3966fe28e91fc2c0028b82c3cf Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 8 Dec 2024 09:07:55 +0000 Subject: [PATCH 033/110] fix missing key --- tests/e2e/Services/Projects/ProjectsDevKeys.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index 4a561fdf76..a57a886d4c 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -89,8 +89,6 @@ trait ProjectsDevKeys $this->assertEquals($keyId, $response['body']['$id']); $this->assertEquals('Key Test', $response['body']['name']); $this->assertNotEmpty($response['body']['secret']); - $this->assertArrayHasKey('sdks', $response['body']); - $this->assertEmpty($response['body']['sdks']); $this->assertArrayHasKey('accessedAt', $response['body']); $this->assertEmpty($response['body']['accessedAt']); From 5627082165a31b3319fa9a9b9a625195ad43e065 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 8 Dec 2024 09:45:40 +0000 Subject: [PATCH 034/110] update --- tests/e2e/Services/Projects/ProjectsDevKeys.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index a57a886d4c..8398bcfa7e 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -241,7 +241,7 @@ trait ProjectsDevKeys $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', 'x-appwrite-project' => $id, - 'x-appwrite-dev-key' => $response['body']['secret'] + 'x-appwrite-dev-key' => $data['secret'] ], [ 'email' => 'user@appwrite.io', 'password' => 'password' From dcacb5ee2cf46f972b44adfb5975a1deb9b174c1 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 15 Dec 2024 08:45:09 +0000 Subject: [PATCH 035/110] test fix --- tests/e2e/Services/Projects/ProjectsDevKeys.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index 8398bcfa7e..b8af5cfda7 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -2,6 +2,7 @@ namespace Tests\E2E\Services\Projects; +use Appwrite\ID; use Tests\E2E\Client; use Utopia\Database\DateTime; @@ -126,7 +127,7 @@ trait ProjectsDevKeys $devKey = $response['body']['secret']; - for ($i = 0; $i < 10; $i++) { + for ($i = 0; $i < 11; $i++) { $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', 'x-appwrite-project' => $id, @@ -134,7 +135,7 @@ trait ProjectsDevKeys 'email' => 'user@appwrite.io', 'password' => 'password' ]); - $this->assertEquals(200, $res['headers']['status-code']); + $this->assertEquals(401, $res['headers']['status-code']); } $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', From 172ef61663e2c463ef1033bab36f428d6caf462d Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 15 Dec 2024 08:54:10 +0000 Subject: [PATCH 036/110] remove devkey check on error --- app/controllers/general.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 2940b33447..842a3543e3 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -743,7 +743,7 @@ App::error() ->inject('log') ->inject('queueForUsage') ->inject('devKey') - ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Usage $queueForUsage, Document $devKey) { + ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Usage $queueForUsage) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); $route = $utopia->getRoute(); $class = \get_class($error); @@ -949,7 +949,7 @@ App::error() // TODO filter out secrets and server details when not in development // but devKey is provided - $output = ((App::isDevelopment()) || (!$devKey->isEmpty())) ? [ + $output = (App::isDevelopment()) ? [ 'message' => $message, 'code' => $code, 'file' => $file, @@ -990,7 +990,7 @@ App::error() $response->dynamic( new Document($output), - $utopia->isDevelopment() || !$devKey->isEmpty() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR + $utopia->isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR ); }); From ae0e1eb30250f44ba957fa87637dfe427c0a2613 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 15 Dec 2024 08:54:13 +0000 Subject: [PATCH 037/110] fix lint --- tests/e2e/Services/Projects/ProjectsDevKeys.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index b8af5cfda7..5cde1a6d8f 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -2,7 +2,6 @@ namespace Tests\E2E\Services\Projects; -use Appwrite\ID; use Tests\E2E\Client; use Utopia\Database\DateTime; From b25772483beb764f233624a4a1ab468c30d0308c Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 15 Dec 2024 08:57:44 +0000 Subject: [PATCH 038/110] key that expires after 5 second --- .../e2e/Services/Projects/ProjectsDevKeys.php | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index 5cde1a6d8f..fc550270d9 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -176,6 +176,40 @@ trait ProjectsDevKeys 'password' => 'password' ]); $this->assertEquals(429, $res['headers']['status-code']); + + + /** + * Test for FAILURE after expire + */ + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/development-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 5), + ]); + + $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $id, + 'x-appwrite-dev-key' => $response['body']['secret'] + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals(401, $res['headers']['status-code']); + + sleep(5); + + $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $id, + 'x-appwrite-dev-key' => $response['body']['secret'] + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals(429, $res['headers']['status-code']); } From 4ffdc9c1037143aa5ddcf1d92e6a3c57d85a036e Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 15 Dec 2024 09:22:05 +0000 Subject: [PATCH 039/110] fix existing test by adding dev key --- tests/e2e/Scopes/ProjectCustom.php | 14 ++++++++++++++ tests/e2e/Scopes/SideClient.php | 9 +++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Scopes/ProjectCustom.php b/tests/e2e/Scopes/ProjectCustom.php index 7f84ace6f2..5b492cfc06 100644 --- a/tests/e2e/Scopes/ProjectCustom.php +++ b/tests/e2e/Scopes/ProjectCustom.php @@ -3,6 +3,7 @@ namespace Tests\E2E\Scopes; use Tests\E2E\Client; +use Utopia\Database\DateTime; use Utopia\Database\Helpers\ID; trait ProjectCustom @@ -103,6 +104,17 @@ trait ProjectCustom $this->assertNotEmpty($key['body']); $this->assertNotEmpty($key['body']['secret']); + $devKey = $this->client->call(Client::METHOD_POST, '/projects/' . $project['body']['$id'] . '/development-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 3600), + ]); + $this->assertEquals(201, $devKey['headers']['status-code']); + $this->assertNotEmpty($devKey['body']); + $this->assertNotEmpty($devKey['body']['secret']); + $webhook = $this->client->call(Client::METHOD_POST, '/projects/' . $project['body']['$id'] . '/webhooks', [ 'origin' => 'http://localhost', 'content-type' => 'application/json', @@ -143,9 +155,11 @@ trait ProjectCustom '$id' => $project['body']['$id'], 'name' => $project['body']['name'], 'apiKey' => $key['body']['secret'], + 'devKey' => $devKey['body']['secret'], 'webhookId' => $webhook['body']['$id'], 'signatureKey' => $webhook['body']['signatureKey'], ]; + if ($fresh) { return $project; } diff --git a/tests/e2e/Scopes/SideClient.php b/tests/e2e/Scopes/SideClient.php index 54f77a9747..205d5d4426 100644 --- a/tests/e2e/Scopes/SideClient.php +++ b/tests/e2e/Scopes/SideClient.php @@ -4,12 +4,17 @@ namespace Tests\E2E\Scopes; trait SideClient { - public function getHeaders(): array + public function getHeaders(bool $devKey = true): array { - return [ + $headers = [ 'origin' => 'http://localhost', 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $this->getUser()['session'], + ]; + if ($devKey) { + $headers['x-appwrite-dev-key'] = $this->getProject()['devKey']; + } + return $headers; } /** From 3ad865319c4f7d3839fe84679607515f1f6df6cb Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 15 Dec 2024 09:22:16 +0000 Subject: [PATCH 040/110] refactor: rename endpoint --- .../DevKeys/Http/DevKeys/CreateKey.php | 2 +- .../DevKeys/Http/DevKeys/DeleteKey.php | 2 +- .../Modules/DevKeys/Http/DevKeys/GetKey.php | 2 +- .../Modules/DevKeys/Http/DevKeys/ListKeys.php | 2 +- .../DevKeys/Http/DevKeys/UpdateKey.php | 2 +- tests/e2e/Scopes/ProjectCustom.php | 2 +- .../e2e/Services/Projects/ProjectsDevKeys.php | 26 +++++++++---------- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php index 68ca88a58a..fd23a9d7eb 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php @@ -27,7 +27,7 @@ class CreateKey extends Action { $this ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) - ->setHttpPath('/v1/projects/:projectId/development-keys') + ->setHttpPath('/v1/projects/:projectId/dev-keys') ->desc('Create dev key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php index fcc6083340..44a8a9da3e 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php @@ -22,7 +22,7 @@ class DeleteKey extends Action { $this ->setHttpMethod(Action::HTTP_REQUEST_METHOD_DELETE) - ->setHttpPath('/v1/projects/:projectId/development-keys/:keyId') + ->setHttpPath('/v1/projects/:projectId/dev-keys/:keyId') ->desc('Delete dev key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php index f9c542c6b2..93da6ca8b3 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php @@ -22,7 +22,7 @@ class GetKey extends Action { $this ->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET) - ->setHttpPath('/v1/projects/:projectId/development-keys/:keyId') + ->setHttpPath('/v1/projects/:projectId/dev-keys/:keyId') ->desc('Get dev key') ->groups(['api', 'projects']) ->label('scope', 'projects.read') diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php index a4b4083080..2ef7cfa9eb 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php @@ -23,7 +23,7 @@ class ListKeys extends Action { $this ->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET) - ->setHttpPath('/v1/projects/:projectId/development-keys') + ->setHttpPath('/v1/projects/:projectId/dev-keys') ->desc('List dev keys') ->groups(['api', 'projects']) ->label('scope', 'projects.read') diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php index de29b40960..f56b2ed6d2 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php @@ -23,7 +23,7 @@ class UpdateKey extends Action public function __construct() { $this->setHttpMethod(Action::HTTP_REQUEST_METHOD_PUT) - ->setHttpPath('/v1/projects/:projectId/development-keys/:keyId') + ->setHttpPath('/v1/projects/:projectId/dev-keys/:keyId') ->desc('Update dev key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') diff --git a/tests/e2e/Scopes/ProjectCustom.php b/tests/e2e/Scopes/ProjectCustom.php index 5b492cfc06..b9561765fe 100644 --- a/tests/e2e/Scopes/ProjectCustom.php +++ b/tests/e2e/Scopes/ProjectCustom.php @@ -104,7 +104,7 @@ trait ProjectCustom $this->assertNotEmpty($key['body']); $this->assertNotEmpty($key['body']['secret']); - $devKey = $this->client->call(Client::METHOD_POST, '/projects/' . $project['body']['$id'] . '/development-keys', array_merge([ + $devKey = $this->client->call(Client::METHOD_POST, '/projects/' . $project['body']['$id'] . '/dev-keys', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index fc550270d9..79644bb936 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -15,7 +15,7 @@ trait ProjectsDevKeys { $id = $data['projectId'] ?? ''; - $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/development-keys', array_merge([ + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -31,7 +31,7 @@ trait ProjectsDevKeys $this->assertEmpty($response['body']['accessedAt']); /** TEST expiry date is required */ - $res = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/development-keys', array_merge([ + $res = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -57,7 +57,7 @@ trait ProjectsDevKeys { $id = $data['projectId'] ?? ''; - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/development-keys', array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); @@ -79,7 +79,7 @@ trait ProjectsDevKeys $id = $data['projectId'] ?? ''; $keyId = $data['keyId'] ?? ''; - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/development-keys/' . $keyId, array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys/' . $keyId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); @@ -95,7 +95,7 @@ trait ProjectsDevKeys /** * Test for FAILURE */ - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/development-keys/error', array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys/error', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); @@ -116,7 +116,7 @@ trait ProjectsDevKeys /** * Test for SUCCESS */ - $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/development-keys', array_merge([ + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -159,7 +159,7 @@ trait ProjectsDevKeys /** * Test for FAILURE */ - $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/development-keys', array_merge([ + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -181,7 +181,7 @@ trait ProjectsDevKeys /** * Test for FAILURE after expire */ - $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/development-keys', array_merge([ + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -222,7 +222,7 @@ trait ProjectsDevKeys $id = $data['projectId'] ?? ''; $keyId = $data['keyId'] ?? ''; - $response = $this->client->call(Client::METHOD_PUT, '/projects/' . $id . '/development-keys/' . $keyId, array_merge([ + $response = $this->client->call(Client::METHOD_PUT, '/projects/' . $id . '/dev-keys/' . $keyId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -237,7 +237,7 @@ trait ProjectsDevKeys $this->assertArrayHasKey('accessedAt', $response['body']); $this->assertEmpty($response['body']['accessedAt']); - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/development-keys/' . $keyId, array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys/' . $keyId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); @@ -261,7 +261,7 @@ trait ProjectsDevKeys $id = $data['projectId'] ?? ''; $keyId = $data['keyId'] ?? ''; - $response = $this->client->call(Client::METHOD_DELETE, '/projects/' . $id . '/development-keys/' . $keyId, array_merge([ + $response = $this->client->call(Client::METHOD_DELETE, '/projects/' . $id . '/dev-keys/' . $keyId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); @@ -282,7 +282,7 @@ trait ProjectsDevKeys ]); $this->assertEquals(429, $res['headers']['status-code']); - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/development-keys/' . $keyId, array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys/' . $keyId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); @@ -293,7 +293,7 @@ trait ProjectsDevKeys /** * Test for FAILURE */ - $response = $this->client->call(Client::METHOD_DELETE, '/projects/' . $id . '/development-keys/error', array_merge([ + $response = $this->client->call(Client::METHOD_DELETE, '/projects/' . $id . '/dev-keys/error', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); From ba83dd925261ccf6acc0ef6b42fc7022bc6b85d2 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 15 Dec 2024 09:33:17 +0000 Subject: [PATCH 041/110] fix dev key creation --- tests/e2e/Scopes/ProjectCustom.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/e2e/Scopes/ProjectCustom.php b/tests/e2e/Scopes/ProjectCustom.php index b9561765fe..9c8041027f 100644 --- a/tests/e2e/Scopes/ProjectCustom.php +++ b/tests/e2e/Scopes/ProjectCustom.php @@ -105,8 +105,10 @@ trait ProjectCustom $this->assertNotEmpty($key['body']['secret']); $devKey = $this->client->call(Client::METHOD_POST, '/projects/' . $project['body']['$id'] . '/dev-keys', array_merge([ + 'origin' => 'http://localhost', 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + 'x-appwrite-project' => 'console', ], $this->getHeaders()), [ 'name' => 'Key Test', 'expire' => DateTime::addSeconds(new \DateTime(), 3600), From 85656f81a5dacd1bb7889622a93881aa5d08884c Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 15 Dec 2024 10:03:52 +0000 Subject: [PATCH 042/110] update db name --- .../Modules/DevKeys/Http/DevKeys/CreateKey.php | 12 ++++++------ .../Modules/DevKeys/Http/DevKeys/DeleteKey.php | 14 +++++++------- .../Modules/DevKeys/Http/DevKeys/GetKey.php | 10 +++++----- .../Modules/DevKeys/Http/DevKeys/ListKeys.php | 10 +++++----- .../Modules/DevKeys/Http/DevKeys/UpdateKey.php | 14 +++++++------- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php index fd23a9d7eb..a1f4c2a296 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php @@ -41,13 +41,13 @@ class CreateKey extends Action ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.', false) ->inject('response') - ->inject('dbForConsole') - ->callback(fn ($projectId, $name, $expire, $response, $dbForConsole) => $this->action($projectId, $name, $expire, $response, $dbForConsole)); + ->inject('dbForPlatform') + ->callback(fn ($projectId, $name, $expire, $response, $dbForPlatform) => $this->action($projectId, $name, $expire, $response, $dbForPlatform)); } - public function action(string $projectId, string $name, ?string $expire, Response $response, Database $dbForConsole) + public function action(string $projectId, string $name, ?string $expire, Response $response, Database $dbForPlatform) { - $project = $dbForConsole->getDocument('projects', $projectId); + $project = $dbForPlatform->getDocument('projects', $projectId); if ($project->isEmpty()) { throw new Exception(Exception::PROJECT_NOT_FOUND); @@ -69,9 +69,9 @@ class CreateKey extends Action 'secret' => \bin2hex(\random_bytes(128)), ]); - $key = $dbForConsole->createDocument('devKeys', $key); + $key = $dbForPlatform->createDocument('devKeys', $key); - $dbForConsole->purgeCachedDocument('projects', $project->getId()); + $dbForPlatform->purgeCachedDocument('projects', $project->getId()); $response ->setStatusCode(Response::STATUS_CODE_CREATED) diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php index 44a8a9da3e..0fae2c30aa 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php @@ -34,20 +34,20 @@ class DeleteKey extends Action ->param('projectId', '', new UID(), 'Project unique ID.') ->param('keyId', '', new UID(), 'Key unique ID.') ->inject('response') - ->inject('dbForConsole') - ->callback(fn ($projectId, $keyId, $response, $dbForConsole) => $this->action($projectId, $keyId, $response, $dbForConsole)); + ->inject('dbForPlatform') + ->callback(fn ($projectId, $keyId, $response, $dbForPlatform) => $this->action($projectId, $keyId, $response, $dbForPlatform)); } - public function action(string $projectId, string $keyId, Response $response, Database $dbForConsole) + public function action(string $projectId, string $keyId, Response $response, Database $dbForPlatform) { - $project = $dbForConsole->getDocument('projects', $projectId); + $project = $dbForPlatform->getDocument('projects', $projectId); if ($project->isEmpty()) { throw new Exception(Exception::PROJECT_NOT_FOUND); } - $key = $dbForConsole->findOne('devKeys', [ + $key = $dbForPlatform->findOne('devKeys', [ Query::equal('$id', [$keyId]), Query::equal('projectInternalId', [$project->getInternalId()]), ]); @@ -56,9 +56,9 @@ class DeleteKey extends Action throw new Exception(Exception::KEY_NOT_FOUND); } - $dbForConsole->deleteDocument('devKeys', $key->getId()); + $dbForPlatform->deleteDocument('devKeys', $key->getId()); - $dbForConsole->purgeCachedDocument('projects', $project->getId()); + $dbForPlatform->purgeCachedDocument('projects', $project->getId()); $response->noContent(); } diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php index 93da6ca8b3..4f2a7fa9ca 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php @@ -35,20 +35,20 @@ class GetKey extends Action ->param('projectId', '', new UID(), 'Project unique ID.') ->param('keyId', '', new UID(), 'Key unique ID.') ->inject('response') - ->inject('dbForConsole') - ->callback(fn ($projectId, $keyId, $response, $dbForConsole) => $this->action($projectId, $keyId, $response, $dbForConsole)); + ->inject('dbForPlatform') + ->callback(fn ($projectId, $keyId, $response, $dbForPlatform) => $this->action($projectId, $keyId, $response, $dbForPlatform)); } - public function action(string $projectId, string $keyId, Response $response, Database $dbForConsole) + public function action(string $projectId, string $keyId, Response $response, Database $dbForPlatform) { - $project = $dbForConsole->getDocument('projects', $projectId); + $project = $dbForPlatform->getDocument('projects', $projectId); if ($project->isEmpty()) { throw new Exception(Exception::PROJECT_NOT_FOUND); } - $key = $dbForConsole->findOne('devKeys', [ + $key = $dbForPlatform->findOne('devKeys', [ Query::equal('$id', [$keyId]), Query::equal('projectInternalId', [$project->getInternalId()]), ]); diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php index 2ef7cfa9eb..e9df3167ea 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php @@ -35,20 +35,20 @@ class ListKeys extends Action ->label('sdk.response.model', Response::MODEL_DEV_KEY_LIST) ->param('projectId', '', new UID(), 'Project unique ID.') ->inject('response') - ->inject('dbForConsole') - ->callback(fn ($projectId, $response, $dbForConsole) => $this->action($projectId, $response, $dbForConsole)); + ->inject('dbForPlatform') + ->callback(fn ($projectId, $response, $dbForPlatform) => $this->action($projectId, $response, $dbForPlatform)); } - public function action(string $projectId, Response $response, Database $dbForConsole) + public function action(string $projectId, Response $response, Database $dbForPlatform) { - $project = $dbForConsole->getDocument('projects', $projectId); + $project = $dbForPlatform->getDocument('projects', $projectId); if ($project->isEmpty()) { throw new Exception(Exception::PROJECT_NOT_FOUND); } - $keys = $dbForConsole->find('devKeys', [ + $keys = $dbForPlatform->find('devKeys', [ Query::equal('projectInternalId', [$project->getInternalId()]), Query::limit(5000), ]); diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php index f56b2ed6d2..6d17429084 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php +++ b/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php @@ -38,19 +38,19 @@ class UpdateKey extends Action ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.') ->inject('response') - ->inject('dbForConsole') - ->callback(fn ($projectId, $keyId, $name, $expire, $response, $dbForConsole) => $this->action($projectId, $keyId, $name, $expire, $response, $dbForConsole)); + ->inject('dbForPlatform') + ->callback(fn ($projectId, $keyId, $name, $expire, $response, $dbForPlatform) => $this->action($projectId, $keyId, $name, $expire, $response, $dbForPlatform)); } - public function action(string $projectId, string $keyId, string $name, ?string $expire, Response $response, Database $dbForConsole) + public function action(string $projectId, string $keyId, string $name, ?string $expire, Response $response, Database $dbForPlatform) { - $project = $dbForConsole->getDocument('projects', $projectId); + $project = $dbForPlatform->getDocument('projects', $projectId); if ($project->isEmpty()) { throw new Exception(Exception::PROJECT_NOT_FOUND); } - $key = $dbForConsole->findOne('devKeys', [ + $key = $dbForPlatform->findOne('devKeys', [ Query::equal('$id', [$keyId]), Query::equal('projectInternalId', [$project->getInternalId()]), ]); @@ -63,9 +63,9 @@ class UpdateKey extends Action ->setAttribute('name', $name) ->setAttribute('expire', $expire); - $dbForConsole->updateDocument('devKeys', $key->getId(), $key); + $dbForPlatform->updateDocument('devKeys', $key->getId(), $key); - $dbForConsole->purgeCachedDocument('projects', $project->getId()); + $dbForPlatform->purgeCachedDocument('projects', $project->getId()); $response->dynamic($key, Response::MODEL_DEV_KEY); } From 6f3c60d4623710eb83773fb0cbc6066fc69589ea Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 15 Dec 2024 10:25:22 +0000 Subject: [PATCH 043/110] fix tests --- tests/e2e/Scopes/SideClient.php | 2 +- tests/e2e/Services/Projects/ProjectsDevKeys.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Scopes/SideClient.php b/tests/e2e/Scopes/SideClient.php index 205d5d4426..17ee7a3002 100644 --- a/tests/e2e/Scopes/SideClient.php +++ b/tests/e2e/Scopes/SideClient.php @@ -11,7 +11,7 @@ trait SideClient 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $this->getUser()['session'], ]; - if ($devKey) { + if ($devKey && isset($this->getProject()['devKey'])) { $headers['x-appwrite-dev-key'] = $this->getProject()['devKey']; } return $headers; diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index 79644bb936..fbbdda6e93 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -126,7 +126,7 @@ trait ProjectsDevKeys $devKey = $response['body']['secret']; - for ($i = 0; $i < 11; $i++) { + for ($i = 0; $i < 10; $i++) { $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', 'x-appwrite-project' => $id, From 05baa6090dbfc6292e2e91479a9a95f77b1cf643 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 15 Dec 2024 10:31:55 +0000 Subject: [PATCH 044/110] fix cycle --- tests/e2e/Scopes/ProjectCustom.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Scopes/ProjectCustom.php b/tests/e2e/Scopes/ProjectCustom.php index 9c8041027f..42aed2ba7c 100644 --- a/tests/e2e/Scopes/ProjectCustom.php +++ b/tests/e2e/Scopes/ProjectCustom.php @@ -104,12 +104,12 @@ trait ProjectCustom $this->assertNotEmpty($key['body']); $this->assertNotEmpty($key['body']['secret']); - $devKey = $this->client->call(Client::METHOD_POST, '/projects/' . $project['body']['$id'] . '/dev-keys', array_merge([ + $devKey = $this->client->call(Client::METHOD_POST, '/projects/' . $project['body']['$id'] . '/dev-keys', [ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'cookie' => 'a_session_console=' . $this->getRoot()['session'], 'x-appwrite-project' => 'console', - ], $this->getHeaders()), [ + ], [ 'name' => 'Key Test', 'expire' => DateTime::addSeconds(new \DateTime(), 3600), ]); From b8388a4092715fb7eb7b0f79a92df7655b9dbf9b Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 15 Dec 2024 10:41:01 +0000 Subject: [PATCH 045/110] fix expected in test --- tests/e2e/Services/Account/AccountBase.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/e2e/Services/Account/AccountBase.php b/tests/e2e/Services/Account/AccountBase.php index 2d72625121..b748f24233 100644 --- a/tests/e2e/Services/Account/AccountBase.php +++ b/tests/e2e/Services/Account/AccountBase.php @@ -118,7 +118,7 @@ trait AccountBase 'password' => $shortPassword ]); - $this->assertEquals($response['headers']['status-code'], 400); + $this->assertEquals(400, $response['headers']['status-code']); $longPassword = ''; for ($i = 0; $i < 257; $i++) { // 256 is the limit @@ -135,7 +135,7 @@ trait AccountBase 'password' => $longPassword, ]); - $this->assertEquals($response['headers']['status-code'], 400); + $this->assertEquals(400, $response['headers']['status-code']); return [ 'id' => $id, @@ -156,7 +156,7 @@ trait AccountBase 'email' => 'otpuser@appwrite.io' ]); - $this->assertEquals($response['headers']['status-code'], 201); + $this->assertEquals(201, $response['headers']['status-code'], ); $this->assertNotEmpty($response['body']['$id']); $this->assertNotEmpty($response['body']['$createdAt']); $this->assertNotEmpty($response['body']['userId']); From cd54ba1b83b51b980bcc19b911eb3593567d14da Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 15 Dec 2024 10:49:35 +0000 Subject: [PATCH 046/110] fix account test --- tests/e2e/Services/Account/AccountBase.php | 3 +++ tests/e2e/Services/Account/AccountCustomClientTest.php | 2 ++ 2 files changed, 5 insertions(+) diff --git a/tests/e2e/Services/Account/AccountBase.php b/tests/e2e/Services/Account/AccountBase.php index b748f24233..fcf836c713 100644 --- a/tests/e2e/Services/Account/AccountBase.php +++ b/tests/e2e/Services/Account/AccountBase.php @@ -112,6 +112,7 @@ trait AccountBase 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-dev-key' => $this->getProject()['devKey'] ?? '' ]), [ 'userId' => ID::unique(), 'email' => 'shortpass@appwrite.io', @@ -129,6 +130,7 @@ trait AccountBase 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-dev-key' => $this->getProject()['devKey'] ?? '' ]), [ 'userId' => ID::unique(), 'email' => 'longpass@appwrite.io', @@ -286,6 +288,7 @@ trait AccountBase 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-dev-key' => $this->getProject()['devKey'] ?? '' ]), [ 'userId' => ID::unique(), 'email' => $email, diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index cca27cc3be..5d89f6f3f0 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -618,6 +618,7 @@ class AccountCustomClientTest extends Scope 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-dev-key' => $this->getProject()['devKey'] ?? '' ]), [ 'userId' => ID::unique(), 'email' => $data['email'], @@ -1216,6 +1217,7 @@ class AccountCustomClientTest extends Scope 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-dev-key' => $this->getProject()['devKey'] ?? '' ]), [ 'userId' => ID::unique(), 'email' => $email, From 195ae3fdcb72aaf3b130e0789f0b15a18ec5cc57 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 15 Dec 2024 11:06:46 +0000 Subject: [PATCH 047/110] update project desc --- tests/e2e/Scopes/Scope.php | 2 +- tests/e2e/Scopes/SideConsole.php | 2 +- tests/e2e/Services/GraphQL/AbuseTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/e2e/Scopes/Scope.php b/tests/e2e/Scopes/Scope.php index 3213ff4c5d..fda4abc1dc 100644 --- a/tests/e2e/Scopes/Scope.php +++ b/tests/e2e/Scopes/Scope.php @@ -56,7 +56,7 @@ abstract class Scope extends TestCase /** * @return array */ - abstract public function getHeaders(): array; + abstract public function getHeaders(bool $devKey): array; /** * @return array diff --git a/tests/e2e/Scopes/SideConsole.php b/tests/e2e/Scopes/SideConsole.php index 74a0dd0c60..d619861a75 100644 --- a/tests/e2e/Scopes/SideConsole.php +++ b/tests/e2e/Scopes/SideConsole.php @@ -4,7 +4,7 @@ namespace Tests\E2E\Scopes; trait SideConsole { - public function getHeaders(): array + public function getHeaders(bool $devKey = false): array { return [ 'origin' => 'http://localhost', diff --git a/tests/e2e/Services/GraphQL/AbuseTest.php b/tests/e2e/Services/GraphQL/AbuseTest.php index d4e87cf029..ea97492c2b 100644 --- a/tests/e2e/Services/GraphQL/AbuseTest.php +++ b/tests/e2e/Services/GraphQL/AbuseTest.php @@ -88,7 +88,7 @@ class AbuseTest extends Scope $response = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, - ], $this->getHeaders()), $graphQLPayload); + ], $this->getHeaders(false)), $graphQLPayload); $max = System::getEnv('_APP_GRAPHQL_MAX_QUERY_COMPLEXITY', 250); From d5e775416d2d50eb5bfc71272ec7f0d1197d2f8b Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 27 Jan 2025 10:36:40 +0000 Subject: [PATCH 048/110] chore: added devkeys collections to new file, and formatting --- app/config/collections/platform.php | 112 ++++++++++++++++++++++++++++ app/controllers/general.php | 2 +- 2 files changed, 113 insertions(+), 1 deletion(-) diff --git a/app/config/collections/platform.php b/app/config/collections/platform.php index a5fedb6461..3c51a0d9af 100644 --- a/app/config/collections/platform.php +++ b/app/config/collections/platform.php @@ -287,6 +287,17 @@ return [ 'array' => false, 'filters' => ['subQueryKeys'], ], + [ + '$id' => ID::custom('devKeys'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['subQueryDevKeys'], + ], [ '$id' => ID::custom('search'), 'type' => Database::VAR_STRING, @@ -689,6 +700,107 @@ return [ ], ], + 'devKeys' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('devKeys'), + 'name' => 'Dev keys', + 'attributes' => [ + [ + '$id' => ID::custom('projectInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('projectId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => 0, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('name'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('secret'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 512, // var_dump of \bin2hex(\random_bytes(128)) => string(256) doubling for encryption + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => ['encrypt'], + ], + [ + '$id' => ID::custom('expire'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('accessedAt'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('sdks'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => true, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_project'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['projectInternalId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => '_key_accessedAt', + 'type' => Database::INDEX_KEY, + 'attributes' => ['accessedAt'], + 'lengths' => [], + 'orders' => [], + ], + ], + ], + 'webhooks' => [ '$collection' => ID::custom(Database::METADATA), '$id' => ID::custom('webhooks'), diff --git a/app/controllers/general.php b/app/controllers/general.php index 39afa48b19..6775740c7d 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -10,11 +10,11 @@ use Appwrite\Event\Func; use Appwrite\Event\Usage; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Network\Validator\Origin; +use Appwrite\Platform\Appwrite; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; -use Appwrite\Platform\Appwrite; use Appwrite\Utopia\Request; use Appwrite\Utopia\Request\Filters\V16 as RequestV16; use Appwrite\Utopia\Request\Filters\V17 as RequestV17; From b399ff00a6d1bfb8e88760463244eb3bfb759766 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 28 Jan 2025 14:06:56 +0000 Subject: [PATCH 049/110] chore: update e2e tests to use abuse env --- .github/workflows/tests.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5b7438de42..32241446cc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,6 +20,17 @@ jobs: with: submodules: recursive + - name: Detect test types + id: detect-tests + run: | + ABUSE_ENABLED=$(grep -oP '^_APP_OPTIONS_ABUSE=\K\w+' .env || echo 'disabled') + + if [ "$ABUSE_ENABLED" = "enabled" ]; then + echo 'test_suffixes=["CustomClient", "CustomServer"]' >> $GITHUB_OUTPUT + else + echo 'test_suffixes=["ConsoleClient"]' >> $GITHUB_OUTPUT + fi + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -127,6 +138,7 @@ jobs: Messaging, Migrations ] + test_suffix: ${{ fromJSON(needs.setup.outputs.test_suffixes) }} tables-mode: [ 'Project', 'Shared V1', @@ -150,7 +162,7 @@ jobs: docker compose up -d sleep 30 - - name: Run ${{ matrix.service }} tests with ${{ matrix.tables-mode }} table mode + - name: Run ${{ matrix.service }} ${{ matrix.test_suffix }} tests (${{ matrix.tables-mode }}) run: | if [ "${{ matrix.tables-mode }}" == "Shared V1" ]; then echo "Using shared tables V1" @@ -169,7 +181,7 @@ jobs: docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ - appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug + appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }}/${{ matrix.service }}${{ matrix.test_suffix }}Test.php --debug benchmarking: name: Benchmark From 9561ed99d222c6479077728ebf2605df5582f54d Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 28 Jan 2025 14:18:00 +0000 Subject: [PATCH 050/110] chore: fix getHeaders implementation in scope tests --- tests/e2e/Scopes/SideNone.php | 2 +- tests/e2e/Scopes/SideServer.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Scopes/SideNone.php b/tests/e2e/Scopes/SideNone.php index 79c52afe00..3765834011 100644 --- a/tests/e2e/Scopes/SideNone.php +++ b/tests/e2e/Scopes/SideNone.php @@ -4,7 +4,7 @@ namespace Tests\E2E\Scopes; trait SideNone { - public function getHeaders(): array + public function getHeaders(bool $devKey): array { return []; } diff --git a/tests/e2e/Scopes/SideServer.php b/tests/e2e/Scopes/SideServer.php index b5e15150e9..7137b8e81e 100644 --- a/tests/e2e/Scopes/SideServer.php +++ b/tests/e2e/Scopes/SideServer.php @@ -9,7 +9,7 @@ trait SideServer */ protected $key = []; - public function getHeaders(): array + public function getHeaders(bool $devKey): array { return [ 'x-appwrite-key' => $this->getProject()['apiKey'] From cad18dc7c797df66f54f7c86f2357d9531a5b724 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 28 Jan 2025 14:29:45 +0000 Subject: [PATCH 051/110] chore: fix abstract implementations for scope tests --- tests/e2e/Scopes/Scope.php | 2 +- tests/e2e/Scopes/SideConsole.php | 2 +- tests/e2e/Scopes/SideNone.php | 2 +- tests/e2e/Scopes/SideServer.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/e2e/Scopes/Scope.php b/tests/e2e/Scopes/Scope.php index fda4abc1dc..6deaa62c05 100644 --- a/tests/e2e/Scopes/Scope.php +++ b/tests/e2e/Scopes/Scope.php @@ -56,7 +56,7 @@ abstract class Scope extends TestCase /** * @return array */ - abstract public function getHeaders(bool $devKey): array; + abstract public function getHeaders(bool $devKey = true): array; /** * @return array diff --git a/tests/e2e/Scopes/SideConsole.php b/tests/e2e/Scopes/SideConsole.php index d619861a75..9ad3e93d6a 100644 --- a/tests/e2e/Scopes/SideConsole.php +++ b/tests/e2e/Scopes/SideConsole.php @@ -4,7 +4,7 @@ namespace Tests\E2E\Scopes; trait SideConsole { - public function getHeaders(bool $devKey = false): array + public function getHeaders(bool $devKey = true): array { return [ 'origin' => 'http://localhost', diff --git a/tests/e2e/Scopes/SideNone.php b/tests/e2e/Scopes/SideNone.php index 3765834011..1660beb777 100644 --- a/tests/e2e/Scopes/SideNone.php +++ b/tests/e2e/Scopes/SideNone.php @@ -4,7 +4,7 @@ namespace Tests\E2E\Scopes; trait SideNone { - public function getHeaders(bool $devKey): array + public function getHeaders(bool $devKey = true): array { return []; } diff --git a/tests/e2e/Scopes/SideServer.php b/tests/e2e/Scopes/SideServer.php index 7137b8e81e..d27b2092b0 100644 --- a/tests/e2e/Scopes/SideServer.php +++ b/tests/e2e/Scopes/SideServer.php @@ -9,7 +9,7 @@ trait SideServer */ protected $key = []; - public function getHeaders(bool $devKey): array + public function getHeaders(bool $devKey = false): array { return [ 'x-appwrite-key' => $this->getProject()['apiKey'] From 222516dbd4291d6c45fd5950d5a001c2b7d5fe5c Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 28 Jan 2025 14:41:58 +0000 Subject: [PATCH 052/110] chore: added base tests in e2e service --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 32241446cc..4aa5252de0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -26,9 +26,9 @@ jobs: ABUSE_ENABLED=$(grep -oP '^_APP_OPTIONS_ABUSE=\K\w+' .env || echo 'disabled') if [ "$ABUSE_ENABLED" = "enabled" ]; then - echo 'test_suffixes=["CustomClient", "CustomServer"]' >> $GITHUB_OUTPUT + echo 'test_suffixes=["Base", "CustomClient", "CustomServer"]' >> $GITHUB_OUTPUT else - echo 'test_suffixes=["ConsoleClient"]' >> $GITHUB_OUTPUT + echo 'test_suffixes=["Base", "ConsoleClient"]' >> $GITHUB_OUTPUT fi - name: Set up Docker Buildx From 7bd29501c2fd81afd0807484de8f40c24f0647af Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 28 Jan 2025 16:07:41 +0000 Subject: [PATCH 053/110] chore: added test_suffixes to outputs --- .github/workflows/tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4aa5252de0..8ac58fd744 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,6 +14,8 @@ jobs: setup: name: Setup & Build Appwrite Image runs-on: ubuntu-latest + outputs: + test_suffixes: ${{ steps.detect-tests.outputs.test_suffixes }} steps: - name: Checkout repository uses: actions/checkout@v4 From acadaf2307217c4095a8e3c5df5f46ade8e168d9 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 28 Jan 2025 16:13:07 +0000 Subject: [PATCH 054/110] chore: fix naming for tests --- .github/workflows/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8ac58fd744..b65a9193fd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -28,9 +28,9 @@ jobs: ABUSE_ENABLED=$(grep -oP '^_APP_OPTIONS_ABUSE=\K\w+' .env || echo 'disabled') if [ "$ABUSE_ENABLED" = "enabled" ]; then - echo 'test_suffixes=["Base", "CustomClient", "CustomServer"]' >> $GITHUB_OUTPUT + echo 'test_suffixes=["Base", "CustomClientTest", "CustomServerTest"]' >> $GITHUB_OUTPUT else - echo 'test_suffixes=["Base", "ConsoleClient"]' >> $GITHUB_OUTPUT + echo 'test_suffixes=["Base", "ConsoleClientTest"]' >> $GITHUB_OUTPUT fi - name: Set up Docker Buildx @@ -183,7 +183,7 @@ jobs: docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ - appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }}/${{ matrix.service }}${{ matrix.test_suffix }}Test.php --debug + appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }}/${{ matrix.service }}${{ matrix.test_suffix }}.php --debug benchmarking: name: Benchmark From af7f59952552854f6bd271088ab344595b7606f2 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 28 Jan 2025 16:22:32 +0000 Subject: [PATCH 055/110] chore: fix remove base --- .github/workflows/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b65a9193fd..3644b1d1c0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -28,9 +28,9 @@ jobs: ABUSE_ENABLED=$(grep -oP '^_APP_OPTIONS_ABUSE=\K\w+' .env || echo 'disabled') if [ "$ABUSE_ENABLED" = "enabled" ]; then - echo 'test_suffixes=["Base", "CustomClientTest", "CustomServerTest"]' >> $GITHUB_OUTPUT + echo 'test_suffixes=["CustomClient", "CustomServer"]' >> $GITHUB_OUTPUT else - echo 'test_suffixes=["Base", "ConsoleClientTest"]' >> $GITHUB_OUTPUT + echo 'test_suffixes=["ConsoleClient"]' >> $GITHUB_OUTPUT fi - name: Set up Docker Buildx @@ -183,7 +183,7 @@ jobs: docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ - appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }}/${{ matrix.service }}${{ matrix.test_suffix }}.php --debug + appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }}/${{ matrix.service }}${{ matrix.test_suffix }}Test.php --debug benchmarking: name: Benchmark From c01f67f44fce3e033f8b616b7d3ac30e14b9dfa0 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 28 Jan 2025 16:58:27 +0000 Subject: [PATCH 056/110] chore: fix parsing of test files --- .github/workflows/tests.yml | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3644b1d1c0..19a77c8a56 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -179,11 +179,27 @@ jobs: export _APP_DATABASE_SHARED_TABLES= export _APP_DATABASE_SHARED_TABLES_V1= fi - - docker compose exec -T \ - -e _APP_DATABASE_SHARED_TABLES \ - -e _APP_DATABASE_SHARED_TABLES_V1 \ - appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }}/${{ matrix.service }}${{ matrix.test_suffix }}Test.php --debug + + TEST_SUFFIXES=${{ matrix.test_suffix }} + TEST_FOLDER="/usr/src/code/tests/e2e/Services/${{ matrix.service }}" + ALL_TEST_FILES=$(find "$TEST_FOLDER" -type f -name "*Test.php") + + for TEST_FILE in $ALL_TEST_FILES; do + SUFFIX=$(echo "$TEST_FILE" | grep -oE '(CustomClient|CustomServer|ConsoleClient)Test\.php$' | sed 's/Test\.php$//') + + if [ -n "$SUFFIX" ]; then + if [[ "$TEST_SUFFIXES" == *"$SUFFIX"* ]]; then + docker compose exec -T \ + -e _APP_DATABASE_SHARED_TABLES \ + -e _APP_DATABASE_SHARED_TABLES_V1 \ + appwrite test "$TEST_FILE" --debug + else + docker compose exec -T \ + -e _APP_DATABASE_SHARED_TABLES \ + -e _APP_DATABASE_SHARED_TABLES_V1 \ + appwrite test "$TEST_FILE" --debug + fi + done benchmarking: name: Benchmark From c2aff2c2cb36053e8544618b88a93d4cbd928c8a Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 28 Jan 2025 17:16:35 +0000 Subject: [PATCH 057/110] chore: fix fetching of test files --- .github/workflows/tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 19a77c8a56..02cbd30bbb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -182,7 +182,7 @@ jobs: TEST_SUFFIXES=${{ matrix.test_suffix }} TEST_FOLDER="/usr/src/code/tests/e2e/Services/${{ matrix.service }}" - ALL_TEST_FILES=$(find "$TEST_FOLDER" -type f -name "*Test.php") + ALL_TEST_FILES=$(docker compose exec -T appwrite find "$TEST_FOLDER" -type f -name "*Test.php") for TEST_FILE in $ALL_TEST_FILES; do SUFFIX=$(echo "$TEST_FILE" | grep -oE '(CustomClient|CustomServer|ConsoleClient)Test\.php$' | sed 's/Test\.php$//') @@ -193,6 +193,7 @@ jobs: -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ appwrite test "$TEST_FILE" --debug + fi else docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ From c73f2afbe11166985f61fb99649efadbb6d9a400 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 28 Jan 2025 17:41:48 +0000 Subject: [PATCH 058/110] chore: fix run tests loop --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 02cbd30bbb..2978afeaba 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -164,7 +164,7 @@ jobs: docker compose up -d sleep 30 - - name: Run ${{ matrix.service }} ${{ matrix.test_suffix }} tests (${{ matrix.tables-mode }}) + - name: Run ${{ matrix.service }} tests with ${{ matrix.tables-mode }} table mode run: | if [ "${{ matrix.tables-mode }}" == "Shared V1" ]; then echo "Using shared tables V1" From 28e1ce9dc24f9df4d5c40bcd7510fe53f1bcc865 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 28 Jan 2025 17:49:05 +0000 Subject: [PATCH 059/110] chore: fix recursive tests --- .github/workflows/tests.yml | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2978afeaba..ac222c1df7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,25 +14,12 @@ jobs: setup: name: Setup & Build Appwrite Image runs-on: ubuntu-latest - outputs: - test_suffixes: ${{ steps.detect-tests.outputs.test_suffixes }} steps: - name: Checkout repository uses: actions/checkout@v4 with: submodules: recursive - - name: Detect test types - id: detect-tests - run: | - ABUSE_ENABLED=$(grep -oP '^_APP_OPTIONS_ABUSE=\K\w+' .env || echo 'disabled') - - if [ "$ABUSE_ENABLED" = "enabled" ]; then - echo 'test_suffixes=["CustomClient", "CustomServer"]' >> $GITHUB_OUTPUT - else - echo 'test_suffixes=["ConsoleClient"]' >> $GITHUB_OUTPUT - fi - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -140,7 +127,6 @@ jobs: Messaging, Migrations ] - test_suffix: ${{ fromJSON(needs.setup.outputs.test_suffixes) }} tables-mode: [ 'Project', 'Shared V1', @@ -180,7 +166,9 @@ jobs: export _APP_DATABASE_SHARED_TABLES_V1= fi - TEST_SUFFIXES=${{ matrix.test_suffix }} + ABUSE_ENABLED=$(grep -oP '^_APP_OPTIONS_ABUSE=\K\w+' .env || echo 'disabled') + TEST_SUFFIXES=$( [[ "$ABUSE_ENABLED" == "enabled" ]] && echo 'CustomClient CustomServer' || echo 'ConsoleClient') + TEST_FOLDER="/usr/src/code/tests/e2e/Services/${{ matrix.service }}" ALL_TEST_FILES=$(docker compose exec -T appwrite find "$TEST_FOLDER" -type f -name "*Test.php") From 1e71eb84c4fd642505ce0eb41b0874aba5427d94 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 29 Jan 2025 04:25:52 +0000 Subject: [PATCH 060/110] chore: added devkeys to account and database tests --- tests/e2e/Services/Account/AccountBase.php | 1 + tests/e2e/Services/Databases/DatabasesPermissionsScope.php | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/e2e/Services/Account/AccountBase.php b/tests/e2e/Services/Account/AccountBase.php index fcf836c713..1a77cccb18 100644 --- a/tests/e2e/Services/Account/AccountBase.php +++ b/tests/e2e/Services/Account/AccountBase.php @@ -99,6 +99,7 @@ trait AccountBase 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-dev-key' => $this->getProject()['devKey'] ?? '', ]), [ 'userId' => ID::unique(), 'email' => '', diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsScope.php b/tests/e2e/Services/Databases/DatabasesPermissionsScope.php index 336e47db08..0042d253ac 100644 --- a/tests/e2e/Services/Databases/DatabasesPermissionsScope.php +++ b/tests/e2e/Services/Databases/DatabasesPermissionsScope.php @@ -15,6 +15,7 @@ trait DatabasesPermissionsScope 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-dev-key' => $this->getProject()['devKey'] ?? '', ], [ 'userId' => $id, 'email' => $email, From f132c2b266e3324f047b65fd32102848e6652e01 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 29 Jan 2025 05:52:03 +0000 Subject: [PATCH 061/110] chore: changed ordering of tests --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ac222c1df7..6bbef54dc0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -167,7 +167,7 @@ jobs: fi ABUSE_ENABLED=$(grep -oP '^_APP_OPTIONS_ABUSE=\K\w+' .env || echo 'disabled') - TEST_SUFFIXES=$( [[ "$ABUSE_ENABLED" == "enabled" ]] && echo 'CustomClient CustomServer' || echo 'ConsoleClient') + TEST_SUFFIXES=$( [[ "$ABUSE_ENABLED" == "enabled" ]] && echo 'CustomServer CustomClient' || echo 'ConsoleClient') TEST_FOLDER="/usr/src/code/tests/e2e/Services/${{ matrix.service }}" ALL_TEST_FILES=$(docker compose exec -T appwrite find "$TEST_FOLDER" -type f -name "*Test.php") From 23111d26c48cec11e788f10ce66dc4bd29d7e7d3 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 30 Jan 2025 09:28:23 +0000 Subject: [PATCH 062/110] chore: filtered out customclienttests --- .github/workflows/tests.yml | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6bbef54dc0..a252b20172 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -166,27 +166,12 @@ jobs: export _APP_DATABASE_SHARED_TABLES_V1= fi - ABUSE_ENABLED=$(grep -oP '^_APP_OPTIONS_ABUSE=\K\w+' .env || echo 'disabled') - TEST_SUFFIXES=$( [[ "$ABUSE_ENABLED" == "enabled" ]] && echo 'CustomServer CustomClient' || echo 'ConsoleClient') - - TEST_FOLDER="/usr/src/code/tests/e2e/Services/${{ matrix.service }}" - ALL_TEST_FILES=$(docker compose exec -T appwrite find "$TEST_FOLDER" -type f -name "*Test.php") - - for TEST_FILE in $ALL_TEST_FILES; do - SUFFIX=$(echo "$TEST_FILE" | grep -oE '(CustomClient|CustomServer|ConsoleClient)Test\.php$' | sed 's/Test\.php$//') - - if [ -n "$SUFFIX" ]; then - if [[ "$TEST_SUFFIXES" == *"$SUFFIX"* ]]; then - docker compose exec -T \ - -e _APP_DATABASE_SHARED_TABLES \ - -e _APP_DATABASE_SHARED_TABLES_V1 \ - appwrite test "$TEST_FILE" --debug - fi - else + for test_file in /usr/src/code/tests/e2e/Services/${{ matrix.service }}/*Test.php; do + if [[ "$test_file" != *CustomClientTest.php ]]; then docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ - appwrite test "$TEST_FILE" --debug + appwrite test "$test_file" --debug fi done From 5a1ad4d5ae4e813f2061941c1062c9379ef10976 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 30 Jan 2025 11:48:43 +0000 Subject: [PATCH 063/110] chore: added grouping of devkeys in ci --- .github/workflows/tests.yml | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a252b20172..007e3a6c1b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -166,14 +166,20 @@ jobs: export _APP_DATABASE_SHARED_TABLES_V1= fi - for test_file in /usr/src/code/tests/e2e/Services/${{ matrix.service }}/*Test.php; do - if [[ "$test_file" != *CustomClientTest.php ]]; then - docker compose exec -T \ - -e _APP_DATABASE_SHARED_TABLES \ - -e _APP_DATABASE_SHARED_TABLES_V1 \ - appwrite test "$test_file" --debug - fi - done + docker compose exec -T \ + -e _APP_DATABASE_SHARED_TABLES \ + -e _APP_DATABASE_SHARED_TABLES_V1 \ + appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude=devKeys + + if [ "${{ matrix.service }}" == "Projects" ]; then + export _APP_OPTIONS_ABUSE=enabled + + docker compose exec -T \ + -e _APP_OPTIONS_ABUSE \ + -e _APP_DATABASE_SHARED_TABLES \ + -e _APP_DATABASE_SHARED_TABLES_V1 \ + appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --group=devKeys + fi benchmarking: name: Benchmark From bd801712b95b34e350f183fc1b3240d4b8bc784a Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 30 Jan 2025 11:51:43 +0000 Subject: [PATCH 064/110] chore: disabled options abuse env --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index e998cabee1..8f7d7996e7 100644 --- a/.env +++ b/.env @@ -15,7 +15,7 @@ _APP_SYSTEM_TEAM_EMAIL=team@appwrite.io _APP_EMAIL_SECURITY=security@appwrite.io _APP_EMAIL_CERTIFICATES=certificates@appwrite.io _APP_SYSTEM_RESPONSE_FORMAT= -_APP_OPTIONS_ABUSE=enabled +_APP_OPTIONS_ABUSE=disabled _APP_OPTIONS_ROUTER_PROTECTION=disabled _APP_OPTIONS_FORCE_HTTPS=disabled _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled From 751f7e3fadfea97801a8480961ae3ec886cf94b8 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 30 Jan 2025 12:11:09 +0000 Subject: [PATCH 065/110] chore: shifted abuse project test to different job in ci --- .github/workflows/tests.yml | 58 ++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 007e3a6c1b..2dd7351369 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -171,16 +171,60 @@ jobs: -e _APP_DATABASE_SHARED_TABLES_V1 \ appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude=devKeys - if [ "${{ matrix.service }}" == "Projects" ]; then - export _APP_OPTIONS_ABUSE=enabled + e2e_dev_keys: + name: E2E Service Test + runs-on: ubuntu-latest + needs: setup + strategy: + fail-fast: false + matrix: + tables-mode: [ + 'Project', + 'Shared V1', + 'Shared V2', + ] - docker compose exec -T \ - -e _APP_OPTIONS_ABUSE \ - -e _APP_DATABASE_SHARED_TABLES \ - -e _APP_DATABASE_SHARED_TABLES_V1 \ - appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --group=devKeys + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: Load Cache + uses: actions/cache@v4 + with: + key: ${{ env.CACHE_KEY }} + path: /tmp/${{ env.IMAGE }}.tar + fail-on-cache-miss: true + + - name: Load and Start Appwrite + run: | + docker load --input /tmp/${{ env.IMAGE }}.tar + docker compose up -d + sleep 30 + + - name: Run Projects tests with dev keys in ${{ matrix.tables-mode }} table mode + run: | + if [ "${{ matrix.tables-mode }}" == "Shared V1" ]; then + echo "Using shared tables V1" + export _APP_DATABASE_SHARED_TABLES=database_db_main + export _APP_DATABASE_SHARED_TABLES_V1=database_db_main + elif [ "${{ matrix.tables-mode }}" == "Shared V2" ]; then + echo "Using shared tables V2" + export _APP_DATABASE_SHARED_TABLES=database_db_main + export _APP_DATABASE_SHARED_TABLES_V1= + else + echo "Using project tables" + export _APP_DATABASE_SHARED_TABLES= + export _APP_DATABASE_SHARED_TABLES_V1= fi + export _APP_OPTIONS_ABUSE=enabled + + docker compose exec -T \ + -e _APP_OPTIONS_ABUSE \ + -e _APP_DATABASE_SHARED_TABLES \ + -e _APP_DATABASE_SHARED_TABLES_V1 \ + appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys + benchmarking: name: Benchmark runs-on: ubuntu-latest From 67ba9a880dfc510ac8eef4f6d7cdb5baafaf8d86 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 30 Jan 2025 12:25:56 +0000 Subject: [PATCH 066/110] chore: fix tests --- .github/workflows/tests.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2dd7351369..5f295c4359 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -172,7 +172,7 @@ jobs: appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude=devKeys e2e_dev_keys: - name: E2E Service Test + name: E2E Service Test (Dev Keys) runs-on: ubuntu-latest needs: setup strategy: @@ -217,10 +217,8 @@ jobs: export _APP_DATABASE_SHARED_TABLES_V1= fi - export _APP_OPTIONS_ABUSE=enabled - docker compose exec -T \ - -e _APP_OPTIONS_ABUSE \ + -e _APP_OPTIONS_ABUSE=enabled \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys From 3522e63404a7a9c524c2f1230f670d0c342cf5f3 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 30 Jan 2025 12:36:28 +0000 Subject: [PATCH 067/110] chore: fix tests --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5f295c4359..ce924e20d3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -198,6 +198,7 @@ jobs: - name: Load and Start Appwrite run: | docker load --input /tmp/${{ env.IMAGE }}.tar + sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env docker compose up -d sleep 30 @@ -218,7 +219,6 @@ jobs: fi docker compose exec -T \ - -e _APP_OPTIONS_ABUSE=enabled \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys From 2fa22b4b8521caa5a7c3f2e85932649a0bc776cd Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 30 Jan 2025 14:05:53 +0000 Subject: [PATCH 068/110] chore: workflow added rebuild on cache miss --- .github/workflows/tests.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ce924e20d3..c5a565b269 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -138,12 +138,29 @@ jobs: uses: actions/checkout@v4 - name: Load Cache + id: cache-docker-image uses: actions/cache@v4 with: key: ${{ env.CACHE_KEY }} path: /tmp/${{ env.IMAGE }}.tar fail-on-cache-miss: true + - name: Build Docker Image if Cache Missed + if: steps.cache-docker-image.outputs.cache-hit != 'true' + uses: docker/build-push-action@v3 + with: + context: . + push: false + tags: ${{ env.IMAGE }} + load: true + cache-from: type=gha + cache-to: type=gha,mode=max + outputs: type=docker,dest=/tmp/${{ env.IMAGE }}.tar + build-args: | + DEBUG=false + TESTING=true + VERSION=dev + - name: Load and Start Appwrite run: | docker load --input /tmp/${{ env.IMAGE }}.tar From 1b91b601bcb3128758b0a90415fc8698f850c048 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 30 Jan 2025 14:18:08 +0000 Subject: [PATCH 069/110] chore: remove cache rebuild --- .github/workflows/tests.yml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c5a565b269..c83d98bd0c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -145,22 +145,6 @@ jobs: path: /tmp/${{ env.IMAGE }}.tar fail-on-cache-miss: true - - name: Build Docker Image if Cache Missed - if: steps.cache-docker-image.outputs.cache-hit != 'true' - uses: docker/build-push-action@v3 - with: - context: . - push: false - tags: ${{ env.IMAGE }} - load: true - cache-from: type=gha - cache-to: type=gha,mode=max - outputs: type=docker,dest=/tmp/${{ env.IMAGE }}.tar - build-args: | - DEBUG=false - TESTING=true - VERSION=dev - - name: Load and Start Appwrite run: | docker load --input /tmp/${{ env.IMAGE }}.tar From b4849cd6caf6b13b40c890a5a542b6c7d6a46d72 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 30 Jan 2025 14:18:28 +0000 Subject: [PATCH 070/110] chore: remove id --- .github/workflows/tests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c83d98bd0c..ce924e20d3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -138,7 +138,6 @@ jobs: uses: actions/checkout@v4 - name: Load Cache - id: cache-docker-image uses: actions/cache@v4 with: key: ${{ env.CACHE_KEY }} From 942034087155727fe271899792a6ed64cc0c4e56 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 4 Feb 2025 10:53:15 +0000 Subject: [PATCH 071/110] chore: changed directory structure for devkeys --- src/Appwrite/Platform/Appwrite.php | 4 +-- .../Modules/DevKeys/Services/Http.php | 23 --------------- .../Http/DevKeys/CreateDevKey.php} | 28 +++++++++++++------ .../Http/DevKeys/DeleteDevKey.php} | 27 ++++++++++++------ .../Http/DevKeys/GetDevKey.php} | 28 +++++++++++++------ .../Http/DevKeys/ListDevKeys.php} | 28 +++++++++++++------ .../Http/DevKeys/UpdateDevKey.php} | 28 +++++++++++++------ .../Modules/{DevKeys => Projects}/Module.php | 4 +-- .../Modules/Projects/Services/Http.php | 23 +++++++++++++++ 9 files changed, 122 insertions(+), 71 deletions(-) delete mode 100644 src/Appwrite/Platform/Modules/DevKeys/Services/Http.php rename src/Appwrite/Platform/Modules/{DevKeys/Http/DevKeys/CreateKey.php => Projects/Http/DevKeys/CreateDevKey.php} (78%) rename src/Appwrite/Platform/Modules/{DevKeys/Http/DevKeys/DeleteKey.php => Projects/Http/DevKeys/DeleteDevKey.php} (71%) rename src/Appwrite/Platform/Modules/{DevKeys/Http/DevKeys/GetKey.php => Projects/Http/DevKeys/GetDevKey.php} (70%) rename src/Appwrite/Platform/Modules/{DevKeys/Http/DevKeys/ListKeys.php => Projects/Http/DevKeys/ListDevKeys.php} (68%) rename src/Appwrite/Platform/Modules/{DevKeys/Http/DevKeys/UpdateKey.php => Projects/Http/DevKeys/UpdateDevKey.php} (76%) rename src/Appwrite/Platform/Modules/{DevKeys => Projects}/Module.php (62%) create mode 100644 src/Appwrite/Platform/Modules/Projects/Services/Http.php diff --git a/src/Appwrite/Platform/Appwrite.php b/src/Appwrite/Platform/Appwrite.php index ec19b3248c..b4510df777 100644 --- a/src/Appwrite/Platform/Appwrite.php +++ b/src/Appwrite/Platform/Appwrite.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform; use Appwrite\Platform\Modules\Core; -use Appwrite\Platform\Modules\DevKeys; +use Appwrite\Platform\Modules\Projects; use Utopia\Platform\Platform; class Appwrite extends Platform @@ -11,6 +11,6 @@ class Appwrite extends Platform public function __construct() { parent::__construct(new Core()); - $this->addModule(new DevKeys\Module()); + $this->addModule(new Projects\Module()); } } diff --git a/src/Appwrite/Platform/Modules/DevKeys/Services/Http.php b/src/Appwrite/Platform/Modules/DevKeys/Services/Http.php deleted file mode 100644 index 0e9decc3a9..0000000000 --- a/src/Appwrite/Platform/Modules/DevKeys/Services/Http.php +++ /dev/null @@ -1,23 +0,0 @@ -type = Service::TYPE_HTTP; - $this->addAction(CreateKey::getName(), new CreateKey()); - $this->addAction(UpdateKey::getName(), new UpdateKey()); - $this->addAction(GetKey::getName(), new GetKey()); - $this->addAction(ListKeys::getName(), new ListKeys()); - $this->addAction(DeleteKey::getName(), new DeleteKey()); - } -} diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/CreateDevKey.php similarity index 78% rename from src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php rename to src/Appwrite/Platform/Modules/Projects/Http/DevKeys/CreateDevKey.php index a1f4c2a296..3f28cd6f81 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/CreateKey.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/CreateDevKey.php @@ -1,8 +1,11 @@ desc('Create dev key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'createDevKey') - ->label('sdk.response.code', Response::STATUS_CODE_CREATED) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_DEV_KEY) + ->label('sdk', new Method( + namespace: 'projects', + name: 'createDevKey', + description: '', + auth: [AuthType::ADMIN], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_CREATED, + model: Response::MODEL_DEV_KEY + ) + ], + contentType: ContentType::JSON + )) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.', false) diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/DeleteDevKey.php similarity index 71% rename from src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php rename to src/Appwrite/Platform/Modules/Projects/Http/DevKeys/DeleteDevKey.php index 0fae2c30aa..a81382e280 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/DeleteKey.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/DeleteDevKey.php @@ -1,8 +1,11 @@ desc('Delete dev key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'deleteDevKey') - ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) - ->label('sdk.response.model', Response::MODEL_NONE) + ->label('sdk', new Method( + namespace: 'projects', + name: 'deleteDevKey', + description: '', + auth: [AuthType::ADMIN], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_CREATED, + model: Response::MODEL_NONE + ) + ], + contentType: ContentType::NONE + )) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('keyId', '', new UID(), 'Key unique ID.') ->inject('response') diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/GetDevKey.php similarity index 70% rename from src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php rename to src/Appwrite/Platform/Modules/Projects/Http/DevKeys/GetDevKey.php index 4f2a7fa9ca..d67166c324 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/GetKey.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/GetDevKey.php @@ -1,8 +1,11 @@ desc('Get dev key') ->groups(['api', 'projects']) ->label('scope', 'projects.read') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'getDevKey') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_DEV_KEY) + ->label('sdk', new Method( + namespace: 'projects', + name: 'getDevKey', + description: '', + auth: [AuthType::ADMIN], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_DEV_KEY + ) + ], + contentType: ContentType::JSON + )) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('keyId', '', new UID(), 'Key unique ID.') ->inject('response') diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/ListDevKeys.php similarity index 68% rename from src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php rename to src/Appwrite/Platform/Modules/Projects/Http/DevKeys/ListDevKeys.php index e9df3167ea..9dc281ef07 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/ListKeys.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/ListDevKeys.php @@ -1,8 +1,11 @@ desc('List dev keys') ->groups(['api', 'projects']) ->label('scope', 'projects.read') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'listDevKeys') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_DEV_KEY_LIST) + ->label('sdk', new Method( + namespace: 'projects', + name: 'listDevKeys', + description: '', + auth: [AuthType::ADMIN], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_CREATED, + model: Response::MODEL_DEV_KEY_LIST + ) + ], + contentType: ContentType::JSON + )) ->param('projectId', '', new UID(), 'Project unique ID.') ->inject('response') ->inject('dbForPlatform') diff --git a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/UpdateDevKey.php similarity index 76% rename from src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php rename to src/Appwrite/Platform/Modules/Projects/Http/DevKeys/UpdateDevKey.php index 6d17429084..3f7952c79d 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Http/DevKeys/UpdateKey.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/UpdateDevKey.php @@ -1,8 +1,11 @@ desc('Update dev key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'projects') - ->label('sdk.method', 'updateDevKey') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_DEV_KEY) + ->label('sdk', new Method( + namespace: 'projects', + name: 'updateDevKey', + description: '', + auth: [AuthType::ADMIN], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_CREATED, + model: Response::MODEL_DEV_KEY + ) + ], + contentType: ContentType::JSON + )) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('keyId', '', new UID(), 'Key unique ID.') ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') diff --git a/src/Appwrite/Platform/Modules/DevKeys/Module.php b/src/Appwrite/Platform/Modules/Projects/Module.php similarity index 62% rename from src/Appwrite/Platform/Modules/DevKeys/Module.php rename to src/Appwrite/Platform/Modules/Projects/Module.php index 796ec50186..2a550acf54 100644 --- a/src/Appwrite/Platform/Modules/DevKeys/Module.php +++ b/src/Appwrite/Platform/Modules/Projects/Module.php @@ -1,8 +1,8 @@ type = Service::TYPE_HTTP; + $this->addAction(CreateDevKey::getName(), new CreateDevKey()); + $this->addAction(UpdateDevKey::getName(), new UpdateDevKey()); + $this->addAction(GetDevKey::getName(), new GetDevKey()); + $this->addAction(ListDevKeys::getName(), new ListDevKeys()); + $this->addAction(DeleteDevKey::getName(), new DeleteDevKey()); + } +} From ebd6b1b01336c7a08e9683986f2e1a6e44360768 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 4 Feb 2025 11:32:02 +0000 Subject: [PATCH 072/110] chore: added missing import --- .../Platform/Modules/Projects/Http/DevKeys/CreateDevKey.php | 1 + .../Platform/Modules/Projects/Http/DevKeys/DeleteDevKey.php | 1 + .../Platform/Modules/Projects/Http/DevKeys/GetDevKey.php | 1 + .../Platform/Modules/Projects/Http/DevKeys/ListDevKeys.php | 1 + .../Platform/Modules/Projects/Http/DevKeys/UpdateDevKey.php | 1 + 5 files changed, 5 insertions(+) diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/CreateDevKey.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/CreateDevKey.php index 3f28cd6f81..2d8b00a38f 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/CreateDevKey.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/CreateDevKey.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Modules\Projects\Http\DevKeys; use Appwrite\Extend\Exception; +use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/DeleteDevKey.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/DeleteDevKey.php index a81382e280..ebfbd6639f 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/DeleteDevKey.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/DeleteDevKey.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Modules\Projects\Http\DevKeys; use Appwrite\Extend\Exception; +use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/GetDevKey.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/GetDevKey.php index d67166c324..8ee46beee7 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/GetDevKey.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/GetDevKey.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Modules\Projects\Http\DevKeys; use Appwrite\Extend\Exception; +use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/ListDevKeys.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/ListDevKeys.php index 9dc281ef07..eea1249b54 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/ListDevKeys.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/ListDevKeys.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Modules\Projects\Http\DevKeys; use Appwrite\Extend\Exception; +use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/UpdateDevKey.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/UpdateDevKey.php index 3f7952c79d..3559d4edf4 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/UpdateDevKey.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/UpdateDevKey.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Modules\Projects\Http\DevKeys; use Appwrite\Extend\Exception; +use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; From fccc6d560536ac9c687b820d0d3286c1c172402a Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 4 Feb 2025 11:45:41 +0000 Subject: [PATCH 073/110] chore: added docs for devkeys --- docs/references/projects/create-dev-key.md | 1 + docs/references/projects/delete-dev-key.md | 1 + docs/references/projects/get-dev-key.md | 1 + docs/references/projects/list-dev-keys.md | 1 + docs/references/projects/update-dev-key.md | 1 + .../Platform/Modules/Projects/Http/DevKeys/CreateDevKey.php | 2 +- .../Platform/Modules/Projects/Http/DevKeys/DeleteDevKey.php | 2 +- .../Platform/Modules/Projects/Http/DevKeys/GetDevKey.php | 2 +- .../Platform/Modules/Projects/Http/DevKeys/ListDevKeys.php | 2 +- .../Platform/Modules/Projects/Http/DevKeys/UpdateDevKey.php | 2 +- 10 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 docs/references/projects/create-dev-key.md create mode 100644 docs/references/projects/delete-dev-key.md create mode 100644 docs/references/projects/get-dev-key.md create mode 100644 docs/references/projects/list-dev-keys.md create mode 100644 docs/references/projects/update-dev-key.md diff --git a/docs/references/projects/create-dev-key.md b/docs/references/projects/create-dev-key.md new file mode 100644 index 0000000000..4d6afd789b --- /dev/null +++ b/docs/references/projects/create-dev-key.md @@ -0,0 +1 @@ +Create a new project dev key. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development. Strictly meant for development purposes only. \ No newline at end of file diff --git a/docs/references/projects/delete-dev-key.md b/docs/references/projects/delete-dev-key.md new file mode 100644 index 0000000000..f78b373739 --- /dev/null +++ b/docs/references/projects/delete-dev-key.md @@ -0,0 +1 @@ +Delete a project's dev key by its unique ID. Once deleted, the key will no longer allow bypassing of rate limits and better logging of errors. \ No newline at end of file diff --git a/docs/references/projects/get-dev-key.md b/docs/references/projects/get-dev-key.md new file mode 100644 index 0000000000..ad7ede3cfa --- /dev/null +++ b/docs/references/projects/get-dev-key.md @@ -0,0 +1 @@ +Get a project's dev key by its unique ID. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development. \ No newline at end of file diff --git a/docs/references/projects/list-dev-keys.md b/docs/references/projects/list-dev-keys.md new file mode 100644 index 0000000000..7fcf3fa9f4 --- /dev/null +++ b/docs/references/projects/list-dev-keys.md @@ -0,0 +1 @@ +List all the project's dev keys. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development. \ No newline at end of file diff --git a/docs/references/projects/update-dev-key.md b/docs/references/projects/update-dev-key.md new file mode 100644 index 0000000000..55ea27f9c1 --- /dev/null +++ b/docs/references/projects/update-dev-key.md @@ -0,0 +1 @@ +Update a project's dev key by its unique ID. Use this endpoint to update a project's dev key name or expiration time. \ No newline at end of file diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/CreateDevKey.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/CreateDevKey.php index 2d8b00a38f..90a8969774 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/CreateDevKey.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/CreateDevKey.php @@ -38,7 +38,7 @@ class CreateDevKey extends Action ->label('sdk', new Method( namespace: 'projects', name: 'createDevKey', - description: '', + description: '/docs/references/projects/create-dev-key.md', auth: [AuthType::ADMIN], responses: [ new SDKResponse( diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/DeleteDevKey.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/DeleteDevKey.php index ebfbd6639f..27b2b83396 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/DeleteDevKey.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/DeleteDevKey.php @@ -33,7 +33,7 @@ class DeleteDevKey extends Action ->label('sdk', new Method( namespace: 'projects', name: 'deleteDevKey', - description: '', + description: '/docs/references/projects/delete-dev-key.md', auth: [AuthType::ADMIN], responses: [ new SDKResponse( diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/GetDevKey.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/GetDevKey.php index 8ee46beee7..5e5960b121 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/GetDevKey.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/GetDevKey.php @@ -33,7 +33,7 @@ class GetDevKey extends Action ->label('sdk', new Method( namespace: 'projects', name: 'getDevKey', - description: '', + description: '/docs/references/projects/get-dev-key.md', auth: [AuthType::ADMIN], responses: [ new SDKResponse( diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/ListDevKeys.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/ListDevKeys.php index eea1249b54..267284fa2c 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/ListDevKeys.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/ListDevKeys.php @@ -34,7 +34,7 @@ class ListDevKeys extends Action ->label('sdk', new Method( namespace: 'projects', name: 'listDevKeys', - description: '', + description: '/docs/references/projects/list-dev-keys.md', auth: [AuthType::ADMIN], responses: [ new SDKResponse( diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/UpdateDevKey.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/UpdateDevKey.php index 3559d4edf4..e2601a1693 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/UpdateDevKey.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/UpdateDevKey.php @@ -34,7 +34,7 @@ class UpdateDevKey extends Action ->label('sdk', new Method( namespace: 'projects', name: 'updateDevKey', - description: '', + description: '/docs/references/projects/update-dev-key.md', auth: [AuthType::ADMIN], responses: [ new SDKResponse( From cea308ef7161c062da66e5d1afebde9a47b84e00 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 4 Feb 2025 11:55:05 +0000 Subject: [PATCH 074/110] chore: remove confusing comment --- app/controllers/general.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 6775740c7d..2599c124bc 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -990,8 +990,6 @@ App::error() $type = $error->getType(); - // TODO filter out secrets and server details when not in development - // but devKey is provided $output = (App::isDevelopment()) ? [ 'message' => $message, 'code' => $code, From 4aa3a51ef026cc23a35ef13ff492a6ef69e85d76 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 5 Feb 2025 09:52:43 +0000 Subject: [PATCH 075/110] chore: refactor module structure --- .../Http/DevKeys/{CreateDevKey.php => Create.php} | 2 +- .../Http/DevKeys/{DeleteDevKey.php => Delete.php} | 2 +- .../Projects/Http/DevKeys/{GetDevKey.php => Get.php} | 2 +- .../Http/DevKeys/{UpdateDevKey.php => Update.php} | 2 +- .../Http/DevKeys/{ListDevKeys.php => XList.php} | 2 +- .../Platform/Modules/Projects/Services/Http.php | 10 +++++----- 6 files changed, 10 insertions(+), 10 deletions(-) rename src/Appwrite/Platform/Modules/Projects/Http/DevKeys/{CreateDevKey.php => Create.php} (98%) rename src/Appwrite/Platform/Modules/Projects/Http/DevKeys/{DeleteDevKey.php => Delete.php} (98%) rename src/Appwrite/Platform/Modules/Projects/Http/DevKeys/{GetDevKey.php => Get.php} (98%) rename src/Appwrite/Platform/Modules/Projects/Http/DevKeys/{UpdateDevKey.php => Update.php} (98%) rename src/Appwrite/Platform/Modules/Projects/Http/DevKeys/{ListDevKeys.php => XList.php} (98%) diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/CreateDevKey.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php similarity index 98% rename from src/Appwrite/Platform/Modules/Projects/Http/DevKeys/CreateDevKey.php rename to src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php index 90a8969774..c29515d3b1 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/CreateDevKey.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php @@ -19,7 +19,7 @@ use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Validator\Text; -class CreateDevKey extends Action +class Create extends Action { use HTTP; public static function getName() diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/DeleteDevKey.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php similarity index 98% rename from src/Appwrite/Platform/Modules/Projects/Http/DevKeys/DeleteDevKey.php rename to src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php index 27b2b83396..4f4a021a75 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/DeleteDevKey.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php @@ -14,7 +14,7 @@ use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -class DeleteDevKey extends Action +class Delete extends Action { use HTTP; public static function getName() diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/GetDevKey.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php similarity index 98% rename from src/Appwrite/Platform/Modules/Projects/Http/DevKeys/GetDevKey.php rename to src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php index 5e5960b121..f0cec5370d 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/GetDevKey.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php @@ -14,7 +14,7 @@ use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -class GetDevKey extends Action +class Get extends Action { use HTTP; public static function getName() diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/UpdateDevKey.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php similarity index 98% rename from src/Appwrite/Platform/Modules/Projects/Http/DevKeys/UpdateDevKey.php rename to src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php index e2601a1693..9a8f488864 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/UpdateDevKey.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php @@ -16,7 +16,7 @@ use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Validator\Text; -class UpdateDevKey extends Action +class Update extends Action { use HTTP; public static function getName() diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/ListDevKeys.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php similarity index 98% rename from src/Appwrite/Platform/Modules/Projects/Http/DevKeys/ListDevKeys.php rename to src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php index 267284fa2c..9d86b8bdd0 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/ListDevKeys.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php @@ -15,7 +15,7 @@ use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -class ListDevKeys extends Action +class XList extends Action { use HTTP; public static function getName() diff --git a/src/Appwrite/Platform/Modules/Projects/Services/Http.php b/src/Appwrite/Platform/Modules/Projects/Services/Http.php index a2b8fffa0a..cec8ed6d16 100644 --- a/src/Appwrite/Platform/Modules/Projects/Services/Http.php +++ b/src/Appwrite/Platform/Modules/Projects/Services/Http.php @@ -2,11 +2,11 @@ namespace Appwrite\Platform\Modules\Projects\Services; -use Appwrite\Platform\Modules\Projects\Http\DevKeys\CreateDevKey; -use Appwrite\Platform\Modules\Projects\Http\DevKeys\DeleteDevKey; -use Appwrite\Platform\Modules\Projects\Http\DevKeys\GetDevKey; -use Appwrite\Platform\Modules\Projects\Http\DevKeys\ListDevKeys; -use Appwrite\Platform\Modules\Projects\Http\DevKeys\UpdateDevKey; +use Appwrite\Platform\Modules\Projects\Http\DevKeys\Create as CreateDevKey; +use Appwrite\Platform\Modules\Projects\Http\DevKeys\Delete as DeleteDevKey; +use Appwrite\Platform\Modules\Projects\Http\DevKeys\Get as GetDevKey; +use Appwrite\Platform\Modules\Projects\Http\DevKeys\Update as UpdateDevKey; +use Appwrite\Platform\Modules\Projects\Http\DevKeys\XList as ListDevKeys; use Utopia\Platform\Service; class Http extends Service From 1d571e470bec29d46e89620bbc6c67948a992725 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 6 Feb 2025 10:30:00 +0000 Subject: [PATCH 076/110] chore: update module endpoint descriptions --- docs/references/projects/create-dev-key.md | 1 - docs/references/projects/delete-dev-key.md | 1 - docs/references/projects/get-dev-key.md | 1 - docs/references/projects/list-dev-keys.md | 1 - docs/references/projects/update-dev-key.md | 1 - .../Platform/Modules/Projects/Http/DevKeys/Create.php | 4 +++- .../Platform/Modules/Projects/Http/DevKeys/Delete.php | 4 +++- .../Platform/Modules/Projects/Http/DevKeys/Get.php | 4 +++- .../Platform/Modules/Projects/Http/DevKeys/Update.php | 4 +++- .../Platform/Modules/Projects/Http/DevKeys/XList.php | 4 +++- src/Appwrite/SDK/Method.php | 10 ++++++---- src/Appwrite/Specification/Format/OpenAPI3.php | 5 ++++- src/Appwrite/Specification/Format/Swagger2.php | 5 ++++- 13 files changed, 29 insertions(+), 16 deletions(-) delete mode 100644 docs/references/projects/create-dev-key.md delete mode 100644 docs/references/projects/delete-dev-key.md delete mode 100644 docs/references/projects/get-dev-key.md delete mode 100644 docs/references/projects/list-dev-keys.md delete mode 100644 docs/references/projects/update-dev-key.md diff --git a/docs/references/projects/create-dev-key.md b/docs/references/projects/create-dev-key.md deleted file mode 100644 index 4d6afd789b..0000000000 --- a/docs/references/projects/create-dev-key.md +++ /dev/null @@ -1 +0,0 @@ -Create a new project dev key. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development. Strictly meant for development purposes only. \ No newline at end of file diff --git a/docs/references/projects/delete-dev-key.md b/docs/references/projects/delete-dev-key.md deleted file mode 100644 index f78b373739..0000000000 --- a/docs/references/projects/delete-dev-key.md +++ /dev/null @@ -1 +0,0 @@ -Delete a project's dev key by its unique ID. Once deleted, the key will no longer allow bypassing of rate limits and better logging of errors. \ No newline at end of file diff --git a/docs/references/projects/get-dev-key.md b/docs/references/projects/get-dev-key.md deleted file mode 100644 index ad7ede3cfa..0000000000 --- a/docs/references/projects/get-dev-key.md +++ /dev/null @@ -1 +0,0 @@ -Get a project's dev key by its unique ID. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development. \ No newline at end of file diff --git a/docs/references/projects/list-dev-keys.md b/docs/references/projects/list-dev-keys.md deleted file mode 100644 index 7fcf3fa9f4..0000000000 --- a/docs/references/projects/list-dev-keys.md +++ /dev/null @@ -1 +0,0 @@ -List all the project's dev keys. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development. \ No newline at end of file diff --git a/docs/references/projects/update-dev-key.md b/docs/references/projects/update-dev-key.md deleted file mode 100644 index 55ea27f9c1..0000000000 --- a/docs/references/projects/update-dev-key.md +++ /dev/null @@ -1 +0,0 @@ -Update a project's dev key by its unique ID. Use this endpoint to update a project's dev key name or expiration time. \ No newline at end of file diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php index c29515d3b1..778f87e417 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php @@ -38,7 +38,9 @@ class Create extends Action ->label('sdk', new Method( namespace: 'projects', name: 'createDevKey', - description: '/docs/references/projects/create-dev-key.md', + description: <<label('sdk', new Method( namespace: 'projects', name: 'deleteDevKey', - description: '/docs/references/projects/delete-dev-key.md', + description: <<label('sdk', new Method( namespace: 'projects', name: 'getDevKey', - description: '/docs/references/projects/get-dev-key.md', + description: <<label('sdk', new Method( namespace: 'projects', name: 'updateDevKey', - description: '/docs/references/projects/update-dev-key.md', + description: <<label('sdk', new Method( namespace: 'projects', name: 'listDevKeys', - description: '/docs/references/projects/list-dev-keys.md', + description: <<getDescriptionFilePath(); + if (\str_ends_with($desc, '.md')) { + $descPath = $this->getDescriptionFilePath(); - if (empty($descPath)) { - self::$errors[] = "Error with {$this->getRouteName()} method: Description file not found at {$desc}"; - return; + if (empty($descPath)) { + self::$errors[] = "Error with {$this->getRouteName()} method: Description file not found at {$desc}"; + return; + } } } diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index bd5405539d..6785afa868 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -177,11 +177,14 @@ class OpenAPI3 extends Format $namespace = $sdk->getNamespace() ?? 'default'; + $desc = $desc ?? ''; + $descContents = \str_ends_with($desc, '.md') ? \file_get_contents($desc) : ''; + $temp = [ 'summary' => $route->getDesc(), 'operationId' => $namespace . ucfirst($method), 'tags' => [$namespace], - 'description' => ($desc) ? \file_get_contents($desc) : '', + 'description' => $descContents, 'responses' => [], 'x-appwrite' => [ // Appwrite related metadata 'method' => $method, diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 7277e3ab2b..3565b5da75 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -173,13 +173,16 @@ class Swagger2 extends Format $namespace = $sdk->getNamespace() ?? 'default'; + $desc = $desc ?? ''; + $descContents = \str_ends_with($desc, '.md') ? \file_get_contents($desc) : ''; + $temp = [ 'summary' => $route->getDesc(), 'operationId' => $namespace . ucfirst($method), 'consumes' => [], 'produces' => [], 'tags' => [$namespace], - 'description' => ($desc) ? \file_get_contents($desc) : '', + 'description' => $descContents, 'responses' => [], 'x-appwrite' => [ // Appwrite related metadata 'method' => $method, From 37dbbf226e71e927fa0b2a3b5fcf036a7c89856b Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 6 Feb 2025 10:42:33 +0000 Subject: [PATCH 077/110] chore: fix: descContents handling --- src/Appwrite/Specification/Format/OpenAPI3.php | 2 +- src/Appwrite/Specification/Format/Swagger2.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 6785afa868..e60d342b0b 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -178,7 +178,7 @@ class OpenAPI3 extends Format $namespace = $sdk->getNamespace() ?? 'default'; $desc = $desc ?? ''; - $descContents = \str_ends_with($desc, '.md') ? \file_get_contents($desc) : ''; + $descContents = \str_ends_with($desc, '.md') ? \file_get_contents($desc) : $desc; $temp = [ 'summary' => $route->getDesc(), diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 3565b5da75..fae164f0a6 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -174,7 +174,7 @@ class Swagger2 extends Format $namespace = $sdk->getNamespace() ?? 'default'; $desc = $desc ?? ''; - $descContents = \str_ends_with($desc, '.md') ? \file_get_contents($desc) : ''; + $descContents = \str_ends_with($desc, '.md') ? \file_get_contents($desc) : $desc; $temp = [ 'summary' => $route->getDesc(), From a0138455bcaeb124c5ef858dcc325b403d4927c1 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 13 Feb 2025 19:21:15 +0000 Subject: [PATCH 078/110] fix: queueForStats naming --- app/controllers/general.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 18a9736e3c..324de05e6e 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -508,7 +508,7 @@ App::init() ->inject('isResourceBlocked') ->inject('previewHostname') ->inject('devKey') - ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, callable $isResourceBlocked, string $previewHostname, Document $devKey) { + ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, callable $isResourceBlocked, string $previewHostname, Document $devKey) { /* * Appwrite Router */ @@ -742,7 +742,7 @@ App::options() ->inject('isResourceBlocked') ->inject('previewHostname') ->inject('devKey') - ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname, Document $devKey) { + ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname, Document $devKey) { /* * Appwrite Router */ @@ -777,7 +777,7 @@ App::error() ->inject('log') ->inject('queueForUsage') ->inject('devKey') - ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Usage $queueForUsage) { + ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, StatsUsage $queueForStatsUsage) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); $route = $utopia->getRoute(); $class = \get_class($error); From 392803b5e88fcdd4d15aab035e21d833cda44df2 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 13 Feb 2025 19:24:00 +0000 Subject: [PATCH 079/110] chore: fix merge conflicts --- app/controllers/general.php | 2 +- app/controllers/shared/api.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 324de05e6e..ea27e8633e 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -775,7 +775,7 @@ App::error() ->inject('project') ->inject('logger') ->inject('log') - ->inject('queueForUsage') + ->inject('queueForStatsUsage') ->inject('devKey') ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, StatsUsage $queueForStatsUsage) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 43a1d88536..9dd804ea2f 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -441,7 +441,7 @@ App::init() ->inject('timelimit') ->inject('mode') ->inject('devKey') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, callable $timelimit, string $mode, Document $devKey) use ($usageDatabaseListener, $eventDatabaseListener) { + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, string $mode, Document $devKey) use ($usageDatabaseListener, $eventDatabaseListener) { $route = $utopia->getRoute(); From f67d32fd3510a951d4dfb8813e4f5e2f6879984f Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 13 Feb 2025 19:32:33 +0000 Subject: [PATCH 080/110] chore: updated devkeys test for shared mode --- .github/workflows/tests.yml | 43 +++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0a70389a8c..97b2af35ab 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -176,7 +176,7 @@ jobs: docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ - appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug + appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude=devKeys e2e_shared_mode_test: name: E2E Shared Mode Service Test @@ -250,11 +250,46 @@ jobs: name: E2E Service Test (Dev Keys) runs-on: ubuntu-latest needs: setup + strategy: + fail-fast: false + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: Load Cache + uses: actions/cache@v4 + with: + key: ${{ env.CACHE_KEY }} + path: /tmp/${{ env.IMAGE }}.tar + fail-on-cache-miss: true + + - name: Load and Start Appwrite + run: | + docker load --input /tmp/${{ env.IMAGE }}.tar + sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env + docker compose up -d + sleep 30 + + - name: Run Projects tests with dev keys in ${{ matrix.tables-mode }} table mode + run: | + echo "Using project tables" + export _APP_DATABASE_SHARED_TABLES= + export _APP_DATABASE_SHARED_TABLES_V1= + + docker compose exec -T \ + -e _APP_DATABASE_SHARED_TABLES \ + -e _APP_DATABASE_SHARED_TABLES_V1 \ + appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys + + e2e_dev_keys_shared_mode: + name: E2E Shared Mode Test (Dev Keys) + runs-on: ubuntu-latest + needs: [ setup, check_database_changes ] + if: needs.check_database_changes.outputs.database_changed == 'true' strategy: fail-fast: false matrix: tables-mode: [ - 'Project', 'Shared V1', 'Shared V2', ] @@ -286,10 +321,6 @@ jobs: echo "Using shared tables V2" export _APP_DATABASE_SHARED_TABLES=database_db_main export _APP_DATABASE_SHARED_TABLES_V1= - else - echo "Using project tables" - export _APP_DATABASE_SHARED_TABLES= - export _APP_DATABASE_SHARED_TABLES_V1= fi docker compose exec -T \ From 46c4c7b28b9e92341b718ed40c2464596efe2c57 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 13 Feb 2025 19:36:01 +0000 Subject: [PATCH 081/110] chore: fix name --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 97b2af35ab..8bbeaaf8a3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -282,7 +282,7 @@ jobs: appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys e2e_dev_keys_shared_mode: - name: E2E Shared Mode Test (Dev Keys) + name: E2E Shared Mode Service Test (Dev Keys) runs-on: ubuntu-latest needs: [ setup, check_database_changes ] if: needs.check_database_changes.outputs.database_changed == 'true' From f5172f8462c846bbc2709713587b0748857ca2e7 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 25 Feb 2025 12:30:11 +0000 Subject: [PATCH 082/110] chore: regenerate specs --- app/config/specs/open-api3-latest-client.json | 10 +- .../specs/open-api3-latest-console.json | 627 ++++++++++++++--- app/config/specs/open-api3-latest-server.json | 178 ++--- app/config/specs/swagger2-latest-client.json | 10 +- app/config/specs/swagger2-latest-console.json | 630 +++++++++++++++--- app/config/specs/swagger2-latest-server.json | 180 ++--- 6 files changed, 1295 insertions(+), 340 deletions(-) diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index 820b1f55e0..316fe13116 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -3383,7 +3383,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "schema": { "type": "string", @@ -3404,7 +3404,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3423,7 +3424,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ] }, "in": "path" @@ -4365,7 +4367,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 7f57dfc437..fc448d8ae8 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -3387,7 +3387,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "schema": { "type": "string", @@ -3408,7 +3408,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3427,7 +3428,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ] }, "in": "path" @@ -7823,7 +7825,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -11585,7 +11587,7 @@ }, "x-appwrite": { "method": "getCertificate", - "weight": 134, + "weight": 133, "cookies": false, "type": "", "deprecated": false, @@ -11692,7 +11694,7 @@ }, "x-appwrite": { "method": "getPubSub", - "weight": 130, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -11718,54 +11720,6 @@ ] } }, - "\/health\/queue": { - "get": { - "summary": "Get queue", - "operationId": "healthGetQueue", - "tags": [ - "health" - ], - "description": "Check the Appwrite queue messaging servers are up and connection is successful.", - "responses": { - "200": { - "description": "Health Status", - "content": { - "application\/json": { - "schema": { - "$ref": "#\/components\/schemas\/healthStatus" - } - } - } - } - }, - "x-appwrite": { - "method": "getQueue", - "weight": 129, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "health\/get-queue.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "health.read", - "platforms": [ - "server" - ], - "packaging": false, - "auth": { - "Project": [] - } - }, - "security": [ - { - "Project": [], - "Key": [] - } - ] - } - }, "\/health\/queue\/builds": { "get": { "summary": "Get builds queue", @@ -11788,7 +11742,7 @@ }, "x-appwrite": { "method": "getQueueBuilds", - "weight": 136, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -11849,7 +11803,7 @@ }, "x-appwrite": { "method": "getQueueCertificates", - "weight": 135, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -11910,7 +11864,7 @@ }, "x-appwrite": { "method": "getQueueDatabases", - "weight": 137, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -11982,7 +11936,7 @@ }, "x-appwrite": { "method": "getQueueDeletes", - "weight": 138, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -12081,8 +12035,9 @@ "v1-audits", "v1-mails", "v1-functions", - "v1-usage", - "v1-usage-dump", + "v1-stats-resources", + "v1-stats-usage", + "v1-stats-usage-dump", "v1-webhooks", "v1-certificates", "v1-builds", @@ -12130,7 +12085,7 @@ }, "x-appwrite": { "method": "getQueueFunctions", - "weight": 142, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -12191,7 +12146,7 @@ }, "x-appwrite": { "method": "getQueueLogs", - "weight": 133, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -12252,7 +12207,7 @@ }, "x-appwrite": { "method": "getQueueMails", - "weight": 139, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -12313,7 +12268,7 @@ }, "x-appwrite": { "method": "getQueueMessaging", - "weight": 140, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -12374,7 +12329,7 @@ }, "x-appwrite": { "method": "getQueueMigrations", - "weight": 141, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -12413,14 +12368,14 @@ ] } }, - "\/health\/queue\/usage": { + "\/health\/queue\/stats-resources": { "get": { - "summary": "Get usage queue", - "operationId": "healthGetQueueUsage", + "summary": "Get stats resources queue", + "operationId": "healthGetQueueStatsResources", "tags": [ "health" ], - "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.", "responses": { "200": { "description": "Health Queue", @@ -12434,13 +12389,13 @@ } }, "x-appwrite": { - "method": "getQueueUsage", - "weight": 143, + "method": "getQueueStatsResources", + "weight": 142, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md", + "demo": "health\/get-queue-stats-resources.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -12474,10 +12429,71 @@ ] } }, - "\/health\/queue\/usage-dump": { + "\/health\/queue\/stats-usage": { + "get": { + "summary": "Get stats usage queue", + "operationId": "healthGetQueueUsage", + "tags": [ + "health" + ], + "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "responses": { + "200": { + "description": "Health Queue", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/healthQueue" + } + } + } + } + }, + "x-appwrite": { + "method": "getQueueUsage", + "weight": 143, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "health\/get-queue-usage.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "health.read", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "threshold", + "description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 5000 + }, + "in": "query" + } + ] + } + }, + "\/health\/queue\/stats-usage-dump": { "get": { "summary": "Get usage dump queue", - "operationId": "healthGetQueueUsageDump", + "operationId": "healthGetQueueStatsUsageDump", "tags": [ "health" ], @@ -12495,13 +12511,13 @@ } }, "x-appwrite": { - "method": "getQueueUsageDump", + "method": "getQueueStatsUsageDump", "weight": 144, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage-dump.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md", + "demo": "health\/get-queue-stats-usage-dump.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -12557,7 +12573,7 @@ }, "x-appwrite": { "method": "getQueueWebhooks", - "weight": 132, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -12714,7 +12730,7 @@ }, "x-appwrite": { "method": "getTime", - "weight": 131, + "weight": 130, "cookies": false, "type": "", "deprecated": false, @@ -17609,17 +17625,17 @@ }, "endpoint": { "type": "string", - "description": "Source's Appwrite Endpoint", + "description": "Source Appwrite endpoint", "x-example": "https:\/\/example.com" }, "projectId": { "type": "string", - "description": "Source's Project ID", + "description": "Source Project ID", "x-example": "" }, "apiKey": { "type": "string", - "description": "Source's API Key", + "description": "Source API Key", "x-example": "" } }, @@ -20322,6 +20338,368 @@ } } }, + "\/projects\/{projectId}\/dev-keys": { + "get": { + "summary": "List dev keys", + "operationId": "projectsListDevKeys", + "tags": [ + "projects" + ], + "description": "", + "responses": { + "201": { + "description": "Dev Keys List", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/devKeyList" + } + } + } + } + }, + "x-appwrite": { + "method": "listDevKeys", + "weight": 395, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/list-dev-keys.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterList all the project\\'s dev keys. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development.'", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.read", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + }, + "post": { + "summary": "Create dev key", + "operationId": "projectsCreateDevKey", + "tags": [ + "projects" + ], + "description": "", + "responses": { + "201": { + "description": "DevKey", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/devKey" + } + } + } + } + }, + "x-appwrite": { + "method": "createDevKey", + "weight": 392, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/create-dev-key.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new project dev key. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development. Strictly meant for development purposes only.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.write", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Key name. Max length: 128 chars.", + "x-example": "" + }, + "expire": { + "type": "string", + "description": "Expiration time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format.", + "x-example": null + } + }, + "required": [ + "name", + "expire" + ] + } + } + } + } + } + }, + "\/projects\/{projectId}\/dev-keys\/{keyId}": { + "get": { + "summary": "Get dev key", + "operationId": "projectsGetDevKey", + "tags": [ + "projects" + ], + "description": "", + "responses": { + "200": { + "description": "DevKey", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/devKey" + } + } + } + } + }, + "x-appwrite": { + "method": "getDevKey", + "weight": 394, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/get-dev-key.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterGet a project\\'s dev key by its unique ID. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.read", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "keyId", + "description": "Key unique ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + }, + "put": { + "summary": "Update dev key", + "operationId": "projectsUpdateDevKey", + "tags": [ + "projects" + ], + "description": "", + "responses": { + "201": { + "description": "DevKey", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/devKey" + } + } + } + } + }, + "x-appwrite": { + "method": "updateDevKey", + "weight": 393, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/update-dev-key.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterUpdate a project\\'s dev key by its unique ID. Use this endpoint to update a project\\'s dev key name or expiration time.'", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.write", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "keyId", + "description": "Key unique ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Key name. Max length: 128 chars.", + "x-example": "" + }, + "expire": { + "type": "string", + "description": "Expiration time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format.", + "x-example": null + } + }, + "required": [ + "name", + "expire" + ] + } + } + } + } + }, + "delete": { + "summary": "Delete dev key", + "operationId": "projectsDeleteDevKey", + "tags": [ + "projects" + ], + "description": "", + "responses": { + "201": { + "description": "File" + } + }, + "x-appwrite": { + "method": "deleteDevKey", + "weight": 396, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/delete-dev-key.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterDelete a project\\'s dev key by its unique ID. Once deleted, the key will no longer allow bypassing of rate limits and better logging of errors.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.write", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "keyId", + "description": "Key unique ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + } + }, "\/projects\/{projectId}\/jwts": { "post": { "summary": "Create JWT", @@ -31084,6 +31462,30 @@ "keys" ] }, + "devKeyList": { + "description": "Dev Keys List", + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Total number of keys documents that matched your query.", + "x-example": 5, + "format": "int32" + }, + "keys": { + "type": "array", + "description": "List of keys.", + "items": { + "$ref": "#\/components\/schemas\/devKey" + }, + "x-example": "" + } + }, + "required": [ + "total", + "keys" + ] + }, "platformList": { "description": "Platforms List", "type": "object", @@ -34797,6 +35199,14 @@ }, "x-example": {} }, + "devKeys": { + "type": "array", + "description": "List of dev keys.", + "items": { + "$ref": "#\/components\/schemas\/devKey" + }, + "x-example": {} + }, "smtpEnabled": { "type": "boolean", "description": "Status for custom SMTP", @@ -34975,6 +35385,7 @@ "platforms", "webhooks", "keys", + "devKeys", "smtpEnabled", "smtpSenderName", "smtpSenderEmail", @@ -35164,6 +35575,56 @@ "sdks" ] }, + "devKey": { + "description": "DevKey", + "type": "object", + "properties": { + "$id": { + "type": "string", + "description": "Key ID.", + "x-example": "5e5ea5c16897e" + }, + "$createdAt": { + "type": "string", + "description": "Key creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "$updatedAt": { + "type": "string", + "description": "Key update date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "name": { + "type": "string", + "description": "Key name.", + "x-example": "My API Key" + }, + "expire": { + "type": "string", + "description": "Key expiration date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "secret": { + "type": "string", + "description": "Secret key.", + "x-example": "919c2d18fb5d4...a2ae413da83346ad2" + }, + "accessedAt": { + "type": "string", + "description": "Most recent access date in ISO 8601 format. This attribute is only updated again after 24 hours.", + "x-example": "2020-10-15T06:38:00.000+00:00" + } + }, + "required": [ + "$id", + "$createdAt", + "$updatedAt", + "name", + "expire", + "secret", + "accessedAt" + ] + }, "mockNumber": { "description": "Mock Number", "type": "object", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 68d408762a..280c080514 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -3077,7 +3077,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "schema": { "type": "string", @@ -3098,7 +3098,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3117,7 +3118,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ] }, "in": "path" @@ -7381,7 +7383,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -10461,7 +10463,7 @@ }, "x-appwrite": { "method": "getCertificate", - "weight": 134, + "weight": 133, "cookies": false, "type": "", "deprecated": false, @@ -10570,7 +10572,7 @@ }, "x-appwrite": { "method": "getPubSub", - "weight": 130, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -10597,55 +10599,6 @@ ] } }, - "\/health\/queue": { - "get": { - "summary": "Get queue", - "operationId": "healthGetQueue", - "tags": [ - "health" - ], - "description": "Check the Appwrite queue messaging servers are up and connection is successful.", - "responses": { - "200": { - "description": "Health Status", - "content": { - "application\/json": { - "schema": { - "$ref": "#\/components\/schemas\/healthStatus" - } - } - } - } - }, - "x-appwrite": { - "method": "getQueue", - "weight": 129, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "health\/get-queue.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "health.read", - "platforms": [ - "server" - ], - "packaging": false, - "auth": { - "Project": [], - "Key": [] - } - }, - "security": [ - { - "Project": [], - "Key": [] - } - ] - } - }, "\/health\/queue\/builds": { "get": { "summary": "Get builds queue", @@ -10668,7 +10621,7 @@ }, "x-appwrite": { "method": "getQueueBuilds", - "weight": 136, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -10730,7 +10683,7 @@ }, "x-appwrite": { "method": "getQueueCertificates", - "weight": 135, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -10792,7 +10745,7 @@ }, "x-appwrite": { "method": "getQueueDatabases", - "weight": 137, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -10865,7 +10818,7 @@ }, "x-appwrite": { "method": "getQueueDeletes", - "weight": 138, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -10966,8 +10919,9 @@ "v1-audits", "v1-mails", "v1-functions", - "v1-usage", - "v1-usage-dump", + "v1-stats-resources", + "v1-stats-usage", + "v1-stats-usage-dump", "v1-webhooks", "v1-certificates", "v1-builds", @@ -11015,7 +10969,7 @@ }, "x-appwrite": { "method": "getQueueFunctions", - "weight": 142, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -11077,7 +11031,7 @@ }, "x-appwrite": { "method": "getQueueLogs", - "weight": 133, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -11139,7 +11093,7 @@ }, "x-appwrite": { "method": "getQueueMails", - "weight": 139, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -11201,7 +11155,7 @@ }, "x-appwrite": { "method": "getQueueMessaging", - "weight": 140, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -11263,7 +11217,7 @@ }, "x-appwrite": { "method": "getQueueMigrations", - "weight": 141, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -11303,14 +11257,14 @@ ] } }, - "\/health\/queue\/usage": { + "\/health\/queue\/stats-resources": { "get": { - "summary": "Get usage queue", - "operationId": "healthGetQueueUsage", + "summary": "Get stats resources queue", + "operationId": "healthGetQueueStatsResources", "tags": [ "health" ], - "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.", "responses": { "200": { "description": "Health Queue", @@ -11324,13 +11278,13 @@ } }, "x-appwrite": { - "method": "getQueueUsage", - "weight": 143, + "method": "getQueueStatsResources", + "weight": 142, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md", + "demo": "health\/get-queue-stats-resources.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -11365,10 +11319,72 @@ ] } }, - "\/health\/queue\/usage-dump": { + "\/health\/queue\/stats-usage": { + "get": { + "summary": "Get stats usage queue", + "operationId": "healthGetQueueUsage", + "tags": [ + "health" + ], + "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "responses": { + "200": { + "description": "Health Queue", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/healthQueue" + } + } + } + } + }, + "x-appwrite": { + "method": "getQueueUsage", + "weight": 143, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "health\/get-queue-usage.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "health.read", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "threshold", + "description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 5000 + }, + "in": "query" + } + ] + } + }, + "\/health\/queue\/stats-usage-dump": { "get": { "summary": "Get usage dump queue", - "operationId": "healthGetQueueUsageDump", + "operationId": "healthGetQueueStatsUsageDump", "tags": [ "health" ], @@ -11386,13 +11402,13 @@ } }, "x-appwrite": { - "method": "getQueueUsageDump", + "method": "getQueueStatsUsageDump", "weight": 144, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage-dump.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md", + "demo": "health\/get-queue-stats-usage-dump.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -11449,7 +11465,7 @@ }, "x-appwrite": { "method": "getQueueWebhooks", - "weight": 132, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -11609,7 +11625,7 @@ }, "x-appwrite": { "method": "getTime", - "weight": 131, + "weight": 130, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index c0980c44ce..8960bfaa5c 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -3557,7 +3557,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "type": "string", "x-example": "amex", @@ -3577,7 +3577,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3596,7 +3597,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ], "in": "path" }, @@ -4547,7 +4549,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 94b0d55199..284e863880 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -3577,7 +3577,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "type": "string", "x-example": "amex", @@ -3597,7 +3597,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3616,7 +3617,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ], "in": "path" }, @@ -8012,7 +8014,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -11810,7 +11812,7 @@ }, "x-appwrite": { "method": "getCertificate", - "weight": 134, + "weight": 133, "cookies": false, "type": "", "deprecated": false, @@ -11919,7 +11921,7 @@ }, "x-appwrite": { "method": "getPubSub", - "weight": 130, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -11945,56 +11947,6 @@ ] } }, - "\/health\/queue": { - "get": { - "summary": "Get queue", - "operationId": "healthGetQueue", - "consumes": [ - "application\/json" - ], - "produces": [ - "application\/json" - ], - "tags": [ - "health" - ], - "description": "Check the Appwrite queue messaging servers are up and connection is successful.", - "responses": { - "200": { - "description": "Health Status", - "schema": { - "$ref": "#\/definitions\/healthStatus" - } - } - }, - "x-appwrite": { - "method": "getQueue", - "weight": 129, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "health\/get-queue.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "health.read", - "platforms": [ - "server" - ], - "packaging": false, - "auth": { - "Project": [] - } - }, - "security": [ - { - "Project": [], - "Key": [] - } - ] - } - }, "\/health\/queue\/builds": { "get": { "summary": "Get builds queue", @@ -12019,7 +11971,7 @@ }, "x-appwrite": { "method": "getQueueBuilds", - "weight": 136, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -12080,7 +12032,7 @@ }, "x-appwrite": { "method": "getQueueCertificates", - "weight": 135, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -12141,7 +12093,7 @@ }, "x-appwrite": { "method": "getQueueDatabases", - "weight": 137, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -12211,7 +12163,7 @@ }, "x-appwrite": { "method": "getQueueDeletes", - "weight": 138, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -12309,8 +12261,9 @@ "v1-audits", "v1-mails", "v1-functions", - "v1-usage", - "v1-usage-dump", + "v1-stats-resources", + "v1-stats-usage", + "v1-stats-usage-dump", "v1-webhooks", "v1-certificates", "v1-builds", @@ -12357,7 +12310,7 @@ }, "x-appwrite": { "method": "getQueueFunctions", - "weight": 142, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -12418,7 +12371,7 @@ }, "x-appwrite": { "method": "getQueueLogs", - "weight": 133, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -12479,7 +12432,7 @@ }, "x-appwrite": { "method": "getQueueMails", - "weight": 139, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -12540,7 +12493,7 @@ }, "x-appwrite": { "method": "getQueueMessaging", - "weight": 140, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -12601,7 +12554,7 @@ }, "x-appwrite": { "method": "getQueueMigrations", - "weight": 141, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -12638,10 +12591,10 @@ ] } }, - "\/health\/queue\/usage": { + "\/health\/queue\/stats-resources": { "get": { - "summary": "Get usage queue", - "operationId": "healthGetQueueUsage", + "summary": "Get stats resources queue", + "operationId": "healthGetQueueStatsResources", "consumes": [ "application\/json" ], @@ -12651,7 +12604,7 @@ "tags": [ "health" ], - "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.", "responses": { "200": { "description": "Health Queue", @@ -12661,13 +12614,13 @@ } }, "x-appwrite": { - "method": "getQueueUsage", - "weight": 143, + "method": "getQueueStatsResources", + "weight": 142, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md", + "demo": "health\/get-queue-stats-resources.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -12699,10 +12652,71 @@ ] } }, - "\/health\/queue\/usage-dump": { + "\/health\/queue\/stats-usage": { + "get": { + "summary": "Get stats usage queue", + "operationId": "healthGetQueueUsage", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "health" + ], + "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "responses": { + "200": { + "description": "Health Queue", + "schema": { + "$ref": "#\/definitions\/healthQueue" + } + } + }, + "x-appwrite": { + "method": "getQueueUsage", + "weight": 143, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "health\/get-queue-usage.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "health.read", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "threshold", + "description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.", + "required": false, + "type": "integer", + "format": "int32", + "default": 5000, + "in": "query" + } + ] + } + }, + "\/health\/queue\/stats-usage-dump": { "get": { "summary": "Get usage dump queue", - "operationId": "healthGetQueueUsageDump", + "operationId": "healthGetQueueStatsUsageDump", "consumes": [ "application\/json" ], @@ -12722,13 +12736,13 @@ } }, "x-appwrite": { - "method": "getQueueUsageDump", + "method": "getQueueStatsUsageDump", "weight": 144, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage-dump.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md", + "demo": "health\/get-queue-stats-usage-dump.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -12784,7 +12798,7 @@ }, "x-appwrite": { "method": "getQueueWebhooks", - "weight": 132, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -12945,7 +12959,7 @@ }, "x-appwrite": { "method": "getTime", - "weight": 131, + "weight": 130, "cookies": false, "type": "", "deprecated": false, @@ -18070,19 +18084,19 @@ }, "endpoint": { "type": "string", - "description": "Source's Appwrite Endpoint", + "description": "Source Appwrite endpoint", "default": null, "x-example": "https:\/\/example.com" }, "projectId": { "type": "string", - "description": "Source's Project ID", + "description": "Source Project ID", "default": null, "x-example": "" }, "apiKey": { "type": "string", - "description": "Source's API Key", + "description": "Source API Key", "default": null, "x-example": "" } @@ -20797,6 +20811,367 @@ ] } }, + "\/projects\/{projectId}\/dev-keys": { + "get": { + "summary": "List dev keys", + "operationId": "projectsListDevKeys", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "projects" + ], + "description": "", + "responses": { + "201": { + "description": "Dev Keys List", + "schema": { + "$ref": "#\/definitions\/devKeyList" + } + } + }, + "x-appwrite": { + "method": "listDevKeys", + "weight": 395, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/list-dev-keys.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterList all the project\\'s dev keys. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development.'", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.read", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + }, + "post": { + "summary": "Create dev key", + "operationId": "projectsCreateDevKey", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "projects" + ], + "description": "", + "responses": { + "201": { + "description": "DevKey", + "schema": { + "$ref": "#\/definitions\/devKey" + } + } + }, + "x-appwrite": { + "method": "createDevKey", + "weight": 392, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/create-dev-key.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new project dev key. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development. Strictly meant for development purposes only.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.write", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Key name. Max length: 128 chars.", + "default": null, + "x-example": "" + }, + "expire": { + "type": "string", + "description": "Expiration time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format.", + "default": null, + "x-example": null + } + }, + "required": [ + "name", + "expire" + ] + } + } + ] + } + }, + "\/projects\/{projectId}\/dev-keys\/{keyId}": { + "get": { + "summary": "Get dev key", + "operationId": "projectsGetDevKey", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "projects" + ], + "description": "", + "responses": { + "200": { + "description": "DevKey", + "schema": { + "$ref": "#\/definitions\/devKey" + } + } + }, + "x-appwrite": { + "method": "getDevKey", + "weight": 394, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/get-dev-key.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterGet a project\\'s dev key by its unique ID. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.read", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "keyId", + "description": "Key unique ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + }, + "put": { + "summary": "Update dev key", + "operationId": "projectsUpdateDevKey", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "projects" + ], + "description": "", + "responses": { + "201": { + "description": "DevKey", + "schema": { + "$ref": "#\/definitions\/devKey" + } + } + }, + "x-appwrite": { + "method": "updateDevKey", + "weight": 393, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/update-dev-key.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterUpdate a project\\'s dev key by its unique ID. Use this endpoint to update a project\\'s dev key name or expiration time.'", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.write", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "keyId", + "description": "Key unique ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Key name. Max length: 128 chars.", + "default": null, + "x-example": "" + }, + "expire": { + "type": "string", + "description": "Expiration time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format.", + "default": null, + "x-example": null + } + }, + "required": [ + "name", + "expire" + ] + } + } + ] + }, + "delete": { + "summary": "Delete dev key", + "operationId": "projectsDeleteDevKey", + "consumes": [ + "application\/json" + ], + "produces": [], + "tags": [ + "projects" + ], + "description": "", + "responses": { + "201": { + "description": "File", + "schema": { + "type": "file" + } + } + }, + "x-appwrite": { + "method": "deleteDevKey", + "weight": 396, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/delete-dev-key.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterDelete a project\\'s dev key by its unique ID. Once deleted, the key will no longer allow bypassing of rate limits and better logging of errors.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.write", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "keyId", + "description": "Key unique ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + } + }, "\/projects\/{projectId}\/jwts": { "post": { "summary": "Create JWT", @@ -31590,6 +31965,31 @@ "keys" ] }, + "devKeyList": { + "description": "Dev Keys List", + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Total number of keys documents that matched your query.", + "x-example": 5, + "format": "int32" + }, + "keys": { + "type": "array", + "description": "List of keys.", + "items": { + "type": "object", + "$ref": "#\/definitions\/devKey" + }, + "x-example": "" + } + }, + "required": [ + "total", + "keys" + ] + }, "platformList": { "description": "Platforms List", "type": "object", @@ -35334,6 +35734,15 @@ }, "x-example": {} }, + "devKeys": { + "type": "array", + "description": "List of dev keys.", + "items": { + "type": "object", + "$ref": "#\/definitions\/devKey" + }, + "x-example": {} + }, "smtpEnabled": { "type": "boolean", "description": "Status for custom SMTP", @@ -35512,6 +35921,7 @@ "platforms", "webhooks", "keys", + "devKeys", "smtpEnabled", "smtpSenderName", "smtpSenderEmail", @@ -35701,6 +36111,56 @@ "sdks" ] }, + "devKey": { + "description": "DevKey", + "type": "object", + "properties": { + "$id": { + "type": "string", + "description": "Key ID.", + "x-example": "5e5ea5c16897e" + }, + "$createdAt": { + "type": "string", + "description": "Key creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "$updatedAt": { + "type": "string", + "description": "Key update date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "name": { + "type": "string", + "description": "Key name.", + "x-example": "My API Key" + }, + "expire": { + "type": "string", + "description": "Key expiration date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "secret": { + "type": "string", + "description": "Secret key.", + "x-example": "919c2d18fb5d4...a2ae413da83346ad2" + }, + "accessedAt": { + "type": "string", + "description": "Most recent access date in ISO 8601 format. This attribute is only updated again after 24 hours.", + "x-example": "2020-10-15T06:38:00.000+00:00" + } + }, + "required": [ + "$id", + "$createdAt", + "$updatedAt", + "name", + "expire", + "secret", + "accessedAt" + ] + }, "mockNumber": { "description": "Mock Number", "type": "object", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index e38495629c..749f87553b 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -3261,7 +3261,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "type": "string", "x-example": "amex", @@ -3281,7 +3281,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3300,7 +3301,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ], "in": "path" }, @@ -7552,7 +7554,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -10689,7 +10691,7 @@ }, "x-appwrite": { "method": "getCertificate", - "weight": 134, + "weight": 133, "cookies": false, "type": "", "deprecated": false, @@ -10800,7 +10802,7 @@ }, "x-appwrite": { "method": "getPubSub", - "weight": 130, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -10827,57 +10829,6 @@ ] } }, - "\/health\/queue": { - "get": { - "summary": "Get queue", - "operationId": "healthGetQueue", - "consumes": [ - "application\/json" - ], - "produces": [ - "application\/json" - ], - "tags": [ - "health" - ], - "description": "Check the Appwrite queue messaging servers are up and connection is successful.", - "responses": { - "200": { - "description": "Health Status", - "schema": { - "$ref": "#\/definitions\/healthStatus" - } - } - }, - "x-appwrite": { - "method": "getQueue", - "weight": 129, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "health\/get-queue.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "health.read", - "platforms": [ - "server" - ], - "packaging": false, - "auth": { - "Project": [], - "Key": [] - } - }, - "security": [ - { - "Project": [], - "Key": [] - } - ] - } - }, "\/health\/queue\/builds": { "get": { "summary": "Get builds queue", @@ -10902,7 +10853,7 @@ }, "x-appwrite": { "method": "getQueueBuilds", - "weight": 136, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -10964,7 +10915,7 @@ }, "x-appwrite": { "method": "getQueueCertificates", - "weight": 135, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -11026,7 +10977,7 @@ }, "x-appwrite": { "method": "getQueueDatabases", - "weight": 137, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -11097,7 +11048,7 @@ }, "x-appwrite": { "method": "getQueueDeletes", - "weight": 138, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -11197,8 +11148,9 @@ "v1-audits", "v1-mails", "v1-functions", - "v1-usage", - "v1-usage-dump", + "v1-stats-resources", + "v1-stats-usage", + "v1-stats-usage-dump", "v1-webhooks", "v1-certificates", "v1-builds", @@ -11245,7 +11197,7 @@ }, "x-appwrite": { "method": "getQueueFunctions", - "weight": 142, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -11307,7 +11259,7 @@ }, "x-appwrite": { "method": "getQueueLogs", - "weight": 133, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -11369,7 +11321,7 @@ }, "x-appwrite": { "method": "getQueueMails", - "weight": 139, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -11431,7 +11383,7 @@ }, "x-appwrite": { "method": "getQueueMessaging", - "weight": 140, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -11493,7 +11445,7 @@ }, "x-appwrite": { "method": "getQueueMigrations", - "weight": 141, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -11531,10 +11483,10 @@ ] } }, - "\/health\/queue\/usage": { + "\/health\/queue\/stats-resources": { "get": { - "summary": "Get usage queue", - "operationId": "healthGetQueueUsage", + "summary": "Get stats resources queue", + "operationId": "healthGetQueueStatsResources", "consumes": [ "application\/json" ], @@ -11544,7 +11496,7 @@ "tags": [ "health" ], - "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.", "responses": { "200": { "description": "Health Queue", @@ -11554,13 +11506,13 @@ } }, "x-appwrite": { - "method": "getQueueUsage", - "weight": 143, + "method": "getQueueStatsResources", + "weight": 142, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md", + "demo": "health\/get-queue-stats-resources.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -11593,10 +11545,72 @@ ] } }, - "\/health\/queue\/usage-dump": { + "\/health\/queue\/stats-usage": { + "get": { + "summary": "Get stats usage queue", + "operationId": "healthGetQueueUsage", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "health" + ], + "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "responses": { + "200": { + "description": "Health Queue", + "schema": { + "$ref": "#\/definitions\/healthQueue" + } + } + }, + "x-appwrite": { + "method": "getQueueUsage", + "weight": 143, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "health\/get-queue-usage.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "health.read", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "threshold", + "description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.", + "required": false, + "type": "integer", + "format": "int32", + "default": 5000, + "in": "query" + } + ] + } + }, + "\/health\/queue\/stats-usage-dump": { "get": { "summary": "Get usage dump queue", - "operationId": "healthGetQueueUsageDump", + "operationId": "healthGetQueueStatsUsageDump", "consumes": [ "application\/json" ], @@ -11616,13 +11630,13 @@ } }, "x-appwrite": { - "method": "getQueueUsageDump", + "method": "getQueueStatsUsageDump", "weight": 144, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage-dump.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md", + "demo": "health\/get-queue-stats-usage-dump.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -11679,7 +11693,7 @@ }, "x-appwrite": { "method": "getQueueWebhooks", - "weight": 132, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -11843,7 +11857,7 @@ }, "x-appwrite": { "method": "getTime", - "weight": 131, + "weight": 130, "cookies": false, "type": "", "deprecated": false, From a62c489a720ad9bfcdd534bc0c3a4d372d76cbad Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 22 Mar 2025 18:38:12 +0530 Subject: [PATCH 083/110] fix: missing import. --- app/init/resources.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/init/resources.php b/app/init/resources.php index f2dcb7d73b..4f64db1ca6 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -28,6 +28,7 @@ use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; +use Utopia\Database\DateTime as DatabaseDateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; From 26142f4f21d3dffcbd3bb3bf426a7d0e20d6a700 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 22 Mar 2025 18:46:13 +0530 Subject: [PATCH 084/110] =?UTF-8?q?revert:=20local=20dockerfile=20changes!?= =?UTF-8?q?=20=F0=9F=A4=A6=E2=80=8D=E2=99=82=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index b16568e955..88d5ed030b 100755 --- a/Dockerfile +++ b/Dockerfile @@ -38,12 +38,10 @@ COPY --from=composer /usr/local/src/vendor /usr/src/code/vendor COPY ./app /usr/src/code/app COPY ./public /usr/src/code/public COPY ./bin /usr/local/bin -#COPY ./docs /usr/src/code/docs -COPY ./src /usr/src/code/srcs +COPY ./docs /usr/src/code/docs +COPY ./src /usr/src/code/src COPY ./dev /usr/src/code/dev -COPY ./vendor/utopia-php/framework/src/App.php /usr/src/code/vendor/utopia-php/framework/src/App.php - # Set Volumes RUN mkdir -p /storage/uploads && \ mkdir -p /storage/cache && \ From 7b7fd218f540f819ceaf7f6f7c036f50bbf55554 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 24 Mar 2025 05:06:18 +0000 Subject: [PATCH 085/110] chore: formatting + updating specs --- .github/workflows/tests.yml | 2 +- app/config/specs/open-api3-latest-client.json | 2 +- .../specs/open-api3-latest-console.json | 21 ++++++++++++++--- app/config/specs/open-api3-latest-server.json | 13 ++++++++++- app/config/specs/swagger2-latest-client.json | 2 +- app/config/specs/swagger2-latest-console.json | 23 ++++++++++++++++--- app/config/specs/swagger2-latest-server.json | 14 ++++++++++- app/controllers/general.php | 2 +- 8 files changed, 67 insertions(+), 12 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8bbeaaf8a3..d4dcd7cac2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -240,7 +240,7 @@ jobs: export _APP_DATABASE_SHARED_TABLES=database_db_main export _APP_DATABASE_SHARED_TABLES_V1= fi - + docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index 316fe13116..d74fc6ffe5 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -1,7 +1,7 @@ { "openapi": "3.0.0", "info": { - "version": "1.6.1", + "version": "1.6.2", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index d6e3b6c2e3..691140ddd9 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -1,7 +1,7 @@ { "openapi": "3.0.0", "info": { - "version": "1.6.1", + "version": "1.6.2", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -11080,6 +11080,11 @@ "type": "string", "description": "Variable value. Max length: 8192 chars.", "x-example": "" + }, + "secret": { + "type": "boolean", + "description": "Is secret? Secret variables can only be updated or deleted, they cannot be read.", + "x-example": false } }, "required": [ @@ -18712,6 +18717,11 @@ "type": "string", "description": "Variable value. Max length: 8192 chars.", "x-example": "" + }, + "secret": { + "type": "boolean", + "description": "Is secret? Secret variables can only be updated or deleted, they cannot be read.", + "x-example": false } }, "required": [ @@ -19056,8 +19066,7 @@ "description": "Project Region.", "x-example": "default", "enum": [ - "default", - "fra" + "default" ], "x-enum-name": null, "x-enum-keys": [] @@ -35776,6 +35785,11 @@ "description": "Variable value.", "x-example": "myPa$$word1" }, + "secret": { + "type": "boolean", + "description": "Variable secret flag. Secret variables can only be updated or deleted, but never read.", + "x-example": false + }, "resourceType": { "type": "string", "description": "Service to which the variable belongs. Possible values are \"project\", \"function\"", @@ -35793,6 +35807,7 @@ "$updatedAt", "key", "value", + "secret", "resourceType", "resourceId" ] diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 3d32d3e978..2a89cc56e0 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -1,7 +1,7 @@ { "openapi": "3.0.0", "info": { - "version": "1.6.1", + "version": "1.6.2", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -9946,6 +9946,11 @@ "type": "string", "description": "Variable value. Max length: 8192 chars.", "x-example": "" + }, + "secret": { + "type": "boolean", + "description": "Is secret? Secret variables can only be updated or deleted, they cannot be read.", + "x-example": false } }, "required": [ @@ -25525,6 +25530,11 @@ "description": "Variable value.", "x-example": "myPa$$word1" }, + "secret": { + "type": "boolean", + "description": "Variable secret flag. Secret variables can only be updated or deleted, but never read.", + "x-example": false + }, "resourceType": { "type": "string", "description": "Service to which the variable belongs. Possible values are \"project\", \"function\"", @@ -25542,6 +25552,7 @@ "$updatedAt", "key", "value", + "secret", "resourceType", "resourceId" ] diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 8960bfaa5c..78e8b71ad1 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "version": "1.6.1", + "version": "1.6.2", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index a0149a39a0..76497471ca 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "version": "1.6.1", + "version": "1.6.2", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -11258,6 +11258,12 @@ "description": "Variable value. Max length: 8192 chars.", "default": null, "x-example": "" + }, + "secret": { + "type": "boolean", + "description": "Is secret? Secret variables can only be updated or deleted, they cannot be read.", + "default": false, + "x-example": false } }, "required": [ @@ -19171,6 +19177,12 @@ "description": "Variable value. Max length: 8192 chars.", "default": null, "x-example": "" + }, + "secret": { + "type": "boolean", + "description": "Is secret? Secret variables can only be updated or deleted, they cannot be read.", + "default": false, + "x-example": false } }, "required": [ @@ -19522,8 +19534,7 @@ "default": "default", "x-example": "default", "enum": [ - "default", - "fra" + "default" ], "x-enum-name": null, "x-enum-keys": [] @@ -36312,6 +36323,11 @@ "description": "Variable value.", "x-example": "myPa$$word1" }, + "secret": { + "type": "boolean", + "description": "Variable secret flag. Secret variables can only be updated or deleted, but never read.", + "x-example": false + }, "resourceType": { "type": "string", "description": "Service to which the variable belongs. Possible values are \"project\", \"function\"", @@ -36329,6 +36345,7 @@ "$updatedAt", "key", "value", + "secret", "resourceType", "resourceId" ] diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 83757c94f4..2a2a86281e 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "version": "1.6.1", + "version": "1.6.2", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -10127,6 +10127,12 @@ "description": "Variable value. Max length: 8192 chars.", "default": null, "x-example": "" + }, + "secret": { + "type": "boolean", + "description": "Is secret? Secret variables can only be updated or deleted, they cannot be read.", + "default": false, + "x-example": false } }, "required": [ @@ -26037,6 +26043,11 @@ "description": "Variable value.", "x-example": "myPa$$word1" }, + "secret": { + "type": "boolean", + "description": "Variable secret flag. Secret variables can only be updated or deleted, but never read.", + "x-example": false + }, "resourceType": { "type": "string", "description": "Service to which the variable belongs. Possible values are \"project\", \"function\"", @@ -26054,6 +26065,7 @@ "$updatedAt", "key", "value", + "secret", "resourceType", "resourceId" ] diff --git a/app/controllers/general.php b/app/controllers/general.php index 1752ef24b3..f37504d90f 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1009,7 +1009,7 @@ App::error() $type = $error->getType(); - $output = (App::isDevelopment()) ? [ + $output = App::isDevelopment() ? [ 'message' => $message, 'code' => $code, 'file' => $file, From 7aecd3814aede58d88cb0675f3863439feb364e9 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 9 Apr 2025 09:15:36 +0000 Subject: [PATCH 086/110] fix: minor mistakes in devkeys naming and initialization --- app/config/collections/platform.php | 2 +- app/init/constants.php | 1 + .../Platform/Modules/Projects/Http/DevKeys/Create.php | 2 +- .../Platform/Modules/Projects/Http/DevKeys/Delete.php | 4 ++-- src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php | 2 +- .../Platform/Modules/Projects/Http/DevKeys/Update.php | 4 ++-- .../Platform/Modules/Projects/Http/DevKeys/XList.php | 6 +++--- src/Appwrite/Utopia/Response.php | 2 +- src/Appwrite/Utopia/Response/Model/DevKey.php | 2 +- 9 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/config/collections/platform.php b/app/config/collections/platform.php index 24223acbd3..39a5889b67 100644 --- a/app/config/collections/platform.php +++ b/app/config/collections/platform.php @@ -722,7 +722,7 @@ return [ 'format' => '', 'size' => Database::LENGTH_KEY, 'signed' => true, - 'required' => false, + 'required' => true, 'default' => 0, 'array' => false, 'filters' => [], diff --git a/app/init/constants.php b/app/init/constants.php index 5e4edfd97d..9d3a08ed76 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -26,6 +26,7 @@ const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1_000_000; const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls +const APP_LIMIT_DEV_KEYS = 5000; // Default maximum number of dev keys to return in list API calls const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php index 778f87e417..baf126cf5b 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php @@ -55,7 +55,7 @@ class Create extends Action ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.', false) ->inject('response') ->inject('dbForPlatform') - ->callback(fn ($projectId, $name, $expire, $response, $dbForPlatform) => $this->action($projectId, $name, $expire, $response, $dbForPlatform)); + ->callback([$this, 'action']); } public function action(string $projectId, string $name, ?string $expire, Response $response, Database $dbForPlatform) diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php index 635a0e77b9..4fabcc4db1 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php @@ -39,7 +39,7 @@ class Delete extends Action auth: [AuthType::ADMIN], responses: [ new SDKResponse( - code: Response::STATUS_CODE_CREATED, + code: Response::STATUS_CODE_NOCONTENT, model: Response::MODEL_NONE ) ], @@ -49,7 +49,7 @@ class Delete extends Action ->param('keyId', '', new UID(), 'Key unique ID.') ->inject('response') ->inject('dbForPlatform') - ->callback(fn ($projectId, $keyId, $response, $dbForPlatform) => $this->action($projectId, $keyId, $response, $dbForPlatform)); + ->callback([$this, 'action']); } public function action(string $projectId, string $keyId, Response $response, Database $dbForPlatform) diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php index eda87238d6..bfbfd6e76c 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php @@ -49,7 +49,7 @@ class Get extends Action ->param('keyId', '', new UID(), 'Key unique ID.') ->inject('response') ->inject('dbForPlatform') - ->callback(fn ($projectId, $keyId, $response, $dbForPlatform) => $this->action($projectId, $keyId, $response, $dbForPlatform)); + ->callback([$this, 'action']); } public function action(string $projectId, string $keyId, Response $response, Database $dbForPlatform) diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php index 7941657a44..7513805913 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php @@ -40,7 +40,7 @@ class Update extends Action auth: [AuthType::ADMIN], responses: [ new SDKResponse( - code: Response::STATUS_CODE_CREATED, + code: Response::STATUS_CODE_OK, model: Response::MODEL_DEV_KEY ) ], @@ -52,7 +52,7 @@ class Update extends Action ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.') ->inject('response') ->inject('dbForPlatform') - ->callback(fn ($projectId, $keyId, $name, $expire, $response, $dbForPlatform) => $this->action($projectId, $keyId, $name, $expire, $response, $dbForPlatform)); + ->callback([$this, 'action']); } public function action(string $projectId, string $keyId, string $name, ?string $expire, Response $response, Database $dbForPlatform) { diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php index 77315a1522..7221d54cda 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php @@ -40,7 +40,7 @@ class XList extends Action auth: [AuthType::ADMIN], responses: [ new SDKResponse( - code: Response::STATUS_CODE_CREATED, + code: Response::STATUS_CODE_OK, model: Response::MODEL_DEV_KEY_LIST ) ], @@ -49,7 +49,7 @@ class XList extends Action ->param('projectId', '', new UID(), 'Project unique ID.') ->inject('response') ->inject('dbForPlatform') - ->callback(fn ($projectId, $response, $dbForPlatform) => $this->action($projectId, $response, $dbForPlatform)); + ->callback([$this, 'action']); } public function action(string $projectId, Response $response, Database $dbForPlatform) @@ -63,7 +63,7 @@ class XList extends Action $keys = $dbForPlatform->find('devKeys', [ Query::equal('projectInternalId', [$project->getInternalId()]), - Query::limit(5000), + Query::limit(APP_LIMIT_DEV_KEYS), ]); $response->dynamic(new Document([ diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index c196331346..55270f5ff9 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -366,7 +366,7 @@ class Response extends SwooleResponse ->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT, true, false)) ->setModel(new BaseList('Webhooks List', self::MODEL_WEBHOOK_LIST, 'webhooks', self::MODEL_WEBHOOK, true, false)) ->setModel(new BaseList('API Keys List', self::MODEL_KEY_LIST, 'keys', self::MODEL_KEY, true, false)) - ->setModel(new BaseList('Dev Keys List', self::MODEL_DEV_KEY_LIST, 'keys', self::MODEL_DEV_KEY, true, false)) + ->setModel(new BaseList('Dev Keys List', self::MODEL_DEV_KEY_LIST, 'devKeys', self::MODEL_DEV_KEY, true, false)) ->setModel(new BaseList('Auth Providers List', self::MODEL_AUTH_PROVIDER_LIST, 'platforms', self::MODEL_AUTH_PROVIDER, true, false)) ->setModel(new BaseList('Platforms List', self::MODEL_PLATFORM_LIST, 'platforms', self::MODEL_PLATFORM, true, false)) ->setModel(new BaseList('Countries List', self::MODEL_COUNTRY_LIST, 'countries', self::MODEL_COUNTRY)) diff --git a/src/Appwrite/Utopia/Response/Model/DevKey.php b/src/Appwrite/Utopia/Response/Model/DevKey.php index 50ce750b6a..d1074bd7d3 100644 --- a/src/Appwrite/Utopia/Response/Model/DevKey.php +++ b/src/Appwrite/Utopia/Response/Model/DevKey.php @@ -37,7 +37,7 @@ class DevKey extends Model 'type' => self::TYPE_STRING, 'description' => 'Key name.', 'default' => '', - 'example' => 'My API Key', + 'example' => 'Dev API Key', ]) ->addRule('expire', [ 'type' => self::TYPE_DATETIME, From 84595e625db50dabcc8f8f3023435b113203ed1c Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 9 Apr 2025 09:31:05 +0000 Subject: [PATCH 087/110] chore: use getDocument instead to fetch devKey --- .../Platform/Modules/Projects/Http/DevKeys/Delete.php | 10 +++++----- .../Platform/Modules/Projects/Http/DevKeys/Get.php | 10 +++++----- .../Platform/Modules/Projects/Http/DevKeys/Update.php | 8 ++------ 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php index 4fabcc4db1..cce26a8de6 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php @@ -9,7 +9,6 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\Database\Database; -use Utopia\Database\Query; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -61,10 +60,11 @@ class Delete extends Action throw new Exception(Exception::PROJECT_NOT_FOUND); } - $key = $dbForPlatform->findOne('devKeys', [ - Query::equal('$id', [$keyId]), - Query::equal('projectInternalId', [$project->getInternalId()]), - ]); + $key = $dbForPlatform->getDocument('devKeys', $keyId); + + if ($key === false || $key->isEmpty() || $key->getAttribute('projectInternalId') !== $project->getInternalId()) { + throw new Exception(Exception::KEY_NOT_FOUND); + } if ($key === false || $key->isEmpty()) { throw new Exception(Exception::KEY_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php index bfbfd6e76c..ebc0c1bcbb 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php @@ -9,7 +9,6 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\Database\Database; -use Utopia\Database\Query; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -61,10 +60,11 @@ class Get extends Action throw new Exception(Exception::PROJECT_NOT_FOUND); } - $key = $dbForPlatform->findOne('devKeys', [ - Query::equal('$id', [$keyId]), - Query::equal('projectInternalId', [$project->getInternalId()]), - ]); + $key = $dbForPlatform->getDocument('devKeys', $keyId); + + if ($key === false || $key->isEmpty() || $key->getAttribute('projectInternalId') !== $project->getInternalId()) { + throw new Exception(Exception::KEY_NOT_FOUND); + } if ($key === false || $key->isEmpty()) { throw new Exception(Exception::KEY_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php index 7513805913..c8560098cd 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php @@ -9,7 +9,6 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\Database\Database; -use Utopia\Database\Query; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; @@ -63,12 +62,9 @@ class Update extends Action throw new Exception(Exception::PROJECT_NOT_FOUND); } - $key = $dbForPlatform->findOne('devKeys', [ - Query::equal('$id', [$keyId]), - Query::equal('projectInternalId', [$project->getInternalId()]), - ]); + $key = $dbForPlatform->getDocument('devKeys', $keyId); - if ($key === false || $key->isEmpty()) { + if ($key === false || $key->isEmpty() || $key->getAttribute('projectInternalId') !== $project->getInternalId()) { throw new Exception(Exception::KEY_NOT_FOUND); } From 4a1bf2eb74b236a6e4a54388151f2327086aa1f9 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 9 Apr 2025 09:44:30 +0000 Subject: [PATCH 088/110] fix: permissions on create devKey --- .../Platform/Modules/Projects/Http/DevKeys/Create.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php index baf126cf5b..df1cb069e0 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php @@ -53,12 +53,13 @@ class Create extends Action ->param('projectId', '', new UID(), 'Project unique ID.') ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.', false) + ->inject('user') ->inject('response') ->inject('dbForPlatform') ->callback([$this, 'action']); } - public function action(string $projectId, string $name, ?string $expire, Response $response, Database $dbForPlatform) + public function action(string $projectId, string $name, ?string $expire, Document $user, Response $response, Database $dbForPlatform) { $project = $dbForPlatform->getDocument('projects', $projectId); @@ -69,9 +70,9 @@ class Create extends Action $key = new Document([ '$id' => ID::unique(), '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), + Permission::read(Role::user($user->getId())), + Permission::update(Role::user($user->getId())), + Permission::delete(Role::user($user->getId())), ], 'projectInternalId' => $project->getInternalId(), 'projectId' => $project->getId(), From b4b356e61f3c09b4490d2a1b2a692e5a2f3acbcb Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 9 Apr 2025 11:14:02 +0000 Subject: [PATCH 089/110] chore: add queries and search to listing devkeys --- .env | 2 +- app/config/collections/platform.php | 18 +++++ app/init/constants.php | 1 - .../Modules/Projects/Http/DevKeys/Create.php | 4 +- .../Modules/Projects/Http/DevKeys/Update.php | 3 +- .../Modules/Projects/Http/DevKeys/XList.php | 29 +++++-- .../e2e/Services/Projects/ProjectsDevKeys.php | 75 ++++++++++++++++++- 7 files changed, 121 insertions(+), 11 deletions(-) diff --git a/.env b/.env index c10c12613b..d18e63c56e 100644 --- a/.env +++ b/.env @@ -15,7 +15,7 @@ _APP_SYSTEM_TEAM_EMAIL=team@appwrite.io _APP_EMAIL_SECURITY=security@appwrite.io _APP_EMAIL_CERTIFICATES=certificates@appwrite.io _APP_SYSTEM_RESPONSE_FORMAT= -_APP_OPTIONS_ABUSE=disabled +_APP_OPTIONS_ABUSE=enabled _APP_OPTIONS_ROUTER_PROTECTION=disabled _APP_OPTIONS_FORCE_HTTPS=disabled _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled diff --git a/app/config/collections/platform.php b/app/config/collections/platform.php index 39a5889b67..7dff5ae547 100644 --- a/app/config/collections/platform.php +++ b/app/config/collections/platform.php @@ -782,6 +782,17 @@ return [ 'array' => true, 'filters' => [], ], + [ + '$id' => ID::custom('search'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], ], 'indexes' => [ [ @@ -798,6 +809,13 @@ return [ 'lengths' => [], 'orders' => [], ], + [ + '$id' => ID::custom('_key_search'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [], + 'orders' => [], + ], ], ], diff --git a/app/init/constants.php b/app/init/constants.php index 9d3a08ed76..5e4edfd97d 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -26,7 +26,6 @@ const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1_000_000; const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls -const APP_LIMIT_DEV_KEYS = 5000; // Default maximum number of dev keys to return in list API calls const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php index df1cb069e0..9636a166c1 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php @@ -67,8 +67,9 @@ class Create extends Action throw new Exception(Exception::PROJECT_NOT_FOUND); } + $devKeyId = ID::unique(); $key = new Document([ - '$id' => ID::unique(), + '$id' => $devKeyId, '$permissions' => [ Permission::read(Role::user($user->getId())), Permission::update(Role::user($user->getId())), @@ -79,6 +80,7 @@ class Create extends Action 'name' => $name, 'expire' => $expire, 'sdks' => [], + 'search' => implode('', [$name, $project->getId(), $devKeyId]), 'accessedAt' => null, 'secret' => \bin2hex(\random_bytes(128)), ]); diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php index c8560098cd..c556578bc2 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php @@ -70,7 +70,8 @@ class Update extends Action $key ->setAttribute('name', $name) - ->setAttribute('expire', $expire); + ->setAttribute('expire', $expire) + ->setAttribute('search', implode('', [$name, $project->getId(), $key->getId()])); $dbForPlatform->updateDocument('devKeys', $key->getId(), $key); diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php index 7221d54cda..71511488f5 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php @@ -10,10 +10,16 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; +use Utopia\Database\Validator\Queries; +use Utopia\Database\Validator\Query\Cursor; +use Utopia\Database\Validator\Query\Limit; +use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; +use Utopia\Validator\Text; class XList extends Action { @@ -47,12 +53,14 @@ class XList extends Action contentType: ContentType::JSON )) ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('queries', [], new Queries([new Limit(), new Offset(), new Cursor()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit, offset and cursor', true) + ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('response') ->inject('dbForPlatform') ->callback([$this, 'action']); } - public function action(string $projectId, Response $response, Database $dbForPlatform) + public function action(string $projectId, ?array $queries, ?string $search, Response $response, Database $dbForPlatform) { $project = $dbForPlatform->getDocument('projects', $projectId); @@ -61,13 +69,22 @@ class XList extends Action throw new Exception(Exception::PROJECT_NOT_FOUND); } - $keys = $dbForPlatform->find('devKeys', [ - Query::equal('projectInternalId', [$project->getInternalId()]), - Query::limit(APP_LIMIT_DEV_KEYS), - ]); + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + if (!empty($search)) { + $queries[] = Query::search('search', $search); + } + + $queries[] = Query::equal('projectInternalId', [$project->getInternalId()]); + + $keys = $dbForPlatform->find('devKeys', $queries); $response->dynamic(new Document([ - 'keys' => $keys, + 'devKeys' => $keys, 'total' => count($keys), ]), Response::MODEL_DEV_KEY_LIST); } diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index fbbdda6e93..54a7e907a5 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -4,6 +4,7 @@ namespace Tests\E2E\Services\Projects; use Tests\E2E\Client; use Utopia\Database\DateTime; +use Utopia\Database\Query; trait ProjectsDevKeys { @@ -13,6 +14,9 @@ trait ProjectsDevKeys */ public function testCreateProjectDevKey($data): array { + /** + * Test for SUCCESS + */ $id = $data['projectId'] ?? ''; $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ @@ -30,6 +34,26 @@ trait ProjectsDevKeys $this->assertArrayHasKey('accessedAt', $response['body']); $this->assertEmpty($response['body']['accessedAt']); + /** Create a second dev key */ + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Dev Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 36000) + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals('Dev Key Test', $response['body']['name']); + $this->assertNotEmpty($response['body']['secret']); + $this->assertArrayHasKey('accessedAt', $response['body']); + $this->assertEmpty($response['body']['accessedAt']); + + /** + * Test for FAILURE + */ + /** TEST expiry date is required */ $res = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ 'content-type' => 'application/json', @@ -55,6 +79,11 @@ trait ProjectsDevKeys */ public function testListProjectDevKey($data): array { + /** + * Test for SUCCESS + */ + + /** List all dev keys */ $id = $data['projectId'] ?? ''; $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys', array_merge([ @@ -62,10 +91,51 @@ trait ProjectsDevKeys 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(2, $response['body']['total']); + + /** List dev keys with limit */ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::limit(1)->toString() + ] + ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(1, $response['body']['total']); + /** List dev keys with search */ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'Dev' + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(1, $response['body']['total']); + $this->assertEquals('Dev Key Test', $response['body']['devKeys'][0]['name']); + + /** + * Test for FAILURE + */ + + /** Test for search with invalid query */ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::search('name', 'Invalid')->toString() + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertEquals('Invalid `queries` param: Invalid query method: search', $response['body']['message']); + return $data; } @@ -76,6 +146,9 @@ trait ProjectsDevKeys */ public function testGetProjectDevKey($data): array { + /** + * Test for SUCCESS + */ $id = $data['projectId'] ?? ''; $keyId = $data['keyId'] ?? ''; @@ -87,7 +160,7 @@ trait ProjectsDevKeys $this->assertEquals(200, $response['headers']['status-code']); $this->assertNotEmpty($response['body']['$id']); $this->assertEquals($keyId, $response['body']['$id']); - $this->assertEquals('Key Test', $response['body']['name']); + $this->assertEquals('Dev Key Test', $response['body']['name']); $this->assertNotEmpty($response['body']['secret']); $this->assertArrayHasKey('accessedAt', $response['body']); $this->assertEmpty($response['body']['accessedAt']); From e8a0425cc3a46fa5a8eb77922db59136b5855249 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 9 Apr 2025 13:03:56 +0000 Subject: [PATCH 090/110] chore: add allowed attributes to query --- .env | 2 +- .../Modules/Projects/Http/DevKeys/XList.php | 8 ++----- .../Database/Validator/Queries/DevKeys.php | 21 +++++++++++++++++++ .../e2e/Services/Projects/ProjectsDevKeys.php | 2 +- 4 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 src/Appwrite/Utopia/Database/Validator/Queries/DevKeys.php diff --git a/.env b/.env index d18e63c56e..c10c12613b 100644 --- a/.env +++ b/.env @@ -15,7 +15,7 @@ _APP_SYSTEM_TEAM_EMAIL=team@appwrite.io _APP_EMAIL_SECURITY=security@appwrite.io _APP_EMAIL_CERTIFICATES=certificates@appwrite.io _APP_SYSTEM_RESPONSE_FORMAT= -_APP_OPTIONS_ABUSE=enabled +_APP_OPTIONS_ABUSE=disabled _APP_OPTIONS_ROUTER_PROTECTION=disabled _APP_OPTIONS_FORCE_HTTPS=disabled _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php index 71511488f5..36b63c721e 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php @@ -12,15 +12,11 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; -use Utopia\Database\Validator\Queries; -use Utopia\Database\Validator\Query\Cursor; -use Utopia\Database\Validator\Query\Limit; -use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Validator\Text; - +use Appwrite\Utopia\Database\Validator\Queries\DevKeys; class XList extends Action { use HTTP; @@ -53,7 +49,7 @@ class XList extends Action contentType: ContentType::JSON )) ->param('projectId', '', new UID(), 'Project unique ID.') - ->param('queries', [], new Queries([new Limit(), new Offset(), new Cursor()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit, offset and cursor', true) + ->param('queries', [], new DevKeys(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit, offset and cursor', true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('response') ->inject('dbForPlatform') diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/DevKeys.php b/src/Appwrite/Utopia/Database/Validator/Queries/DevKeys.php new file mode 100644 index 0000000000..83d49ac2ae --- /dev/null +++ b/src/Appwrite/Utopia/Database/Validator/Queries/DevKeys.php @@ -0,0 +1,21 @@ +assertEquals(400, $response['headers']['status-code']); - $this->assertEquals('Invalid `queries` param: Invalid query method: search', $response['body']['message']); + $this->assertEquals('Searching by attribute "name" requires a fulltext index.', $response['body']['message']); return $data; } From c6399a2a013f30b2f2157e209c797bd423c82026 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 9 Apr 2025 13:11:48 +0000 Subject: [PATCH 091/110] chore: update specs and tests --- .../specs/open-api3-latest-console.json | 39 +++++++++++++---- app/config/specs/swagger2-latest-console.json | 42 +++++++++++++------ .../e2e/Services/Projects/ProjectsDevKeys.php | 11 +++++ 3 files changed, 71 insertions(+), 21 deletions(-) diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index a304a821fe..6538b21d8b 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -20352,7 +20352,7 @@ ], "description": "", "responses": { - "201": { + "200": { "description": "Dev Keys List", "content": { "application\/json": { @@ -20398,6 +20398,27 @@ "x-example": "" }, "in": "path" + }, + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Only supported methods are limit, offset and cursor", + "required": false, + "schema": { + "type": "string", + "default": [] + }, + "in": "query" + }, + { + "name": "search", + "description": "Search term to filter your list results. Max length: 256 chars.", + "required": false, + "schema": { + "type": "string", + "x-example": "", + "default": "" + }, + "in": "query" } ] }, @@ -20560,7 +20581,7 @@ ], "description": "", "responses": { - "201": { + "200": { "description": "DevKey", "content": { "application\/json": { @@ -20652,8 +20673,8 @@ ], "description": "", "responses": { - "201": { - "description": "File" + "204": { + "description": "No content" } }, "x-appwrite": { @@ -31497,13 +31518,13 @@ "properties": { "total": { "type": "integer", - "description": "Total number of keys documents that matched your query.", + "description": "Total number of devKeys documents that matched your query.", "x-example": 5, "format": "int32" }, - "keys": { + "devKeys": { "type": "array", - "description": "List of keys.", + "description": "List of devKeys.", "items": { "$ref": "#\/components\/schemas\/devKey" }, @@ -31512,7 +31533,7 @@ }, "required": [ "total", - "keys" + "devKeys" ] }, "platformList": { @@ -35626,7 +35647,7 @@ "name": { "type": "string", "description": "Key name.", - "x-example": "My API Key" + "x-example": "Dev API Key" }, "expire": { "type": "string", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 4af69d9c4c..0059fd1a4d 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -20833,7 +20833,7 @@ ], "description": "", "responses": { - "201": { + "200": { "description": "Dev Keys List", "schema": { "$ref": "#\/definitions\/devKeyList" @@ -20873,6 +20873,27 @@ "type": "string", "x-example": "", "in": "path" + }, + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Only supported methods are limit, offset and cursor", + "required": false, + "type": "array", + "collectionFormat": "multi", + "items": { + "type": "string" + }, + "default": [], + "in": "query" + }, + { + "name": "search", + "description": "Search term to filter your list results. Max length: 256 chars.", + "required": false, + "type": "string", + "x-example": "", + "default": "", + "in": "query" } ] }, @@ -21039,7 +21060,7 @@ ], "description": "", "responses": { - "201": { + "200": { "description": "DevKey", "schema": { "$ref": "#\/definitions\/devKey" @@ -21127,11 +21148,8 @@ ], "description": "", "responses": { - "201": { - "description": "File", - "schema": { - "type": "file" - } + "204": { + "description": "No content" } }, "x-appwrite": { @@ -31999,13 +32017,13 @@ "properties": { "total": { "type": "integer", - "description": "Total number of keys documents that matched your query.", + "description": "Total number of devKeys documents that matched your query.", "x-example": 5, "format": "int32" }, - "keys": { + "devKeys": { "type": "array", - "description": "List of keys.", + "description": "List of devKeys.", "items": { "type": "object", "$ref": "#\/definitions\/devKey" @@ -32015,7 +32033,7 @@ }, "required": [ "total", - "keys" + "devKeys" ] }, "platformList": { @@ -36161,7 +36179,7 @@ "name": { "type": "string", "description": "Key name.", - "x-example": "My API Key" + "x-example": "Dev API Key" }, "expire": { "type": "string", diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index 2e3532acc2..83b4927425 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -119,6 +119,17 @@ trait ProjectsDevKeys $this->assertEquals(1, $response['body']['total']); $this->assertEquals('Dev Key Test', $response['body']['devKeys'][0]['name']); + /** List dev keys with querying `expire` */ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [Query::lessThan('expire', (new \DateTime())->format('Y-m-d H:i:s'))->toString()] + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(0, $response['body']['total']); // No dev keys expired + /** * Test for FAILURE */ From 3c78e7efff868957a1684f64d03b6f4040bd48f1 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 9 Apr 2025 13:15:59 +0000 Subject: [PATCH 092/110] fix: query description --- app/config/specs/open-api3-latest-console.json | 2 +- app/config/specs/swagger2-latest-console.json | 2 +- .../Platform/Modules/Projects/Http/DevKeys/XList.php | 5 +++-- src/Appwrite/Utopia/Database/Validator/Queries/DevKeys.php | 3 +-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 6538b21d8b..0cc41f4125 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -20401,7 +20401,7 @@ }, { "name": "queries", - "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Only supported methods are limit, offset and cursor", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: accessedAt, expire", "required": false, "schema": { "type": "string", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 0059fd1a4d..28f244b3b7 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -20876,7 +20876,7 @@ }, { "name": "queries", - "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Only supported methods are limit, offset and cursor", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: accessedAt, expire", "required": false, "type": "array", "collectionFormat": "multi", diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php index 36b63c721e..0d3516558e 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php @@ -7,6 +7,7 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Validator\Queries\DevKeys; use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\Document; @@ -16,7 +17,7 @@ use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Validator\Text; -use Appwrite\Utopia\Database\Validator\Queries\DevKeys; + class XList extends Action { use HTTP; @@ -49,7 +50,7 @@ class XList extends Action contentType: ContentType::JSON )) ->param('projectId', '', new UID(), 'Project unique ID.') - ->param('queries', [], new DevKeys(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit, offset and cursor', true) + ->param('queries', [], new DevKeys(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', DevKeys::ALLOWED_ATTRIBUTES), true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('response') ->inject('dbForPlatform') diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/DevKeys.php b/src/Appwrite/Utopia/Database/Validator/Queries/DevKeys.php index 83d49ac2ae..d9dbbeadc4 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/DevKeys.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/DevKeys.php @@ -5,9 +5,8 @@ namespace Appwrite\Utopia\Database\Validator\Queries; class DevKeys extends Base { public const ALLOWED_ATTRIBUTES = [ - 'name', 'accessedAt', - 'expire' + 'expire', ]; /** From b9f50d9f01f5e35c5cb4c63b4bafc96b2d5cf871 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 9 Apr 2025 13:21:06 +0000 Subject: [PATCH 093/110] chore: fix test --- tests/e2e/Services/Projects/ProjectsDevKeys.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index 83b4927425..39ce495c0e 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -145,7 +145,7 @@ trait ProjectsDevKeys ]); $this->assertEquals(400, $response['headers']['status-code']); - $this->assertEquals('Searching by attribute "name" requires a fulltext index.', $response['body']['message']); + $this->assertEquals('Invalid `queries` param: Invalid query: Attribute not found in schema: name', $response['body']['message']); return $data; } From 013555cc8e0601d26b95b06b77e787399a05da0c Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 9 Apr 2025 14:21:56 +0000 Subject: [PATCH 094/110] chore: add tests for hostname check with devkeys --- .env | 2 +- .../e2e/Services/Projects/ProjectsDevKeys.php | 70 ++++++++++++++++++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/.env b/.env index c10c12613b..d18e63c56e 100644 --- a/.env +++ b/.env @@ -15,7 +15,7 @@ _APP_SYSTEM_TEAM_EMAIL=team@appwrite.io _APP_EMAIL_SECURITY=security@appwrite.io _APP_EMAIL_CERTIFICATES=certificates@appwrite.io _APP_SYSTEM_RESPONSE_FORMAT= -_APP_OPTIONS_ABUSE=disabled +_APP_OPTIONS_ABUSE=enabled _APP_OPTIONS_ROUTER_PROTECTION=disabled _APP_OPTIONS_FORCE_HTTPS=disabled _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index 39ce495c0e..5fe39798c8 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -4,6 +4,7 @@ namespace Tests\E2E\Services\Projects; use Tests\E2E\Client; use Utopia\Database\DateTime; +use Utopia\Database\Helpers\ID; use Utopia\Database\Query; trait ProjectsDevKeys @@ -189,6 +190,74 @@ trait ProjectsDevKeys return $data; } + /** + * @depends testCreateProject + * @group devKeys + */ + public function testNoHostValidationWithDevKey($data): void + { + $id = $data['projectId'] ?? ''; + + /** Create a dev key */ + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 3600), + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + $devKey = $response['body']['secret']; + + /** Test oauth2 and get invalid `success` URL */ + $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/google', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $id + ], [ + 'success' => 'https://example.com', + 'failure' => 'https://example.com' + ]); + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertEquals('Invalid `success` param: URL host must be one of: localhost, appwrite.io, *.appwrite.io', $response['body']['message']); + + /** Test oauth2 with devKey and now get oauth2 is disabled */ + $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/google', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $id, + 'x-appwrite-dev-key' => $devKey + ], [ + 'success' => 'https://example.com', + 'failure' => 'https://example.com' + ]); + $this->assertEquals(412, $response['headers']['status-code']); + $this->assertEquals('This provider is disabled. Please enable the provider from your Appwrite console to continue.', $response['body']['message']); + + /** Test hostname in Magic URL */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/magic-url', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $id, + ], [ + 'userId' => ID::unique(), + 'email' => 'user@appwrite.io', + 'url' => 'https://example.com', + ]); + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertEquals('Invalid `url` param: URL host must be one of: localhost, appwrite.io, *.appwrite.io', $response['body']['message']); + + /** Test hostname in Magic URL with devKey */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/magic-url', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $id, + 'x-appwrite-dev-key' => $devKey + ], [ + 'userId' => ID::unique(), + 'email' => 'user@appwrite.io', + 'url' => 'https://example.com', + ]); + $this->assertEquals(201, $response['headers']['status-code']); + } + /** * @depends testCreateProject * @group devKeys @@ -296,7 +365,6 @@ trait ProjectsDevKeys $this->assertEquals(429, $res['headers']['status-code']); } - /** * @depends testCreateProjectDevKey * @group devKeys From 45df0ef85466295246c96ed1f2ad4369b0667967 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 9 Apr 2025 16:24:24 +0000 Subject: [PATCH 095/110] chore: add cors test --- .../e2e/Services/Projects/ProjectsDevKeys.php | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index 5fe39798c8..a17c3fbe99 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -258,6 +258,63 @@ trait ProjectsDevKeys $this->assertEquals(201, $response['headers']['status-code']); } + /** + * @depends testCreateProjectDevKey + * @group devKeys + */ + public function testCorsWithDevKey($data): void + { + $projectId = $data['projectId'] ?? ''; + + $id = $data['projectId'] ?? ''; + + /** Create a dev key */ + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 3600), + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + $devKey = $response['body']['secret']; + $origin = 'http://example.com'; + + /** + * Test CORS without Dev Key (should fail due to origin) + */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'origin' => $origin, + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + + $this->assertEquals(403, $response['headers']['status-code']); + $this->assertNotEquals($origin, $response['headers']['access-control-allow-origin'] ?? null); + $this->assertEquals('http://localhost', $response['headers']['access-control-allow-origin'] ?? null); + + + /** + * Test CORS with Dev Key (should bypass origin check) + */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'origin' => $origin, + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-dev-key' => $devKey + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + + $this->assertEquals(401, $response['headers']['status-code']); + $this->assertEquals('*', $response['headers']['access-control-allow-origin'] ?? null); + } + /** * @depends testCreateProject * @group devKeys From 0479f4cca7e1039cc0039a9a43238d4bb88618e0 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 10 Apr 2025 07:29:24 +0000 Subject: [PATCH 096/110] chore: change abuse back to disabled --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index d18e63c56e..c10c12613b 100644 --- a/.env +++ b/.env @@ -15,7 +15,7 @@ _APP_SYSTEM_TEAM_EMAIL=team@appwrite.io _APP_EMAIL_SECURITY=security@appwrite.io _APP_EMAIL_CERTIFICATES=certificates@appwrite.io _APP_SYSTEM_RESPONSE_FORMAT= -_APP_OPTIONS_ABUSE=enabled +_APP_OPTIONS_ABUSE=disabled _APP_OPTIONS_ROUTER_PROTECTION=disabled _APP_OPTIONS_FORCE_HTTPS=disabled _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled From 3b8354d2ea36fbb5c600fce2a3201516dd5789ba Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 14 Apr 2025 03:55:04 +0000 Subject: [PATCH 097/110] chore: add sdk to key --- app/init/resources.php | 45 +++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/app/init/resources.php b/app/init/resources.php index 81d80092c4..38edfc177d 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -50,6 +50,7 @@ use Utopia\Storage\Device\Wasabi; use Utopia\Storage\Storage; use Utopia\System\System; use Utopia\Validator\Hostname; +use Utopia\Validator\WhiteList; use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub; // Runtime Execution @@ -782,26 +783,48 @@ App::setResource('smsRates', function () { return []; }); -App::setResource('devKey', function (Request $request, Document $project, Database $dbForPlatform) { +App::setResource('devKey', function (Request $request, Document $project, array $servers, Database $dbForPlatform) { $devKey = $request->getHeader('x-appwrite-dev-key', $request->getParam('devKey', '')); + // Check if given key match project's development keys $key = $project->find('secret', $devKey, 'devKeys'); - if ($key) { - $expire = $key->getAttribute('expire'); - if (!empty($expire) && $expire < DatabaseDateTime::formatTz(DatabaseDateTime::now())) { - return new Document([]); - } + if (!$key) { + return new Document([]); + } - $accessedAt = $key->getAttribute('accessedAt', ''); - if (DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) { + // check expiration + $expire = $key->getAttribute('expire'); + if (!empty($expire) && $expire < DatabaseDateTime::formatTz(DatabaseDateTime::now())) { + return new Document([]); + } + + // update access time + $accessedAt = $key->getAttribute('accessedAt', ''); + if (DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) { + $key->setAttribute('accessedAt', DatabaseDateTime::now()); + Authorization::skip(fn () => $dbForPlatform->updateDocument('keys', $key->getId(), $key)); + $dbForPlatform->purgeCachedDocument('projects', $project->getId()); + } + + // add sdk to key + $sdkValidator = new WhiteList($servers, true); + $sdk = $request->getHeader('x-sdk-name', 'UNKNOWN'); + + if ($sdkValidator->isValid($sdk)) { + $sdks = $key->getAttribute('sdks', []); + + if (!in_array($sdk, $sdks)) { + $sdks[] = $sdk; + $key->setAttribute('sdks', $sdks); + + /** Update access time as well */ $key->setAttribute('accessedAt', DatabaseDateTime::now()); Authorization::skip(fn () => $dbForPlatform->updateDocument('keys', $key->getId(), $key)); $dbForPlatform->purgeCachedDocument('projects', $project->getId()); } - return $key; } - return new Document([]); -}, ['request', 'project', 'dbForPlatform']); + return $key; +}, ['request', 'project', 'servers', 'dbForPlatform']); App::setResource('team', function (Document $project, Database $dbForPlatform, App $utopia, Request $request) { $teamInternalId = ''; From f31cb0cdfcdfe289b9ec63f7ed140ce8c6175e3b Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 14 Apr 2025 07:34:05 +0000 Subject: [PATCH 098/110] chore: added tests for checking if sdks are updated in devkeys --- .env | 2 +- app/init/resources.php | 4 +- src/Appwrite/Utopia/Response/Model/DevKey.php | 7 ++ .../e2e/Services/Projects/ProjectsDevKeys.php | 97 +++++++++++-------- 4 files changed, 66 insertions(+), 44 deletions(-) diff --git a/.env b/.env index c10c12613b..d18e63c56e 100644 --- a/.env +++ b/.env @@ -15,7 +15,7 @@ _APP_SYSTEM_TEAM_EMAIL=team@appwrite.io _APP_EMAIL_SECURITY=security@appwrite.io _APP_EMAIL_CERTIFICATES=certificates@appwrite.io _APP_SYSTEM_RESPONSE_FORMAT= -_APP_OPTIONS_ABUSE=disabled +_APP_OPTIONS_ABUSE=enabled _APP_OPTIONS_ROUTER_PROTECTION=disabled _APP_OPTIONS_FORCE_HTTPS=disabled _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled diff --git a/app/init/resources.php b/app/init/resources.php index 38edfc177d..f1a743f1f8 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -802,7 +802,7 @@ App::setResource('devKey', function (Request $request, Document $project, array $accessedAt = $key->getAttribute('accessedAt', ''); if (DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) { $key->setAttribute('accessedAt', DatabaseDateTime::now()); - Authorization::skip(fn () => $dbForPlatform->updateDocument('keys', $key->getId(), $key)); + Authorization::skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key)); $dbForPlatform->purgeCachedDocument('projects', $project->getId()); } @@ -819,7 +819,7 @@ App::setResource('devKey', function (Request $request, Document $project, array /** Update access time as well */ $key->setAttribute('accessedAt', DatabaseDateTime::now()); - Authorization::skip(fn () => $dbForPlatform->updateDocument('keys', $key->getId(), $key)); + Authorization::skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key)); $dbForPlatform->purgeCachedDocument('projects', $project->getId()); } } diff --git a/src/Appwrite/Utopia/Response/Model/DevKey.php b/src/Appwrite/Utopia/Response/Model/DevKey.php index d1074bd7d3..b8da6c0cfc 100644 --- a/src/Appwrite/Utopia/Response/Model/DevKey.php +++ b/src/Appwrite/Utopia/Response/Model/DevKey.php @@ -57,6 +57,13 @@ class DevKey extends Model 'default' => '', 'example' => self::TYPE_DATETIME_EXAMPLE ]) + ->addRule('sdks', [ + 'type' => self::TYPE_STRING, + 'description' => 'List of SDK user agents that used this key.', + 'default' => null, + 'example' => 'appwrite:flutter', + 'array' => true + ]) ; } diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php index a17c3fbe99..57c832750e 100644 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ b/tests/e2e/Services/Projects/ProjectsDevKeys.php @@ -191,24 +191,62 @@ trait ProjectsDevKeys } /** - * @depends testCreateProject + * @depends testCreateProjectDevKey + * @group devKeys + */ + public function testGetDevKeyWithSdks($data): array + { + $id = $data['projectId'] ?? ''; + $keyId = $data['keyId'] ?? ''; + $devKey = $data['secret'] ?? ''; + + /** Use dev key with python sdk */ + $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $id, + 'x-appwrite-dev-key' => $devKey, + 'x-sdk-name' => 'python' + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals(401, $res['headers']['status-code']); + + /** Use dev key with php sdk */ + $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $id, + 'x-appwrite-dev-key' => $devKey, + 'x-sdk-name' => 'php' + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals(401, $res['headers']['status-code']); + + /** Get the dev key */ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys/' . $keyId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertArrayHasKey('sdks', $response['body']); + $this->assertCount(2, $response['body']['sdks']); + $this->assertContains('python', $response['body']['sdks']); + $this->assertContains('php', $response['body']['sdks']); + + return $data; + } + + /** + * @depends testCreateProjectDevKey * @group devKeys */ public function testNoHostValidationWithDevKey($data): void { $id = $data['projectId'] ?? ''; - - /** Create a dev key */ - $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'name' => 'Key Test', - 'expire' => DateTime::addSeconds(new \DateTime(), 3600), - ]); - $this->assertEquals(201, $response['headers']['status-code']); - - $devKey = $response['body']['secret']; + $devKey = $data['secret'] ?? ''; /** Test oauth2 and get invalid `success` URL */ $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/google', [ @@ -265,20 +303,7 @@ trait ProjectsDevKeys public function testCorsWithDevKey($data): void { $projectId = $data['projectId'] ?? ''; - - $id = $data['projectId'] ?? ''; - - /** Create a dev key */ - $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'name' => 'Key Test', - 'expire' => DateTime::addSeconds(new \DateTime(), 3600), - ]); - $this->assertEquals(201, $response['headers']['status-code']); - - $devKey = $response['body']['secret']; + $devKey = $data['secret'] ?? ''; $origin = 'http://example.com'; /** @@ -316,26 +341,17 @@ trait ProjectsDevKeys } /** - * @depends testCreateProject + * @depends testCreateProjectDevKey * @group devKeys */ public function testNoRateLimitWithDevKey($data): void { $id = $data['projectId'] ?? ''; + $devKey = $data['secret'] ?? ''; /** * Test for SUCCESS */ - $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'name' => 'Key Test', - 'expire' => DateTime::addSeconds(new \DateTime(), 3600), - ]); - - $devKey = $response['body']['secret']; - for ($i = 0; $i < 10; $i++) { $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', @@ -365,7 +381,6 @@ trait ProjectsDevKeys ]); $this->assertEquals(401, $res['headers']['status-code']); - /** * Test for FAILURE */ @@ -444,7 +459,7 @@ trait ProjectsDevKeys $this->assertEquals($keyId, $response['body']['$id']); $this->assertEquals('Key Test Update', $response['body']['name']); $this->assertArrayHasKey('accessedAt', $response['body']); - $this->assertEmpty($response['body']['accessedAt']); + $this->assertNotEmpty($response['body']['accessedAt']); $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys/' . $keyId, array_merge([ 'content-type' => 'application/json', @@ -456,7 +471,7 @@ trait ProjectsDevKeys $this->assertEquals($keyId, $response['body']['$id']); $this->assertEquals('Key Test Update', $response['body']['name']); $this->assertArrayHasKey('accessedAt', $response['body']); - $this->assertEmpty($response['body']['accessedAt']); + $this->assertNotEmpty($response['body']['accessedAt']); return $data; } From a1aabd351c6bee1a354278b84ed53cc4ea6bdde1 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 14 Apr 2025 07:34:52 +0000 Subject: [PATCH 099/110] fix: env --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index d18e63c56e..c10c12613b 100644 --- a/.env +++ b/.env @@ -15,7 +15,7 @@ _APP_SYSTEM_TEAM_EMAIL=team@appwrite.io _APP_EMAIL_SECURITY=security@appwrite.io _APP_EMAIL_CERTIFICATES=certificates@appwrite.io _APP_SYSTEM_RESPONSE_FORMAT= -_APP_OPTIONS_ABUSE=enabled +_APP_OPTIONS_ABUSE=disabled _APP_OPTIONS_ROUTER_PROTECTION=disabled _APP_OPTIONS_FORCE_HTTPS=disabled _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled From 6372180537d479945f7aeb64521dfb14b6a23745 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 14 Apr 2025 08:22:42 +0000 Subject: [PATCH 100/110] chore: update specs --- app/config/specs/open-api3-latest-console.json | 11 ++++++++++- app/config/specs/swagger2-latest-console.json | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 0cc41f4125..498f30d671 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -35663,6 +35663,14 @@ "type": "string", "description": "Most recent access date in ISO 8601 format. This attribute is only updated again after 24 hours.", "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "sdks": { + "type": "array", + "description": "List of SDK user agents that used this key.", + "items": { + "type": "string" + }, + "x-example": "appwrite:flutter" } }, "required": [ @@ -35672,7 +35680,8 @@ "name", "expire", "secret", - "accessedAt" + "accessedAt", + "sdks" ] }, "mockNumber": { diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 28f244b3b7..ecacbaf897 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -36195,6 +36195,14 @@ "type": "string", "description": "Most recent access date in ISO 8601 format. This attribute is only updated again after 24 hours.", "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "sdks": { + "type": "array", + "description": "List of SDK user agents that used this key.", + "items": { + "type": "string" + }, + "x-example": "appwrite:flutter" } }, "required": [ @@ -36204,7 +36212,8 @@ "name", "expire", "secret", - "accessedAt" + "accessedAt", + "sdks" ] }, "mockNumber": { From 74abf4897b769ca6562fd3e856b98c7785d275a2 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 14 Apr 2025 08:47:57 +0000 Subject: [PATCH 101/110] chore: fix teamCreateMembership --- app/controllers/api/teams.php | 1 + tests/e2e/Services/Teams/TeamsBaseClient.php | 12 ---------- .../Services/Teams/TeamsConsoleClientTest.php | 24 +++++++++++++++++++ 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index ef589233f2..f58be39857 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -51,6 +51,7 @@ use Utopia\Validator\ArrayList; use Utopia\Validator\Assoc; use Utopia\Validator\Host; use Utopia\Validator\Text; +use Utopia\Validator\URL; use Utopia\Validator\WhiteList; App::post('/v1/teams') diff --git a/tests/e2e/Services/Teams/TeamsBaseClient.php b/tests/e2e/Services/Teams/TeamsBaseClient.php index 3fcd9c043d..1858fd50ad 100644 --- a/tests/e2e/Services/Teams/TeamsBaseClient.php +++ b/tests/e2e/Services/Teams/TeamsBaseClient.php @@ -337,18 +337,6 @@ trait TeamsBaseClient $this->assertEquals(400, $response['headers']['status-code']); - $response = $this->client->call(Client::METHOD_POST, '/teams/' . $teamUid . '/memberships', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'email' => $email, - 'name' => $name, - 'roles' => ['developer'], - 'url' => 'http://example.com/join-us#title' // bad url - ]); - - $this->assertEquals(400, $response['headers']['status-code']); - return [ 'teamUid' => $teamUid, 'teamName' => $teamName, diff --git a/tests/e2e/Services/Teams/TeamsConsoleClientTest.php b/tests/e2e/Services/Teams/TeamsConsoleClientTest.php index 4b5ade7cbf..dec61d5258 100644 --- a/tests/e2e/Services/Teams/TeamsConsoleClientTest.php +++ b/tests/e2e/Services/Teams/TeamsConsoleClientTest.php @@ -14,6 +14,30 @@ class TeamsConsoleClientTest extends Scope use ProjectConsole; use SideClient; + /** + * @depends testCreateTeam + */ + public function testTeamCreateMembershipConsole($data): array + { + $teamUid = $data['teamUid'] ?? ''; + $email = uniqid() . 'friend@localhost.test'; + $name = 'Friend User'; + + $response = $this->client->call(Client::METHOD_POST, '/teams/' . $teamUid . '/memberships', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'email' => $email, + 'name' => $name, + 'roles' => ['developer'], + 'url' => 'http://example.com/join-us#title' // bad url + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + return $data; + } + /** * @depends testCreateTeam */ From 65965692591731ded1903fb5b22ea38b062d3d43 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 14 Apr 2025 18:01:40 +0000 Subject: [PATCH 102/110] chore: make devkeys test dependence free --- .env | 2 +- tests/e2e/Services/Projects/ProjectsBase.php | 42 ++ .../Projects/ProjectsConsoleClientTest.php | 593 +++++++++++++++++- .../e2e/Services/Projects/ProjectsDevKeys.php | 529 ---------------- 4 files changed, 635 insertions(+), 531 deletions(-) delete mode 100644 tests/e2e/Services/Projects/ProjectsDevKeys.php diff --git a/.env b/.env index c10c12613b..d18e63c56e 100644 --- a/.env +++ b/.env @@ -15,7 +15,7 @@ _APP_SYSTEM_TEAM_EMAIL=team@appwrite.io _APP_EMAIL_SECURITY=security@appwrite.io _APP_EMAIL_CERTIFICATES=certificates@appwrite.io _APP_SYSTEM_RESPONSE_FORMAT= -_APP_OPTIONS_ABUSE=disabled +_APP_OPTIONS_ABUSE=enabled _APP_OPTIONS_ROUTER_PROTECTION=disabled _APP_OPTIONS_FORCE_HTTPS=disabled _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled diff --git a/tests/e2e/Services/Projects/ProjectsBase.php b/tests/e2e/Services/Projects/ProjectsBase.php index 53d9626252..0d1d6a5a44 100644 --- a/tests/e2e/Services/Projects/ProjectsBase.php +++ b/tests/e2e/Services/Projects/ProjectsBase.php @@ -2,6 +2,48 @@ namespace Tests\E2E\Services\Projects; +use Tests\E2E\Client; +use Utopia\Database\Helpers\ID; + trait ProjectsBase { + protected function setupProject(mixed $params): string + { + $team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'teamId' => ID::unique(), + 'name' => 'Project Test', + ]); + + $this->assertEquals(201, $team['headers']['status-code'], 'Setup team failed with status code: ' . $team['headers']['status-code'] . ' and response: ' . json_encode($team['body'], JSON_PRETTY_PRINT)); + + $project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + ...$params, + 'teamId' => $team['body']['$id'], + ]); + + $this->assertEquals(201, $project['headers']['status-code'], 'Setup project failed with status code: ' . $project['headers']['status-code'] . ' and response: ' . json_encode($project['body'], JSON_PRETTY_PRINT)); + + return $project['body']['$id']; + } + + protected function setupDevKey(mixed $params): array + { + $devKey = $this->client->call(Client::METHOD_POST, '/projects/' . $params['projectId'] . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), $params); + + $this->assertEquals(201, $devKey['headers']['status-code'], 'Setup devKey failed with status code: ' . $devKey['headers']['status-code'] . ' and response: ' . json_encode($devKey['body'], JSON_PRETTY_PRINT)); + + return [ + '$id' => $devKey['body']['$id'], + 'secret' => $devKey['body']['secret'], + ]; + } } diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 34ecba242f..491e0371b0 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -22,7 +22,6 @@ class ProjectsConsoleClientTest extends Scope use ProjectConsole; use SideClient; use Async; - use ProjectsDevKeys; /** * @group devKeys @@ -4241,4 +4240,596 @@ class ProjectsConsoleClientTest extends Scope return $data; } + + /** + * Devkeys Tests starts here ------------------------------------------------ + */ + + /** + * @group devKeys + */ + public function testCreateProjectDevKey(): void + { + /** + * Test for SUCCESS + */ + $id = $this->setupProject([ + 'projectId' => ID::unique(), + 'name' => 'testCreateProjectDevKey', + 'region' => System::getEnv('_APP_REGION', 'default') + ]); + + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 36000) + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals('Key Test', $response['body']['name']); + $this->assertNotEmpty($response['body']['secret']); + $this->assertArrayHasKey('accessedAt', $response['body']); + $this->assertEmpty($response['body']['accessedAt']); + + /** Create a second dev key */ + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Dev Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 36000) + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals('Dev Key Test', $response['body']['name']); + $this->assertNotEmpty($response['body']['secret']); + $this->assertArrayHasKey('accessedAt', $response['body']); + $this->assertEmpty($response['body']['accessedAt']); + + /** + * Test for FAILURE + */ + + /** TEST expiry date is required */ + $res = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Key Test' + ]); + + $this->assertEquals(400, $res['headers']['status-code']); + } + + + /** + * @group devKeys + */ + public function testListProjectDevKey(): void + { + /** + * Test for SUCCESS + */ + $projectId = $this->setupProject([ + 'projectId' => ID::unique(), + 'name' => 'testListProjectDevKey', + 'region' => System::getEnv('_APP_REGION', 'default') + ]); + + /** Create devKey 1 */ + $this->setupDevKey([ + 'projectId' => $projectId, + 'name' => 'Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 36000) + ]); + + /** Create devKey 2 */ + $this->setupDevKey([ + 'projectId' => $projectId, + 'name' => 'Dev Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 36000) + ]); + + /** List all dev keys */ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $projectId . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(2, $response['body']['total']); + + /** List dev keys with limit */ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $projectId . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::limit(1)->toString() + ] + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(1, $response['body']['total']); + + /** List dev keys with search */ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $projectId . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'Dev' + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(1, $response['body']['total']); + $this->assertEquals('Dev Key Test', $response['body']['devKeys'][0]['name']); + + /** List dev keys with querying `expire` */ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $projectId . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [Query::lessThan('expire', (new \DateTime())->format('Y-m-d H:i:s'))->toString()] + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(0, $response['body']['total']); // No dev keys expired + + /** + * Test for FAILURE + */ + + /** Test for search with invalid query */ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $projectId . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::search('name', 'Invalid')->toString() + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertEquals('Invalid `queries` param: Invalid query: Attribute not found in schema: name', $response['body']['message']); + } + + + /** + * @group devKeys + */ + public function testGetProjectDevKey(): void + { + /** + * Test for SUCCESS + */ + $projectId = $this->setupProject([ + 'projectId' => ID::unique(), + 'name' => 'testGetProjectDevKey', + 'region' => System::getEnv('_APP_REGION', 'default') + ]); + + $devKey = $this->setupDevKey([ + 'projectId' => $projectId, + 'name' => 'Dev Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 36000) + ]); + + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $projectId . '/dev-keys/' . $devKey['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals($devKey['$id'], $response['body']['$id']); + $this->assertEquals('Dev Key Test', $response['body']['name']); + $this->assertNotEmpty($response['body']['secret']); + $this->assertArrayHasKey('accessedAt', $response['body']); + $this->assertEmpty($response['body']['accessedAt']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $projectId . '/dev-keys/error', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(404, $response['headers']['status-code']); + } + + /** + * @group devKeys + */ + public function testGetDevKeyWithSdks(): void + { + /** + * Test for SUCCESS + */ + $projectId = $this->setupProject([ + 'projectId' => ID::unique(), + 'name' => 'testGetDevKeyWithSdks', + 'region' => System::getEnv('_APP_REGION', 'default') + ]); + + $devKey = $this->setupDevKey([ + 'projectId' => $projectId, + 'name' => 'Dev Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 36000) + ]); + + /** Use dev key with python sdk */ + $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-dev-key' => $devKey['secret'], + 'x-sdk-name' => 'python' + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals(401, $res['headers']['status-code']); + + /** Use dev key with php sdk */ + $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-dev-key' => $devKey['secret'], + 'x-sdk-name' => 'php' + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals(401, $res['headers']['status-code']); + + /** Get the dev key */ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $projectId . '/dev-keys/' . $devKey['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertArrayHasKey('sdks', $response['body']); + $this->assertCount(2, $response['body']['sdks']); + $this->assertContains('python', $response['body']['sdks']); + $this->assertContains('php', $response['body']['sdks']); + } + + /** + * @group devKeys + */ + public function testNoHostValidationWithDevKey(): void + { + /** + * Test for SUCCESS + */ + $projectId = $this->setupProject([ + 'projectId' => ID::unique(), + 'name' => 'testNoHostValidationWithDevKey', + 'region' => System::getEnv('_APP_REGION', 'default') + ]); + + $devKey = $this->setupDevKey([ + 'projectId' => $projectId, + 'name' => 'Dev Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 36000) + ]); + + /** Test oauth2 and get invalid `success` URL */ + $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/google', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], [ + 'success' => 'https://example.com', + 'failure' => 'https://example.com' + ]); + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertEquals('Invalid `success` param: URL host must be one of: localhost, appwrite.io, *.appwrite.io', $response['body']['message']); + + /** Test oauth2 with devKey and now get oauth2 is disabled */ + $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/google', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-dev-key' => $devKey['secret'] + ], [ + 'success' => 'https://example.com', + 'failure' => 'https://example.com' + ]); + $this->assertEquals(412, $response['headers']['status-code']); + $this->assertEquals('This provider is disabled. Please enable the provider from your Appwrite console to continue.', $response['body']['message']); + + /** Test hostname in Magic URL */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/magic-url', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], [ + 'userId' => ID::unique(), + 'email' => 'user@appwrite.io', + 'url' => 'https://example.com', + ]); + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertEquals('Invalid `url` param: URL host must be one of: localhost, appwrite.io, *.appwrite.io', $response['body']['message']); + + /** Test hostname in Magic URL with devKey */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/magic-url', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-dev-key' => $devKey['secret'] + ], [ + 'userId' => ID::unique(), + 'email' => 'user@appwrite.io', + 'url' => 'https://example.com', + ]); + $this->assertEquals(201, $response['headers']['status-code']); + } + + /** + * @group devKeys + */ + public function testCorsWithDevKey(): void + { + /** + * Test for SUCCESS + */ + $projectId = $this->setupProject([ + 'projectId' => ID::unique(), + 'name' => 'testCorsWithDevKey', + 'region' => System::getEnv('_APP_REGION', 'default') + ]); + + $devKey = $this->setupDevKey([ + 'projectId' => $projectId, + 'name' => 'Dev Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 36000) + ]); + + $origin = 'http://example.com'; + + /** + * Test CORS without Dev Key (should fail due to origin) + */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'origin' => $origin, + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + + $this->assertEquals(403, $response['headers']['status-code']); + $this->assertNotEquals($origin, $response['headers']['access-control-allow-origin'] ?? null); + $this->assertEquals('http://localhost', $response['headers']['access-control-allow-origin'] ?? null); + + + /** + * Test CORS with Dev Key (should bypass origin check) + */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'origin' => $origin, + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-dev-key' => $devKey['secret'] + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + + $this->assertEquals(401, $response['headers']['status-code']); + $this->assertEquals('*', $response['headers']['access-control-allow-origin'] ?? null); + } + + /** + * @group devKeys + */ + public function testNoRateLimitWithDevKey(): void + { + /** + * Test for SUCCESS + */ + $projectId = $this->setupProject([ + 'projectId' => ID::unique(), + 'name' => 'testNoRateLimitWithDevKey', + 'region' => System::getEnv('_APP_REGION', 'default') + ]); + + $devKey = $this->setupDevKey([ + 'projectId' => $projectId, + 'name' => 'Dev Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 36000) + ]); + + /** + * Test for SUCCESS + */ + for ($i = 0; $i < 10; $i++) { + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals(401, $response['headers']['status-code']); + } + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals(429, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-dev-key' => $devKey['secret'] + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals(401, $response['headers']['status-code']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $projectId . '/dev-keys', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), -3600), + ]); + + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-dev-key' => $response['body']['secret'] + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals(429, $response['headers']['status-code']); + + /** + * Test for FAILURE after expire + */ + $devKey = $this->setupDevKey([ + 'projectId' => $projectId, + 'name' => 'Dev Key Test Expire 5 seconds', + 'expire' => DateTime::addSeconds(new \DateTime(), 5) + ]); + + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-dev-key' => $devKey['secret'] + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals(401, $response['headers']['status-code']); + + sleep(5); + + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-dev-key' => $devKey['secret'] + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals(429, $response['headers']['status-code']); + } + + /** + * @group devKeys + */ + public function testUpdateProjectDevKey(): void + { + $projectId = $this->setupProject([ + 'projectId' => ID::unique(), + 'name' => 'testUpdateProjectDevKey', + 'region' => System::getEnv('_APP_REGION', 'default') + ]); + + $devKey = $this->setupDevKey([ + 'projectId' => $projectId, + 'name' => 'Dev Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 36000) + ]); + + $response = $this->client->call(Client::METHOD_PUT, '/projects/' . $projectId . '/dev-keys/' . $devKey['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Key Test Update', + 'expire' => DateTime::addSeconds(new \DateTime(), 360), + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals($devKey['$id'], $response['body']['$id']); + $this->assertEquals('Key Test Update', $response['body']['name']); + $this->assertArrayHasKey('accessedAt', $response['body']); + $this->assertEmpty($response['body']['accessedAt']); + + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $projectId . '/dev-keys/' . $devKey['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals($devKey['$id'], $response['body']['$id']); + $this->assertEquals('Key Test Update', $response['body']['name']); + $this->assertArrayHasKey('accessedAt', $response['body']); + $this->assertEmpty($response['body']['accessedAt']); + } + + /** + * @group devKeys + */ + public function testDeleteProjectDevKey(): void + { + $projectId = $this->setupProject([ + 'projectId' => ID::unique(), + 'name' => 'testDeleteProjectDevKey', + 'region' => System::getEnv('_APP_REGION', 'default') + ]); + + $devKey = $this->setupDevKey([ + 'projectId' => $projectId, + 'name' => 'Dev Key Test', + 'expire' => DateTime::addSeconds(new \DateTime(), 36000) + ]); + + $response = $this->client->call(Client::METHOD_DELETE, '/projects/' . $projectId . '/dev-keys/' . $devKey['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(204, $response['headers']['status-code']); + $this->assertEmpty($response['body']); + + /** + * Get rate limit trying to use the deleted key + */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-dev-key' => $devKey['secret'] + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals(429, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $projectId . '/dev-keys/' . $devKey['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(404, $response['headers']['status-code']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_DELETE, '/projects/' . $projectId . '/dev-keys/error', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(404, $response['headers']['status-code']); + } + + /** + * Devkeys Tests ends here ------------------------------------------------ + */ } diff --git a/tests/e2e/Services/Projects/ProjectsDevKeys.php b/tests/e2e/Services/Projects/ProjectsDevKeys.php deleted file mode 100644 index 57c832750e..0000000000 --- a/tests/e2e/Services/Projects/ProjectsDevKeys.php +++ /dev/null @@ -1,529 +0,0 @@ -client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'name' => 'Key Test', - 'expire' => DateTime::addSeconds(new \DateTime(), 36000) - ]); - - $this->assertEquals(201, $response['headers']['status-code']); - $this->assertNotEmpty($response['body']['$id']); - $this->assertEquals('Key Test', $response['body']['name']); - $this->assertNotEmpty($response['body']['secret']); - $this->assertArrayHasKey('accessedAt', $response['body']); - $this->assertEmpty($response['body']['accessedAt']); - - /** Create a second dev key */ - $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'name' => 'Dev Key Test', - 'expire' => DateTime::addSeconds(new \DateTime(), 36000) - ]); - - $this->assertEquals(201, $response['headers']['status-code']); - $this->assertNotEmpty($response['body']['$id']); - $this->assertEquals('Dev Key Test', $response['body']['name']); - $this->assertNotEmpty($response['body']['secret']); - $this->assertArrayHasKey('accessedAt', $response['body']); - $this->assertEmpty($response['body']['accessedAt']); - - /** - * Test for FAILURE - */ - - /** TEST expiry date is required */ - $res = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'name' => 'Key Test' - ]); - - $this->assertEquals(400, $res['headers']['status-code']); - - $data = array_merge($data, [ - 'keyId' => $response['body']['$id'], - 'secret' => $response['body']['secret'] - ]); - - return $data; - } - - - /** - * @depends testCreateProjectDevKey - * @group devKeys - */ - public function testListProjectDevKey($data): array - { - /** - * Test for SUCCESS - */ - - /** List all dev keys */ - $id = $data['projectId'] ?? ''; - - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(2, $response['body']['total']); - - /** List dev keys with limit */ - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [ - Query::limit(1)->toString() - ] - ]); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(1, $response['body']['total']); - - /** List dev keys with search */ - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'search' => 'Dev' - ]); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(1, $response['body']['total']); - $this->assertEquals('Dev Key Test', $response['body']['devKeys'][0]['name']); - - /** List dev keys with querying `expire` */ - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [Query::lessThan('expire', (new \DateTime())->format('Y-m-d H:i:s'))->toString()] - ]); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(0, $response['body']['total']); // No dev keys expired - - /** - * Test for FAILURE - */ - - /** Test for search with invalid query */ - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [ - Query::search('name', 'Invalid')->toString() - ] - ]); - - $this->assertEquals(400, $response['headers']['status-code']); - $this->assertEquals('Invalid `queries` param: Invalid query: Attribute not found in schema: name', $response['body']['message']); - - return $data; - } - - - /** - * @depends testCreateProjectDevKey - * @group devKeys - */ - public function testGetProjectDevKey($data): array - { - /** - * Test for SUCCESS - */ - $id = $data['projectId'] ?? ''; - $keyId = $data['keyId'] ?? ''; - - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys/' . $keyId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertNotEmpty($response['body']['$id']); - $this->assertEquals($keyId, $response['body']['$id']); - $this->assertEquals('Dev Key Test', $response['body']['name']); - $this->assertNotEmpty($response['body']['secret']); - $this->assertArrayHasKey('accessedAt', $response['body']); - $this->assertEmpty($response['body']['accessedAt']); - - /** - * Test for FAILURE - */ - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys/error', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(404, $response['headers']['status-code']); - - return $data; - } - - /** - * @depends testCreateProjectDevKey - * @group devKeys - */ - public function testGetDevKeyWithSdks($data): array - { - $id = $data['projectId'] ?? ''; - $keyId = $data['keyId'] ?? ''; - $devKey = $data['secret'] ?? ''; - - /** Use dev key with python sdk */ - $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $id, - 'x-appwrite-dev-key' => $devKey, - 'x-sdk-name' => 'python' - ], [ - 'email' => 'user@appwrite.io', - 'password' => 'password' - ]); - $this->assertEquals(401, $res['headers']['status-code']); - - /** Use dev key with php sdk */ - $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $id, - 'x-appwrite-dev-key' => $devKey, - 'x-sdk-name' => 'php' - ], [ - 'email' => 'user@appwrite.io', - 'password' => 'password' - ]); - $this->assertEquals(401, $res['headers']['status-code']); - - /** Get the dev key */ - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys/' . $keyId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertArrayHasKey('sdks', $response['body']); - $this->assertCount(2, $response['body']['sdks']); - $this->assertContains('python', $response['body']['sdks']); - $this->assertContains('php', $response['body']['sdks']); - - return $data; - } - - /** - * @depends testCreateProjectDevKey - * @group devKeys - */ - public function testNoHostValidationWithDevKey($data): void - { - $id = $data['projectId'] ?? ''; - $devKey = $data['secret'] ?? ''; - - /** Test oauth2 and get invalid `success` URL */ - $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/google', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $id - ], [ - 'success' => 'https://example.com', - 'failure' => 'https://example.com' - ]); - $this->assertEquals(400, $response['headers']['status-code']); - $this->assertEquals('Invalid `success` param: URL host must be one of: localhost, appwrite.io, *.appwrite.io', $response['body']['message']); - - /** Test oauth2 with devKey and now get oauth2 is disabled */ - $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/google', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $id, - 'x-appwrite-dev-key' => $devKey - ], [ - 'success' => 'https://example.com', - 'failure' => 'https://example.com' - ]); - $this->assertEquals(412, $response['headers']['status-code']); - $this->assertEquals('This provider is disabled. Please enable the provider from your Appwrite console to continue.', $response['body']['message']); - - /** Test hostname in Magic URL */ - $response = $this->client->call(Client::METHOD_POST, '/account/sessions/magic-url', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $id, - ], [ - 'userId' => ID::unique(), - 'email' => 'user@appwrite.io', - 'url' => 'https://example.com', - ]); - $this->assertEquals(400, $response['headers']['status-code']); - $this->assertEquals('Invalid `url` param: URL host must be one of: localhost, appwrite.io, *.appwrite.io', $response['body']['message']); - - /** Test hostname in Magic URL with devKey */ - $response = $this->client->call(Client::METHOD_POST, '/account/sessions/magic-url', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $id, - 'x-appwrite-dev-key' => $devKey - ], [ - 'userId' => ID::unique(), - 'email' => 'user@appwrite.io', - 'url' => 'https://example.com', - ]); - $this->assertEquals(201, $response['headers']['status-code']); - } - - /** - * @depends testCreateProjectDevKey - * @group devKeys - */ - public function testCorsWithDevKey($data): void - { - $projectId = $data['projectId'] ?? ''; - $devKey = $data['secret'] ?? ''; - $origin = 'http://example.com'; - - /** - * Test CORS without Dev Key (should fail due to origin) - */ - $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ - 'origin' => $origin, - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - ], [ - 'email' => 'user@appwrite.io', - 'password' => 'password' - ]); - - $this->assertEquals(403, $response['headers']['status-code']); - $this->assertNotEquals($origin, $response['headers']['access-control-allow-origin'] ?? null); - $this->assertEquals('http://localhost', $response['headers']['access-control-allow-origin'] ?? null); - - - /** - * Test CORS with Dev Key (should bypass origin check) - */ - $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ - 'origin' => $origin, - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-dev-key' => $devKey - ], [ - 'email' => 'user@appwrite.io', - 'password' => 'password' - ]); - - $this->assertEquals(401, $response['headers']['status-code']); - $this->assertEquals('*', $response['headers']['access-control-allow-origin'] ?? null); - } - - /** - * @depends testCreateProjectDevKey - * @group devKeys - */ - public function testNoRateLimitWithDevKey($data): void - { - $id = $data['projectId'] ?? ''; - $devKey = $data['secret'] ?? ''; - - /** - * Test for SUCCESS - */ - for ($i = 0; $i < 10; $i++) { - $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $id, - ], [ - 'email' => 'user@appwrite.io', - 'password' => 'password' - ]); - $this->assertEquals(401, $res['headers']['status-code']); - } - $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $id, - ], [ - 'email' => 'user@appwrite.io', - 'password' => 'password' - ]); - $this->assertEquals(429, $res['headers']['status-code']); - - $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $id, - 'x-appwrite-dev-key' => $devKey - ], [ - 'email' => 'user@appwrite.io', - 'password' => 'password' - ]); - $this->assertEquals(401, $res['headers']['status-code']); - - /** - * Test for FAILURE - */ - $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'name' => 'Key Test', - 'expire' => DateTime::addSeconds(new \DateTime(), -3600), - ]); - - $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $id, - 'x-appwrite-dev-key' => $response['body']['secret'] - ], [ - 'email' => 'user@appwrite.io', - 'password' => 'password' - ]); - $this->assertEquals(429, $res['headers']['status-code']); - - - /** - * Test for FAILURE after expire - */ - $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/dev-keys', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'name' => 'Key Test', - 'expire' => DateTime::addSeconds(new \DateTime(), 5), - ]); - - $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $id, - 'x-appwrite-dev-key' => $response['body']['secret'] - ], [ - 'email' => 'user@appwrite.io', - 'password' => 'password' - ]); - $this->assertEquals(401, $res['headers']['status-code']); - - sleep(5); - - $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $id, - 'x-appwrite-dev-key' => $response['body']['secret'] - ], [ - 'email' => 'user@appwrite.io', - 'password' => 'password' - ]); - $this->assertEquals(429, $res['headers']['status-code']); - } - - /** - * @depends testCreateProjectDevKey - * @group devKeys - */ - public function testUpdateProjectDevKey($data): array - { - $id = $data['projectId'] ?? ''; - $keyId = $data['keyId'] ?? ''; - - $response = $this->client->call(Client::METHOD_PUT, '/projects/' . $id . '/dev-keys/' . $keyId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'name' => 'Key Test Update', - 'expire' => DateTime::addSeconds(new \DateTime(), 360), - ]); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertNotEmpty($response['body']['$id']); - $this->assertEquals($keyId, $response['body']['$id']); - $this->assertEquals('Key Test Update', $response['body']['name']); - $this->assertArrayHasKey('accessedAt', $response['body']); - $this->assertNotEmpty($response['body']['accessedAt']); - - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys/' . $keyId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertNotEmpty($response['body']['$id']); - $this->assertEquals($keyId, $response['body']['$id']); - $this->assertEquals('Key Test Update', $response['body']['name']); - $this->assertArrayHasKey('accessedAt', $response['body']); - $this->assertNotEmpty($response['body']['accessedAt']); - - return $data; - } - - /** - * @depends testCreateProjectDevKey - * @group devKeys - */ - public function testDeleteProjectDevKey($data): array - { - $id = $data['projectId'] ?? ''; - $keyId = $data['keyId'] ?? ''; - - $response = $this->client->call(Client::METHOD_DELETE, '/projects/' . $id . '/dev-keys/' . $keyId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(204, $response['headers']['status-code']); - $this->assertEmpty($response['body']); - - /** - * Get rate limit trying to use the deleted key - */ - $res = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $id, - 'x-appwrite-dev-key' => $data['secret'] - ], [ - 'email' => 'user@appwrite.io', - 'password' => 'password' - ]); - $this->assertEquals(429, $res['headers']['status-code']); - - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/dev-keys/' . $keyId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(404, $response['headers']['status-code']); - - - /** - * Test for FAILURE - */ - $response = $this->client->call(Client::METHOD_DELETE, '/projects/' . $id . '/dev-keys/error', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(404, $response['headers']['status-code']); - - return $data; - } -} From 9bec94c976c020b27409c80591c267b86fba62fb Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 14 Apr 2025 18:02:30 +0000 Subject: [PATCH 103/110] fix: env --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index d18e63c56e..c10c12613b 100644 --- a/.env +++ b/.env @@ -15,7 +15,7 @@ _APP_SYSTEM_TEAM_EMAIL=team@appwrite.io _APP_EMAIL_SECURITY=security@appwrite.io _APP_EMAIL_CERTIFICATES=certificates@appwrite.io _APP_SYSTEM_RESPONSE_FORMAT= -_APP_OPTIONS_ABUSE=enabled +_APP_OPTIONS_ABUSE=disabled _APP_OPTIONS_ROUTER_PROTECTION=disabled _APP_OPTIONS_FORCE_HTTPS=disabled _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled From 40c15077e2bfc0a7ed37d13f1600261541f17a99 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 15 Apr 2025 10:47:04 +0000 Subject: [PATCH 104/110] chore: update devkey init and make tests use mock oauth --- app/init/resources.php | 2 +- .../Projects/ProjectsConsoleClientTest.php | 23 +++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/app/init/resources.php b/app/init/resources.php index f1a743f1f8..270062e582 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -819,7 +819,7 @@ App::setResource('devKey', function (Request $request, Document $project, array /** Update access time as well */ $key->setAttribute('accessedAt', DatabaseDateTime::now()); - Authorization::skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key)); + $key = Authorization::skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key)); $dbForPlatform->purgeCachedDocument('projects', $project->getId()); } } diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 491e0371b0..c2ee953044 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -4519,8 +4519,24 @@ class ProjectsConsoleClientTest extends Scope 'expire' => DateTime::addSeconds(new \DateTime(), 36000) ]); + $provider = 'mock'; + $appId = '1'; + $secret = '123456'; + + $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $projectId . '/oauth2', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'provider' => $provider, + 'appId' => $appId, + 'secret' => $secret, + 'enabled' => true, + ]); + $this->assertEquals(200, $response['headers']['status-code']); + /** Test oauth2 and get invalid `success` URL */ - $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/google', [ + $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, ], [ @@ -4531,7 +4547,7 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals('Invalid `success` param: URL host must be one of: localhost, appwrite.io, *.appwrite.io', $response['body']['message']); /** Test oauth2 with devKey and now get oauth2 is disabled */ - $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/google', [ + $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'x-appwrite-dev-key' => $devKey['secret'] @@ -4539,8 +4555,7 @@ class ProjectsConsoleClientTest extends Scope 'success' => 'https://example.com', 'failure' => 'https://example.com' ]); - $this->assertEquals(412, $response['headers']['status-code']); - $this->assertEquals('This provider is disabled. Please enable the provider from your Appwrite console to continue.', $response['body']['message']); + $this->assertEquals(200, $response['headers']['status-code']); /** Test hostname in Magic URL */ $response = $this->client->call(Client::METHOD_POST, '/account/sessions/magic-url', [ From c365480aec0f6cf2408a391231d44ef93793820b Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 15 Apr 2025 12:02:55 +0000 Subject: [PATCH 105/110] chore: small changes from review devkeys --- app/controllers/general.php | 12 ++++++++++-- app/init/resources.php | 6 +++--- .../Modules/Projects/Http/DevKeys/Delete.php | 4 ---- src/Appwrite/Specification/Format/OpenAPI3.php | 2 +- src/Appwrite/Specification/Format/Swagger2.php | 2 +- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 050dda5753..a103451b3b 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -714,9 +714,13 @@ App::init() ->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE') ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Forwarded-For, X-Forwarded-User-Agent') ->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies') - ->addHeader('Access-Control-Allow-Origin', $devKey->isEmpty() ? $refDomain : "*") + ->addHeader('Access-Control-Allow-Origin', $refDomain) ->addHeader('Access-Control-Allow-Credentials', 'true'); + if (!$devKey->isEmpty()) { + $response->addHeader('Access-Control-Allow-Origin', '*'); + } + /* * Validate Client Domain - Check to avoid CSRF attack * Adding Appwrite API domains to allow XDOMAIN communication @@ -772,10 +776,14 @@ App::options() ->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE') ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent') ->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies') - ->addHeader('Access-Control-Allow-Origin', $devKey->isEmpty() ? $origin : '*') + ->addHeader('Access-Control-Allow-Origin', $origin) ->addHeader('Access-Control-Allow-Credentials', 'true') ->noContent(); + if (!$devKey->isEmpty()) { + $response->addHeader('Access-Control-Allow-Origin', '*'); + } + /** OPTIONS requests in utopia do not execute shutdown handlers, as a result we need to track the OPTIONS requests explicitly * @see https://github.com/utopia-php/http/blob/0.33.16/src/App.php#L825-L855 */ diff --git a/app/init/resources.php b/app/init/resources.php index 270062e582..3a93419e11 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -800,7 +800,7 @@ App::setResource('devKey', function (Request $request, Document $project, array // update access time $accessedAt = $key->getAttribute('accessedAt', ''); - if (DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) { + if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) { $key->setAttribute('accessedAt', DatabaseDateTime::now()); Authorization::skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key)); $dbForPlatform->purgeCachedDocument('projects', $project->getId()); @@ -808,9 +808,9 @@ App::setResource('devKey', function (Request $request, Document $project, array // add sdk to key $sdkValidator = new WhiteList($servers, true); - $sdk = $request->getHeader('x-sdk-name', 'UNKNOWN'); + $sdk = $request->getHeader('x-sdk-name', null); - if ($sdkValidator->isValid($sdk)) { + if ($sdk && $sdkValidator->isValid($sdk)) { $sdks = $key->getAttribute('sdks', []); if (!in_array($sdk, $sdks)) { diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php index cce26a8de6..3adf26f816 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php @@ -66,10 +66,6 @@ class Delete extends Action throw new Exception(Exception::KEY_NOT_FOUND); } - if ($key === false || $key->isEmpty()) { - throw new Exception(Exception::KEY_NOT_FOUND); - } - $dbForPlatform->deleteDocument('devKeys', $key->getId()); $dbForPlatform->purgeCachedDocument('projects', $project->getId()); diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index e60d342b0b..157ccc8263 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -177,7 +177,7 @@ class OpenAPI3 extends Format $namespace = $sdk->getNamespace() ?? 'default'; - $desc = $desc ?? ''; + $desc ??= ''; $descContents = \str_ends_with($desc, '.md') ? \file_get_contents($desc) : $desc; $temp = [ diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index fae164f0a6..b6536df9df 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -173,7 +173,7 @@ class Swagger2 extends Format $namespace = $sdk->getNamespace() ?? 'default'; - $desc = $desc ?? ''; + $desc ??= ''; $descContents = \str_ends_with($desc, '.md') ? \file_get_contents($desc) : $desc; $temp = [ From 76c7eb14ebeeecb8cd759ecdce13a82d69f06ac7 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 15 Apr 2025 12:08:18 +0000 Subject: [PATCH 106/110] chore: fix sdk fallback back to unknown --- app/controllers/shared/api.php | 2 +- app/init/resources.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 35a9499d28..14b8091724 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -250,7 +250,7 @@ App::init() $sdkValidator = new WhiteList($servers, true); $sdk = $request->getHeader('x-sdk-name', 'UNKNOWN'); - if ($sdkValidator->isValid($sdk)) { + if ($sdk !== 'UNKNOWN' && $sdkValidator->isValid($sdk)) { $sdks = $dbKey->getAttribute('sdks', []); if (!in_array($sdk, $sdks)) { diff --git a/app/init/resources.php b/app/init/resources.php index 3a93419e11..c8cd9aaf4f 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -808,9 +808,9 @@ App::setResource('devKey', function (Request $request, Document $project, array // add sdk to key $sdkValidator = new WhiteList($servers, true); - $sdk = $request->getHeader('x-sdk-name', null); + $sdk = $request->getHeader('x-sdk-name', 'UNKNOWN'); - if ($sdk && $sdkValidator->isValid($sdk)) { + if ($sdk !== 'UNKNOWN' && $sdkValidator->isValid($sdk)) { $sdks = $key->getAttribute('sdks', []); if (!in_array($sdk, $sdks)) { From b8ce3cb9fe8554331e06cb6b58eb6a87ce2a0929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 17 Apr 2025 15:57:47 +0200 Subject: [PATCH 107/110] Post-merge fixes --- app/config/specs/open-api3-latest-client.json | 6 +- .../specs/open-api3-latest-console.json | 603 ++++++++++++++++-- app/config/specs/open-api3-latest-server.json | 102 +-- app/config/specs/swagger2-latest-client.json | 6 +- app/config/specs/swagger2-latest-console.json | 601 +++++++++++++++-- app/config/specs/swagger2-latest-server.json | 102 +-- app/controllers/general.php | 8 - src/Appwrite/Platform/Appwrite.php | 2 +- src/Appwrite/Utopia/Response.php | 2 +- 9 files changed, 1186 insertions(+), 246 deletions(-) diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index 6d50b1e06e..51fa03872f 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -4763,7 +4763,7 @@ }, "x-appwrite": { "method": "listExecutions", - "weight": 384, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -4837,7 +4837,7 @@ }, "x-appwrite": { "method": "createExecution", - "weight": 382, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -4951,7 +4951,7 @@ }, "x-appwrite": { "method": "getExecution", - "weight": 383, + "weight": 388, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index a1f9e94f6c..bb75849441 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -4354,7 +4354,7 @@ }, "x-appwrite": { "method": "getResource", - "weight": 424, + "weight": 429, "cookies": false, "type": "", "deprecated": false, @@ -9051,7 +9051,7 @@ }, "x-appwrite": { "method": "list", - "weight": 368, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -9123,7 +9123,7 @@ }, "x-appwrite": { "method": "create", - "weight": 365, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -9353,7 +9353,7 @@ }, "x-appwrite": { "method": "listRuntimes", - "weight": 370, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -9401,7 +9401,7 @@ }, "x-appwrite": { "method": "listSpecifications", - "weight": 371, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -9450,7 +9450,7 @@ }, "x-appwrite": { "method": "listTemplates", - "weight": 394, + "weight": 399, "cookies": false, "type": "", "deprecated": false, @@ -9549,7 +9549,7 @@ }, "x-appwrite": { "method": "getTemplate", - "weight": 393, + "weight": 398, "cookies": false, "type": "", "deprecated": false, @@ -9608,7 +9608,7 @@ }, "x-appwrite": { "method": "listUsage", - "weight": 387, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -9679,7 +9679,7 @@ }, "x-appwrite": { "method": "get", - "weight": 366, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -9737,7 +9737,7 @@ }, "x-appwrite": { "method": "update", - "weight": 367, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -9964,7 +9964,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 369, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -10024,7 +10024,7 @@ }, "x-appwrite": { "method": "updateFunctionDeployment", - "weight": 374, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -10103,7 +10103,7 @@ }, "x-appwrite": { "method": "listDeployments", - "weight": 375, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -10185,7 +10185,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 372, + "weight": 377, "cookies": false, "type": "upload", "deprecated": false, @@ -10280,7 +10280,7 @@ }, "x-appwrite": { "method": "createDuplicateDeployment", - "weight": 380, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -10364,7 +10364,7 @@ }, "x-appwrite": { "method": "createTemplateDeployment", - "weight": 377, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -10466,7 +10466,7 @@ }, "x-appwrite": { "method": "createVcsDeployment", - "weight": 378, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -10562,7 +10562,7 @@ }, "x-appwrite": { "method": "getDeployment", - "weight": 373, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -10623,7 +10623,7 @@ }, "x-appwrite": { "method": "deleteDeployment", - "weight": 376, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -10686,7 +10686,7 @@ }, "x-appwrite": { "method": "getDeploymentDownload", - "weight": 379, + "weight": 384, "cookies": false, "type": "location", "deprecated": false, @@ -10775,7 +10775,7 @@ }, "x-appwrite": { "method": "updateDeploymentStatus", - "weight": 381, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -10845,7 +10845,7 @@ }, "x-appwrite": { "method": "listExecutions", - "weight": 384, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -10919,7 +10919,7 @@ }, "x-appwrite": { "method": "createExecution", - "weight": 382, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -11033,7 +11033,7 @@ }, "x-appwrite": { "method": "getExecution", - "weight": 383, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -11097,7 +11097,7 @@ }, "x-appwrite": { "method": "deleteExecution", - "weight": 385, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -11167,7 +11167,7 @@ }, "x-appwrite": { "method": "getUsage", - "weight": 386, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -11248,7 +11248,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 390, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -11306,7 +11306,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 388, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -11396,7 +11396,7 @@ }, "x-appwrite": { "method": "getVariable", - "weight": 389, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -11464,7 +11464,7 @@ }, "x-appwrite": { "method": "updateVariable", - "weight": 391, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -11554,7 +11554,7 @@ }, "x-appwrite": { "method": "deleteVariable", - "weight": 392, + "weight": 397, "cookies": false, "type": "", "deprecated": false, @@ -20708,6 +20708,389 @@ } } }, + "\/projects\/{projectId}\/dev-keys": { + "get": { + "summary": "List dev keys", + "operationId": "projectsListDevKeys", + "tags": [ + "projects" + ], + "description": "List all the project\\'s dev keys. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development.'", + "responses": { + "200": { + "description": "Dev Keys List", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/devKeyList" + } + } + } + } + }, + "x-appwrite": { + "method": "listDevKeys", + "weight": 368, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/list-dev-keys.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterList all the project\\'s dev keys. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development.'", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.read", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: accessedAt, expire", + "required": false, + "schema": { + "type": "string", + "default": [] + }, + "in": "query" + }, + { + "name": "search", + "description": "Search term to filter your list results. Max length: 256 chars.", + "required": false, + "schema": { + "type": "string", + "x-example": "", + "default": "" + }, + "in": "query" + } + ] + }, + "post": { + "summary": "Create dev key", + "operationId": "projectsCreateDevKey", + "tags": [ + "projects" + ], + "description": "Create a new project dev key. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development. Strictly meant for development purposes only.", + "responses": { + "201": { + "description": "DevKey", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/devKey" + } + } + } + } + }, + "x-appwrite": { + "method": "createDevKey", + "weight": 365, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/create-dev-key.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new project dev key. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development. Strictly meant for development purposes only.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.write", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Key name. Max length: 128 chars.", + "x-example": "" + }, + "expire": { + "type": "string", + "description": "Expiration time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format.", + "x-example": null + } + }, + "required": [ + "name", + "expire" + ] + } + } + } + } + } + }, + "\/projects\/{projectId}\/dev-keys\/{keyId}": { + "get": { + "summary": "Get dev key", + "operationId": "projectsGetDevKey", + "tags": [ + "projects" + ], + "description": "Get a project\\'s dev key by its unique ID. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development.", + "responses": { + "200": { + "description": "DevKey", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/devKey" + } + } + } + } + }, + "x-appwrite": { + "method": "getDevKey", + "weight": 367, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/get-dev-key.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterGet a project\\'s dev key by its unique ID. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.read", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "keyId", + "description": "Key unique ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + }, + "put": { + "summary": "Update dev key", + "operationId": "projectsUpdateDevKey", + "tags": [ + "projects" + ], + "description": "Update a project\\'s dev key by its unique ID. Use this endpoint to update a project\\'s dev key name or expiration time.'", + "responses": { + "200": { + "description": "DevKey", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/devKey" + } + } + } + } + }, + "x-appwrite": { + "method": "updateDevKey", + "weight": 366, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/update-dev-key.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterUpdate a project\\'s dev key by its unique ID. Use this endpoint to update a project\\'s dev key name or expiration time.'", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.write", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "keyId", + "description": "Key unique ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Key name. Max length: 128 chars.", + "x-example": "" + }, + "expire": { + "type": "string", + "description": "Expiration time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format.", + "x-example": null + } + }, + "required": [ + "name", + "expire" + ] + } + } + } + } + }, + "delete": { + "summary": "Delete dev key", + "operationId": "projectsDeleteDevKey", + "tags": [ + "projects" + ], + "description": "Delete a project\\'s dev key by its unique ID. Once deleted, the key will no longer allow bypassing of rate limits and better logging of errors.", + "responses": { + "204": { + "description": "No content" + } + }, + "x-appwrite": { + "method": "deleteDevKey", + "weight": 369, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/delete-dev-key.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterDelete a project\\'s dev key by its unique ID. Once deleted, the key will no longer allow bypassing of rate limits and better logging of errors.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.write", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "keyId", + "description": "Key unique ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + } + }, "\/projects\/{projectId}\/jwts": { "post": { "summary": "Create JWT", @@ -24194,7 +24577,7 @@ }, "x-appwrite": { "method": "createAPIRule", - "weight": 425, + "weight": 430, "cookies": false, "type": "", "deprecated": false, @@ -24260,7 +24643,7 @@ }, "x-appwrite": { "method": "createFunctionRule", - "weight": 427, + "weight": 432, "cookies": false, "type": "", "deprecated": false, @@ -24337,7 +24720,7 @@ }, "x-appwrite": { "method": "createRedirectRule", - "weight": 428, + "weight": 433, "cookies": false, "type": "", "deprecated": false, @@ -24428,7 +24811,7 @@ }, "x-appwrite": { "method": "createSiteRule", - "weight": 426, + "weight": 431, "cookies": false, "type": "", "deprecated": false, @@ -24673,7 +25056,7 @@ }, "x-appwrite": { "method": "list", - "weight": 397, + "weight": 402, "cookies": false, "type": "", "deprecated": false, @@ -24742,7 +25125,7 @@ }, "x-appwrite": { "method": "create", - "weight": 395, + "weight": 400, "cookies": false, "type": "", "deprecated": false, @@ -24987,7 +25370,7 @@ }, "x-appwrite": { "method": "listFrameworks", - "weight": 400, + "weight": 405, "cookies": false, "type": "", "deprecated": false, @@ -25035,7 +25418,7 @@ }, "x-appwrite": { "method": "listSpecifications", - "weight": 423, + "weight": 428, "cookies": false, "type": "", "deprecated": false, @@ -25084,7 +25467,7 @@ }, "x-appwrite": { "method": "listTemplates", - "weight": 419, + "weight": 424, "cookies": false, "type": "", "deprecated": false, @@ -25183,7 +25566,7 @@ }, "x-appwrite": { "method": "getTemplate", - "weight": 420, + "weight": 425, "cookies": false, "type": "", "deprecated": false, @@ -25242,7 +25625,7 @@ }, "x-appwrite": { "method": "listUsage", - "weight": 421, + "weight": 426, "cookies": false, "type": "", "deprecated": false, @@ -25313,7 +25696,7 @@ }, "x-appwrite": { "method": "get", - "weight": 396, + "weight": 401, "cookies": false, "type": "", "deprecated": false, @@ -25371,7 +25754,7 @@ }, "x-appwrite": { "method": "update", - "weight": 398, + "weight": 403, "cookies": false, "type": "", "deprecated": false, @@ -25612,7 +25995,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 399, + "weight": 404, "cookies": false, "type": "", "deprecated": false, @@ -25672,7 +26055,7 @@ }, "x-appwrite": { "method": "updateSiteDeployment", - "weight": 406, + "weight": 411, "cookies": false, "type": "", "deprecated": false, @@ -25751,7 +26134,7 @@ }, "x-appwrite": { "method": "listDeployments", - "weight": 405, + "weight": 410, "cookies": false, "type": "", "deprecated": false, @@ -25833,7 +26216,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 401, + "weight": 406, "cookies": false, "type": "upload", "deprecated": false, @@ -25933,7 +26316,7 @@ }, "x-appwrite": { "method": "createDuplicateDeployment", - "weight": 409, + "weight": 414, "cookies": false, "type": "", "deprecated": false, @@ -26012,7 +26395,7 @@ }, "x-appwrite": { "method": "createTemplateDeployment", - "weight": 402, + "weight": 407, "cookies": false, "type": "", "deprecated": false, @@ -26114,7 +26497,7 @@ }, "x-appwrite": { "method": "createVcsDeployment", - "weight": 403, + "weight": 408, "cookies": false, "type": "", "deprecated": false, @@ -26211,7 +26594,7 @@ }, "x-appwrite": { "method": "getDeployment", - "weight": 404, + "weight": 409, "cookies": false, "type": "", "deprecated": false, @@ -26272,7 +26655,7 @@ }, "x-appwrite": { "method": "deleteDeployment", - "weight": 407, + "weight": 412, "cookies": false, "type": "", "deprecated": false, @@ -26335,7 +26718,7 @@ }, "x-appwrite": { "method": "getDeploymentDownload", - "weight": 408, + "weight": 413, "cookies": false, "type": "location", "deprecated": false, @@ -26424,7 +26807,7 @@ }, "x-appwrite": { "method": "updateDeploymentStatus", - "weight": 410, + "weight": 415, "cookies": false, "type": "", "deprecated": false, @@ -26494,7 +26877,7 @@ }, "x-appwrite": { "method": "listLogs", - "weight": 412, + "weight": 417, "cookies": false, "type": "", "deprecated": false, @@ -26564,7 +26947,7 @@ }, "x-appwrite": { "method": "getLog", - "weight": 411, + "weight": 416, "cookies": false, "type": "", "deprecated": false, @@ -26625,7 +27008,7 @@ }, "x-appwrite": { "method": "deleteLog", - "weight": 413, + "weight": 418, "cookies": false, "type": "", "deprecated": false, @@ -26695,7 +27078,7 @@ }, "x-appwrite": { "method": "getUsage", - "weight": 422, + "weight": 427, "cookies": false, "type": "", "deprecated": false, @@ -26776,7 +27159,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 416, + "weight": 421, "cookies": false, "type": "", "deprecated": false, @@ -26834,7 +27217,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 414, + "weight": 419, "cookies": false, "type": "", "deprecated": false, @@ -26924,7 +27307,7 @@ }, "x-appwrite": { "method": "getVariable", - "weight": 415, + "weight": 420, "cookies": false, "type": "", "deprecated": false, @@ -26992,7 +27375,7 @@ }, "x-appwrite": { "method": "updateVariable", - "weight": 417, + "weight": 422, "cookies": false, "type": "", "deprecated": false, @@ -27082,7 +27465,7 @@ }, "x-appwrite": { "method": "deleteVariable", - "weight": 418, + "weight": 423, "cookies": false, "type": "", "deprecated": false, @@ -34331,6 +34714,30 @@ "keys" ] }, + "devKeyList": { + "description": "Dev Keys List", + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Total number of devKeys documents that matched your query.", + "x-example": 5, + "format": "int32" + }, + "devKeys": { + "type": "array", + "description": "List of devKeys.", + "items": { + "$ref": "#\/components\/schemas\/devKey" + }, + "x-example": "" + } + }, + "required": [ + "total", + "devKeys" + ] + }, "platformList": { "description": "Platforms List", "type": "object", @@ -38603,6 +39010,14 @@ }, "x-example": {} }, + "devKeys": { + "type": "array", + "description": "List of dev keys.", + "items": { + "$ref": "#\/components\/schemas\/devKey" + }, + "x-example": {} + }, "smtpEnabled": { "type": "boolean", "description": "Status for custom SMTP", @@ -38786,6 +39201,7 @@ "platforms", "webhooks", "keys", + "devKeys", "smtpEnabled", "smtpSenderName", "smtpSenderEmail", @@ -38976,6 +39392,65 @@ "sdks" ] }, + "devKey": { + "description": "DevKey", + "type": "object", + "properties": { + "$id": { + "type": "string", + "description": "Key ID.", + "x-example": "5e5ea5c16897e" + }, + "$createdAt": { + "type": "string", + "description": "Key creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "$updatedAt": { + "type": "string", + "description": "Key update date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "name": { + "type": "string", + "description": "Key name.", + "x-example": "Dev API Key" + }, + "expire": { + "type": "string", + "description": "Key expiration date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "secret": { + "type": "string", + "description": "Secret key.", + "x-example": "919c2d18fb5d4...a2ae413da83346ad2" + }, + "accessedAt": { + "type": "string", + "description": "Most recent access date in ISO 8601 format. This attribute is only updated again after 24 hours.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "sdks": { + "type": "array", + "description": "List of SDK user agents that used this key.", + "items": { + "type": "string" + }, + "x-example": "appwrite:flutter" + } + }, + "required": [ + "$id", + "$createdAt", + "$updatedAt", + "name", + "expire", + "secret", + "accessedAt", + "sdks" + ] + }, "mockNumber": { "description": "Mock Number", "type": "object", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index df5948090b..ef67d12e0d 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -8136,7 +8136,7 @@ }, "x-appwrite": { "method": "list", - "weight": 368, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -8209,7 +8209,7 @@ }, "x-appwrite": { "method": "create", - "weight": 365, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -8440,7 +8440,7 @@ }, "x-appwrite": { "method": "listRuntimes", - "weight": 370, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -8489,7 +8489,7 @@ }, "x-appwrite": { "method": "listSpecifications", - "weight": 371, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -8539,7 +8539,7 @@ }, "x-appwrite": { "method": "get", - "weight": 366, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -8598,7 +8598,7 @@ }, "x-appwrite": { "method": "update", - "weight": 367, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -8826,7 +8826,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 369, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -8887,7 +8887,7 @@ }, "x-appwrite": { "method": "updateFunctionDeployment", - "weight": 374, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -8967,7 +8967,7 @@ }, "x-appwrite": { "method": "listDeployments", - "weight": 375, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -9050,7 +9050,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 372, + "weight": 377, "cookies": false, "type": "upload", "deprecated": false, @@ -9146,7 +9146,7 @@ }, "x-appwrite": { "method": "createDuplicateDeployment", - "weight": 380, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -9231,7 +9231,7 @@ }, "x-appwrite": { "method": "createTemplateDeployment", - "weight": 377, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -9334,7 +9334,7 @@ }, "x-appwrite": { "method": "createVcsDeployment", - "weight": 378, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -9431,7 +9431,7 @@ }, "x-appwrite": { "method": "getDeployment", - "weight": 373, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -9493,7 +9493,7 @@ }, "x-appwrite": { "method": "deleteDeployment", - "weight": 376, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -9557,7 +9557,7 @@ }, "x-appwrite": { "method": "getDeploymentDownload", - "weight": 379, + "weight": 384, "cookies": false, "type": "location", "deprecated": false, @@ -9647,7 +9647,7 @@ }, "x-appwrite": { "method": "updateDeploymentStatus", - "weight": 381, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -9718,7 +9718,7 @@ }, "x-appwrite": { "method": "listExecutions", - "weight": 384, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -9794,7 +9794,7 @@ }, "x-appwrite": { "method": "createExecution", - "weight": 382, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -9910,7 +9910,7 @@ }, "x-appwrite": { "method": "getExecution", - "weight": 383, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -9976,7 +9976,7 @@ }, "x-appwrite": { "method": "deleteExecution", - "weight": 385, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -10047,7 +10047,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 390, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -10106,7 +10106,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 388, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -10197,7 +10197,7 @@ }, "x-appwrite": { "method": "getVariable", - "weight": 389, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -10266,7 +10266,7 @@ }, "x-appwrite": { "method": "updateVariable", - "weight": 391, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -10357,7 +10357,7 @@ }, "x-appwrite": { "method": "deleteVariable", - "weight": 392, + "weight": 397, "cookies": false, "type": "", "deprecated": false, @@ -16691,7 +16691,7 @@ }, "x-appwrite": { "method": "list", - "weight": 397, + "weight": 402, "cookies": false, "type": "", "deprecated": false, @@ -16761,7 +16761,7 @@ }, "x-appwrite": { "method": "create", - "weight": 395, + "weight": 400, "cookies": false, "type": "", "deprecated": false, @@ -17007,7 +17007,7 @@ }, "x-appwrite": { "method": "listFrameworks", - "weight": 400, + "weight": 405, "cookies": false, "type": "", "deprecated": false, @@ -17056,7 +17056,7 @@ }, "x-appwrite": { "method": "listSpecifications", - "weight": 423, + "weight": 428, "cookies": false, "type": "", "deprecated": false, @@ -17106,7 +17106,7 @@ }, "x-appwrite": { "method": "get", - "weight": 396, + "weight": 401, "cookies": false, "type": "", "deprecated": false, @@ -17165,7 +17165,7 @@ }, "x-appwrite": { "method": "update", - "weight": 398, + "weight": 403, "cookies": false, "type": "", "deprecated": false, @@ -17407,7 +17407,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 399, + "weight": 404, "cookies": false, "type": "", "deprecated": false, @@ -17468,7 +17468,7 @@ }, "x-appwrite": { "method": "updateSiteDeployment", - "weight": 406, + "weight": 411, "cookies": false, "type": "", "deprecated": false, @@ -17548,7 +17548,7 @@ }, "x-appwrite": { "method": "listDeployments", - "weight": 405, + "weight": 410, "cookies": false, "type": "", "deprecated": false, @@ -17631,7 +17631,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 401, + "weight": 406, "cookies": false, "type": "upload", "deprecated": false, @@ -17732,7 +17732,7 @@ }, "x-appwrite": { "method": "createDuplicateDeployment", - "weight": 409, + "weight": 414, "cookies": false, "type": "", "deprecated": false, @@ -17812,7 +17812,7 @@ }, "x-appwrite": { "method": "createTemplateDeployment", - "weight": 402, + "weight": 407, "cookies": false, "type": "", "deprecated": false, @@ -17915,7 +17915,7 @@ }, "x-appwrite": { "method": "createVcsDeployment", - "weight": 403, + "weight": 408, "cookies": false, "type": "", "deprecated": false, @@ -18013,7 +18013,7 @@ }, "x-appwrite": { "method": "getDeployment", - "weight": 404, + "weight": 409, "cookies": false, "type": "", "deprecated": false, @@ -18075,7 +18075,7 @@ }, "x-appwrite": { "method": "deleteDeployment", - "weight": 407, + "weight": 412, "cookies": false, "type": "", "deprecated": false, @@ -18139,7 +18139,7 @@ }, "x-appwrite": { "method": "getDeploymentDownload", - "weight": 408, + "weight": 413, "cookies": false, "type": "location", "deprecated": false, @@ -18229,7 +18229,7 @@ }, "x-appwrite": { "method": "updateDeploymentStatus", - "weight": 410, + "weight": 415, "cookies": false, "type": "", "deprecated": false, @@ -18300,7 +18300,7 @@ }, "x-appwrite": { "method": "listLogs", - "weight": 412, + "weight": 417, "cookies": false, "type": "", "deprecated": false, @@ -18371,7 +18371,7 @@ }, "x-appwrite": { "method": "getLog", - "weight": 411, + "weight": 416, "cookies": false, "type": "", "deprecated": false, @@ -18433,7 +18433,7 @@ }, "x-appwrite": { "method": "deleteLog", - "weight": 413, + "weight": 418, "cookies": false, "type": "", "deprecated": false, @@ -18504,7 +18504,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 416, + "weight": 421, "cookies": false, "type": "", "deprecated": false, @@ -18563,7 +18563,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 414, + "weight": 419, "cookies": false, "type": "", "deprecated": false, @@ -18654,7 +18654,7 @@ }, "x-appwrite": { "method": "getVariable", - "weight": 415, + "weight": 420, "cookies": false, "type": "", "deprecated": false, @@ -18723,7 +18723,7 @@ }, "x-appwrite": { "method": "updateVariable", - "weight": 417, + "weight": 422, "cookies": false, "type": "", "deprecated": false, @@ -18814,7 +18814,7 @@ }, "x-appwrite": { "method": "deleteVariable", - "weight": 418, + "weight": 423, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index dc5600aa86..c156923114 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -4929,7 +4929,7 @@ }, "x-appwrite": { "method": "listExecutions", - "weight": 384, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -5002,7 +5002,7 @@ }, "x-appwrite": { "method": "createExecution", - "weight": 382, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -5120,7 +5120,7 @@ }, "x-appwrite": { "method": "getExecution", - "weight": 383, + "weight": 388, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 76f1152287..6456cc6451 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -4563,7 +4563,7 @@ }, "x-appwrite": { "method": "getResource", - "weight": 424, + "weight": 429, "cookies": false, "type": "", "deprecated": false, @@ -9200,7 +9200,7 @@ }, "x-appwrite": { "method": "list", - "weight": 368, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -9271,7 +9271,7 @@ }, "x-appwrite": { "method": "create", - "weight": 365, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -9521,7 +9521,7 @@ }, "x-appwrite": { "method": "listRuntimes", - "weight": 370, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -9571,7 +9571,7 @@ }, "x-appwrite": { "method": "listSpecifications", - "weight": 371, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -9622,7 +9622,7 @@ }, "x-appwrite": { "method": "listTemplates", - "weight": 394, + "weight": 399, "cookies": false, "type": "", "deprecated": false, @@ -9717,7 +9717,7 @@ }, "x-appwrite": { "method": "getTemplate", - "weight": 393, + "weight": 398, "cookies": false, "type": "", "deprecated": false, @@ -9776,7 +9776,7 @@ }, "x-appwrite": { "method": "listUsage", - "weight": 387, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -9847,7 +9847,7 @@ }, "x-appwrite": { "method": "get", - "weight": 366, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -9905,7 +9905,7 @@ }, "x-appwrite": { "method": "update", - "weight": 367, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -10149,7 +10149,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 369, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -10209,7 +10209,7 @@ }, "x-appwrite": { "method": "updateFunctionDeployment", - "weight": 374, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -10287,7 +10287,7 @@ }, "x-appwrite": { "method": "listDeployments", - "weight": 375, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -10366,7 +10366,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 372, + "weight": 377, "cookies": false, "type": "upload", "deprecated": false, @@ -10457,7 +10457,7 @@ }, "x-appwrite": { "method": "createDuplicateDeployment", - "weight": 380, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -10541,7 +10541,7 @@ }, "x-appwrite": { "method": "createTemplateDeployment", - "weight": 377, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -10646,7 +10646,7 @@ }, "x-appwrite": { "method": "createVcsDeployment", - "weight": 378, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -10743,7 +10743,7 @@ }, "x-appwrite": { "method": "getDeployment", - "weight": 373, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -10804,7 +10804,7 @@ }, "x-appwrite": { "method": "deleteDeployment", - "weight": 376, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -10872,7 +10872,7 @@ }, "x-appwrite": { "method": "getDeploymentDownload", - "weight": 379, + "weight": 384, "cookies": false, "type": "location", "deprecated": false, @@ -10957,7 +10957,7 @@ }, "x-appwrite": { "method": "updateDeploymentStatus", - "weight": 381, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -11025,7 +11025,7 @@ }, "x-appwrite": { "method": "listExecutions", - "weight": 384, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -11098,7 +11098,7 @@ }, "x-appwrite": { "method": "createExecution", - "weight": 382, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -11216,7 +11216,7 @@ }, "x-appwrite": { "method": "getExecution", - "weight": 383, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -11280,7 +11280,7 @@ }, "x-appwrite": { "method": "deleteExecution", - "weight": 385, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -11348,7 +11348,7 @@ }, "x-appwrite": { "method": "getUsage", - "weight": 386, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -11427,7 +11427,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 390, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -11485,7 +11485,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 388, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -11576,7 +11576,7 @@ }, "x-appwrite": { "method": "getVariable", - "weight": 389, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -11642,7 +11642,7 @@ }, "x-appwrite": { "method": "updateVariable", - "weight": 391, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -11733,7 +11733,7 @@ }, "x-appwrite": { "method": "deleteVariable", - "weight": 392, + "weight": 397, "cookies": false, "type": "", "deprecated": false, @@ -21190,6 +21190,385 @@ ] } }, + "\/projects\/{projectId}\/dev-keys": { + "get": { + "summary": "List dev keys", + "operationId": "projectsListDevKeys", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "projects" + ], + "description": "List all the project\\'s dev keys. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development.'", + "responses": { + "200": { + "description": "Dev Keys List", + "schema": { + "$ref": "#\/definitions\/devKeyList" + } + } + }, + "x-appwrite": { + "method": "listDevKeys", + "weight": 368, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/list-dev-keys.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterList all the project\\'s dev keys. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development.'", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.read", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: accessedAt, expire", + "required": false, + "type": "array", + "collectionFormat": "multi", + "items": { + "type": "string" + }, + "default": [], + "in": "query" + }, + { + "name": "search", + "description": "Search term to filter your list results. Max length: 256 chars.", + "required": false, + "type": "string", + "x-example": "", + "default": "", + "in": "query" + } + ] + }, + "post": { + "summary": "Create dev key", + "operationId": "projectsCreateDevKey", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "projects" + ], + "description": "Create a new project dev key. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development. Strictly meant for development purposes only.", + "responses": { + "201": { + "description": "DevKey", + "schema": { + "$ref": "#\/definitions\/devKey" + } + } + }, + "x-appwrite": { + "method": "createDevKey", + "weight": 365, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/create-dev-key.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new project dev key. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development. Strictly meant for development purposes only.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.write", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Key name. Max length: 128 chars.", + "default": null, + "x-example": "" + }, + "expire": { + "type": "string", + "description": "Expiration time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format.", + "default": null, + "x-example": null + } + }, + "required": [ + "name", + "expire" + ] + } + } + ] + } + }, + "\/projects\/{projectId}\/dev-keys\/{keyId}": { + "get": { + "summary": "Get dev key", + "operationId": "projectsGetDevKey", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "projects" + ], + "description": "Get a project\\'s dev key by its unique ID. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development.", + "responses": { + "200": { + "description": "DevKey", + "schema": { + "$ref": "#\/definitions\/devKey" + } + } + }, + "x-appwrite": { + "method": "getDevKey", + "weight": 367, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/get-dev-key.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterGet a project\\'s dev key by its unique ID. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.read", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "keyId", + "description": "Key unique ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + }, + "put": { + "summary": "Update dev key", + "operationId": "projectsUpdateDevKey", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "projects" + ], + "description": "Update a project\\'s dev key by its unique ID. Use this endpoint to update a project\\'s dev key name or expiration time.'", + "responses": { + "200": { + "description": "DevKey", + "schema": { + "$ref": "#\/definitions\/devKey" + } + } + }, + "x-appwrite": { + "method": "updateDevKey", + "weight": 366, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/update-dev-key.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterUpdate a project\\'s dev key by its unique ID. Use this endpoint to update a project\\'s dev key name or expiration time.'", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.write", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "keyId", + "description": "Key unique ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Key name. Max length: 128 chars.", + "default": null, + "x-example": "" + }, + "expire": { + "type": "string", + "description": "Expiration time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format.", + "default": null, + "x-example": null + } + }, + "required": [ + "name", + "expire" + ] + } + } + ] + }, + "delete": { + "summary": "Delete dev key", + "operationId": "projectsDeleteDevKey", + "consumes": [ + "application\/json" + ], + "produces": [], + "tags": [ + "projects" + ], + "description": "Delete a project\\'s dev key by its unique ID. Once deleted, the key will no longer allow bypassing of rate limits and better logging of errors.", + "responses": { + "204": { + "description": "No content" + } + }, + "x-appwrite": { + "method": "deleteDevKey", + "weight": 369, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "projects\/delete-dev-key.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterDelete a project\\'s dev key by its unique ID. Once deleted, the key will no longer allow bypassing of rate limits and better logging of errors.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "projects.write", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "projectId", + "description": "Project unique ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "keyId", + "description": "Key unique ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + } + }, "\/projects\/{projectId}\/jwts": { "post": { "summary": "Create JWT", @@ -24676,7 +25055,7 @@ }, "x-appwrite": { "method": "createAPIRule", - "weight": 425, + "weight": 430, "cookies": false, "type": "", "deprecated": false, @@ -24745,7 +25124,7 @@ }, "x-appwrite": { "method": "createFunctionRule", - "weight": 427, + "weight": 432, "cookies": false, "type": "", "deprecated": false, @@ -24827,7 +25206,7 @@ }, "x-appwrite": { "method": "createRedirectRule", - "weight": 428, + "weight": 433, "cookies": false, "type": "", "deprecated": false, @@ -24923,7 +25302,7 @@ }, "x-appwrite": { "method": "createSiteRule", - "weight": 426, + "weight": 431, "cookies": false, "type": "", "deprecated": false, @@ -25175,7 +25554,7 @@ }, "x-appwrite": { "method": "list", - "weight": 397, + "weight": 402, "cookies": false, "type": "", "deprecated": false, @@ -25246,7 +25625,7 @@ }, "x-appwrite": { "method": "create", - "weight": 395, + "weight": 400, "cookies": false, "type": "", "deprecated": false, @@ -25511,7 +25890,7 @@ }, "x-appwrite": { "method": "listFrameworks", - "weight": 400, + "weight": 405, "cookies": false, "type": "", "deprecated": false, @@ -25561,7 +25940,7 @@ }, "x-appwrite": { "method": "listSpecifications", - "weight": 423, + "weight": 428, "cookies": false, "type": "", "deprecated": false, @@ -25612,7 +25991,7 @@ }, "x-appwrite": { "method": "listTemplates", - "weight": 419, + "weight": 424, "cookies": false, "type": "", "deprecated": false, @@ -25707,7 +26086,7 @@ }, "x-appwrite": { "method": "getTemplate", - "weight": 420, + "weight": 425, "cookies": false, "type": "", "deprecated": false, @@ -25766,7 +26145,7 @@ }, "x-appwrite": { "method": "listUsage", - "weight": 421, + "weight": 426, "cookies": false, "type": "", "deprecated": false, @@ -25837,7 +26216,7 @@ }, "x-appwrite": { "method": "get", - "weight": 396, + "weight": 401, "cookies": false, "type": "", "deprecated": false, @@ -25895,7 +26274,7 @@ }, "x-appwrite": { "method": "update", - "weight": 398, + "weight": 403, "cookies": false, "type": "", "deprecated": false, @@ -26153,7 +26532,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 399, + "weight": 404, "cookies": false, "type": "", "deprecated": false, @@ -26213,7 +26592,7 @@ }, "x-appwrite": { "method": "updateSiteDeployment", - "weight": 406, + "weight": 411, "cookies": false, "type": "", "deprecated": false, @@ -26291,7 +26670,7 @@ }, "x-appwrite": { "method": "listDeployments", - "weight": 405, + "weight": 410, "cookies": false, "type": "", "deprecated": false, @@ -26370,7 +26749,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 401, + "weight": 406, "cookies": false, "type": "upload", "deprecated": false, @@ -26469,7 +26848,7 @@ }, "x-appwrite": { "method": "createDuplicateDeployment", - "weight": 409, + "weight": 414, "cookies": false, "type": "", "deprecated": false, @@ -26547,7 +26926,7 @@ }, "x-appwrite": { "method": "createTemplateDeployment", - "weight": 402, + "weight": 407, "cookies": false, "type": "", "deprecated": false, @@ -26652,7 +27031,7 @@ }, "x-appwrite": { "method": "createVcsDeployment", - "weight": 403, + "weight": 408, "cookies": false, "type": "", "deprecated": false, @@ -26750,7 +27129,7 @@ }, "x-appwrite": { "method": "getDeployment", - "weight": 404, + "weight": 409, "cookies": false, "type": "", "deprecated": false, @@ -26811,7 +27190,7 @@ }, "x-appwrite": { "method": "deleteDeployment", - "weight": 407, + "weight": 412, "cookies": false, "type": "", "deprecated": false, @@ -26879,7 +27258,7 @@ }, "x-appwrite": { "method": "getDeploymentDownload", - "weight": 408, + "weight": 413, "cookies": false, "type": "location", "deprecated": false, @@ -26964,7 +27343,7 @@ }, "x-appwrite": { "method": "updateDeploymentStatus", - "weight": 410, + "weight": 415, "cookies": false, "type": "", "deprecated": false, @@ -27032,7 +27411,7 @@ }, "x-appwrite": { "method": "listLogs", - "weight": 412, + "weight": 417, "cookies": false, "type": "", "deprecated": false, @@ -27104,7 +27483,7 @@ }, "x-appwrite": { "method": "getLog", - "weight": 411, + "weight": 416, "cookies": false, "type": "", "deprecated": false, @@ -27167,7 +27546,7 @@ }, "x-appwrite": { "method": "deleteLog", - "weight": 413, + "weight": 418, "cookies": false, "type": "", "deprecated": false, @@ -27235,7 +27614,7 @@ }, "x-appwrite": { "method": "getUsage", - "weight": 422, + "weight": 427, "cookies": false, "type": "", "deprecated": false, @@ -27314,7 +27693,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 416, + "weight": 421, "cookies": false, "type": "", "deprecated": false, @@ -27372,7 +27751,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 414, + "weight": 419, "cookies": false, "type": "", "deprecated": false, @@ -27463,7 +27842,7 @@ }, "x-appwrite": { "method": "getVariable", - "weight": 415, + "weight": 420, "cookies": false, "type": "", "deprecated": false, @@ -27529,7 +27908,7 @@ }, "x-appwrite": { "method": "updateVariable", - "weight": 417, + "weight": 422, "cookies": false, "type": "", "deprecated": false, @@ -27620,7 +27999,7 @@ }, "x-appwrite": { "method": "deleteVariable", - "weight": 418, + "weight": 423, "cookies": false, "type": "", "deprecated": false, @@ -34894,6 +35273,31 @@ "keys" ] }, + "devKeyList": { + "description": "Dev Keys List", + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Total number of devKeys documents that matched your query.", + "x-example": 5, + "format": "int32" + }, + "devKeys": { + "type": "array", + "description": "List of devKeys.", + "items": { + "type": "object", + "$ref": "#\/definitions\/devKey" + }, + "x-example": "" + } + }, + "required": [ + "total", + "devKeys" + ] + }, "platformList": { "description": "Platforms List", "type": "object", @@ -39201,6 +39605,15 @@ }, "x-example": {} }, + "devKeys": { + "type": "array", + "description": "List of dev keys.", + "items": { + "type": "object", + "$ref": "#\/definitions\/devKey" + }, + "x-example": {} + }, "smtpEnabled": { "type": "boolean", "description": "Status for custom SMTP", @@ -39384,6 +39797,7 @@ "platforms", "webhooks", "keys", + "devKeys", "smtpEnabled", "smtpSenderName", "smtpSenderEmail", @@ -39574,6 +39988,65 @@ "sdks" ] }, + "devKey": { + "description": "DevKey", + "type": "object", + "properties": { + "$id": { + "type": "string", + "description": "Key ID.", + "x-example": "5e5ea5c16897e" + }, + "$createdAt": { + "type": "string", + "description": "Key creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "$updatedAt": { + "type": "string", + "description": "Key update date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "name": { + "type": "string", + "description": "Key name.", + "x-example": "Dev API Key" + }, + "expire": { + "type": "string", + "description": "Key expiration date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "secret": { + "type": "string", + "description": "Secret key.", + "x-example": "919c2d18fb5d4...a2ae413da83346ad2" + }, + "accessedAt": { + "type": "string", + "description": "Most recent access date in ISO 8601 format. This attribute is only updated again after 24 hours.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "sdks": { + "type": "array", + "description": "List of SDK user agents that used this key.", + "items": { + "type": "string" + }, + "x-example": "appwrite:flutter" + } + }, + "required": [ + "$id", + "$createdAt", + "$updatedAt", + "name", + "expire", + "secret", + "accessedAt", + "sdks" + ] + }, "mockNumber": { "description": "Mock Number", "type": "object", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 41661b20ac..d1940fbcb0 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -8282,7 +8282,7 @@ }, "x-appwrite": { "method": "list", - "weight": 368, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -8354,7 +8354,7 @@ }, "x-appwrite": { "method": "create", - "weight": 365, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -8605,7 +8605,7 @@ }, "x-appwrite": { "method": "listRuntimes", - "weight": 370, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -8656,7 +8656,7 @@ }, "x-appwrite": { "method": "listSpecifications", - "weight": 371, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -8708,7 +8708,7 @@ }, "x-appwrite": { "method": "get", - "weight": 366, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -8767,7 +8767,7 @@ }, "x-appwrite": { "method": "update", - "weight": 367, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -9012,7 +9012,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 369, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -9073,7 +9073,7 @@ }, "x-appwrite": { "method": "updateFunctionDeployment", - "weight": 374, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -9152,7 +9152,7 @@ }, "x-appwrite": { "method": "listDeployments", - "weight": 375, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -9232,7 +9232,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 372, + "weight": 377, "cookies": false, "type": "upload", "deprecated": false, @@ -9324,7 +9324,7 @@ }, "x-appwrite": { "method": "createDuplicateDeployment", - "weight": 380, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -9409,7 +9409,7 @@ }, "x-appwrite": { "method": "createTemplateDeployment", - "weight": 377, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -9515,7 +9515,7 @@ }, "x-appwrite": { "method": "createVcsDeployment", - "weight": 378, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -9613,7 +9613,7 @@ }, "x-appwrite": { "method": "getDeployment", - "weight": 373, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -9675,7 +9675,7 @@ }, "x-appwrite": { "method": "deleteDeployment", - "weight": 376, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -9744,7 +9744,7 @@ }, "x-appwrite": { "method": "getDeploymentDownload", - "weight": 379, + "weight": 384, "cookies": false, "type": "location", "deprecated": false, @@ -9830,7 +9830,7 @@ }, "x-appwrite": { "method": "updateDeploymentStatus", - "weight": 381, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -9899,7 +9899,7 @@ }, "x-appwrite": { "method": "listExecutions", - "weight": 384, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -9974,7 +9974,7 @@ }, "x-appwrite": { "method": "createExecution", - "weight": 382, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -10094,7 +10094,7 @@ }, "x-appwrite": { "method": "getExecution", - "weight": 383, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -10160,7 +10160,7 @@ }, "x-appwrite": { "method": "deleteExecution", - "weight": 385, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -10229,7 +10229,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 390, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -10288,7 +10288,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 388, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -10380,7 +10380,7 @@ }, "x-appwrite": { "method": "getVariable", - "weight": 389, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -10447,7 +10447,7 @@ }, "x-appwrite": { "method": "updateVariable", - "weight": 391, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -10539,7 +10539,7 @@ }, "x-appwrite": { "method": "deleteVariable", - "weight": 392, + "weight": 397, "cookies": false, "type": "", "deprecated": false, @@ -17154,7 +17154,7 @@ }, "x-appwrite": { "method": "list", - "weight": 397, + "weight": 402, "cookies": false, "type": "", "deprecated": false, @@ -17226,7 +17226,7 @@ }, "x-appwrite": { "method": "create", - "weight": 395, + "weight": 400, "cookies": false, "type": "", "deprecated": false, @@ -17492,7 +17492,7 @@ }, "x-appwrite": { "method": "listFrameworks", - "weight": 400, + "weight": 405, "cookies": false, "type": "", "deprecated": false, @@ -17543,7 +17543,7 @@ }, "x-appwrite": { "method": "listSpecifications", - "weight": 423, + "weight": 428, "cookies": false, "type": "", "deprecated": false, @@ -17595,7 +17595,7 @@ }, "x-appwrite": { "method": "get", - "weight": 396, + "weight": 401, "cookies": false, "type": "", "deprecated": false, @@ -17654,7 +17654,7 @@ }, "x-appwrite": { "method": "update", - "weight": 398, + "weight": 403, "cookies": false, "type": "", "deprecated": false, @@ -17913,7 +17913,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 399, + "weight": 404, "cookies": false, "type": "", "deprecated": false, @@ -17974,7 +17974,7 @@ }, "x-appwrite": { "method": "updateSiteDeployment", - "weight": 406, + "weight": 411, "cookies": false, "type": "", "deprecated": false, @@ -18053,7 +18053,7 @@ }, "x-appwrite": { "method": "listDeployments", - "weight": 405, + "weight": 410, "cookies": false, "type": "", "deprecated": false, @@ -18133,7 +18133,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 401, + "weight": 406, "cookies": false, "type": "upload", "deprecated": false, @@ -18233,7 +18233,7 @@ }, "x-appwrite": { "method": "createDuplicateDeployment", - "weight": 409, + "weight": 414, "cookies": false, "type": "", "deprecated": false, @@ -18312,7 +18312,7 @@ }, "x-appwrite": { "method": "createTemplateDeployment", - "weight": 402, + "weight": 407, "cookies": false, "type": "", "deprecated": false, @@ -18418,7 +18418,7 @@ }, "x-appwrite": { "method": "createVcsDeployment", - "weight": 403, + "weight": 408, "cookies": false, "type": "", "deprecated": false, @@ -18517,7 +18517,7 @@ }, "x-appwrite": { "method": "getDeployment", - "weight": 404, + "weight": 409, "cookies": false, "type": "", "deprecated": false, @@ -18579,7 +18579,7 @@ }, "x-appwrite": { "method": "deleteDeployment", - "weight": 407, + "weight": 412, "cookies": false, "type": "", "deprecated": false, @@ -18648,7 +18648,7 @@ }, "x-appwrite": { "method": "getDeploymentDownload", - "weight": 408, + "weight": 413, "cookies": false, "type": "location", "deprecated": false, @@ -18734,7 +18734,7 @@ }, "x-appwrite": { "method": "updateDeploymentStatus", - "weight": 410, + "weight": 415, "cookies": false, "type": "", "deprecated": false, @@ -18803,7 +18803,7 @@ }, "x-appwrite": { "method": "listLogs", - "weight": 412, + "weight": 417, "cookies": false, "type": "", "deprecated": false, @@ -18876,7 +18876,7 @@ }, "x-appwrite": { "method": "getLog", - "weight": 411, + "weight": 416, "cookies": false, "type": "", "deprecated": false, @@ -18940,7 +18940,7 @@ }, "x-appwrite": { "method": "deleteLog", - "weight": 413, + "weight": 418, "cookies": false, "type": "", "deprecated": false, @@ -19009,7 +19009,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 416, + "weight": 421, "cookies": false, "type": "", "deprecated": false, @@ -19068,7 +19068,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 414, + "weight": 419, "cookies": false, "type": "", "deprecated": false, @@ -19160,7 +19160,7 @@ }, "x-appwrite": { "method": "getVariable", - "weight": 415, + "weight": 420, "cookies": false, "type": "", "deprecated": false, @@ -19227,7 +19227,7 @@ }, "x-appwrite": { "method": "updateVariable", - "weight": 417, + "weight": 422, "cookies": false, "type": "", "deprecated": false, @@ -19319,7 +19319,7 @@ }, "x-appwrite": { "method": "deleteVariable", - "weight": 418, + "weight": 423, "cookies": false, "type": "", "deprecated": false, diff --git a/app/controllers/general.php b/app/controllers/general.php index 511b4d5035..dbbe592cea 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -13,8 +13,6 @@ use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Network\Validator\Origin; use Appwrite\Platform\Appwrite; -use Appwrite\SDK\AuthType; -use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Transformation\Adapter\Preview; @@ -1565,9 +1563,3 @@ $platform->init(Service::TYPE_HTTP); if (!empty(Method::getErrors())) { throw new \Exception('Errors found during SDK initialization:' . PHP_EOL . implode(PHP_EOL, Method::getErrors())); } - - -// Modules - -$platform = new Appwrite(); -$platform->init(Service::TYPE_HTTP); diff --git a/src/Appwrite/Platform/Appwrite.php b/src/Appwrite/Platform/Appwrite.php index 3bf54eaf30..9968dd22b5 100644 --- a/src/Appwrite/Platform/Appwrite.php +++ b/src/Appwrite/Platform/Appwrite.php @@ -4,8 +4,8 @@ namespace Appwrite\Platform; use Appwrite\Platform\Modules\Console; use Appwrite\Platform\Modules\Core; -use Appwrite\Platform\Modules\Projects; use Appwrite\Platform\Modules\Functions; +use Appwrite\Platform\Modules\Projects; use Appwrite\Platform\Modules\Proxy; use Appwrite\Platform\Modules\Sites; use Utopia\Platform\Platform; diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index a39bc1b86b..b3ecd0ce31 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -37,9 +37,9 @@ use Appwrite\Utopia\Response\Model\Country; use Appwrite\Utopia\Response\Model\Currency; use Appwrite\Utopia\Response\Model\Database; use Appwrite\Utopia\Response\Model\Deployment; -use Appwrite\Utopia\Response\Model\DevKey; use Appwrite\Utopia\Response\Model\DetectionFramework; use Appwrite\Utopia\Response\Model\DetectionRuntime; +use Appwrite\Utopia\Response\Model\DevKey; use Appwrite\Utopia\Response\Model\Document as ModelDocument; use Appwrite\Utopia\Response\Model\Error; use Appwrite\Utopia\Response\Model\ErrorDev; From f8343e01cdb9f2a883959d5e80496db888dc8093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 17 Apr 2025 16:13:09 +0200 Subject: [PATCH 108/110] PR review suggestions --- app/controllers/general.php | 2 +- app/controllers/shared/api.php | 8 ++++---- app/init/resources.php | 2 +- .../Platform/Modules/Projects/Http/DevKeys/Get.php | 4 ---- src/Appwrite/Platform/Tasks/ScheduleBase.php | 2 +- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index dbbe592cea..88fa94c097 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -106,7 +106,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw ); if (!$project->isEmpty() && $project->getId() !== 'console') { - $accessedAt = $project->getAttribute('accessedAt', ''); + $accessedAt = $project->getAttribute('accessedAt', 0); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) { $project->setAttribute('accessedAt', DateTime::now()); Authorization::skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project)); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 3897d78a9a..b24d452b9b 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -250,7 +250,7 @@ App::init() ); if ($dbKey) { - $accessedAt = $dbKey->getAttribute('accessedAt', ''); + $accessedAt = $dbKey->getAttribute('accessedAt', 0); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) { $dbKey->setAttribute('accessedAt', DateTime::now()); @@ -311,7 +311,7 @@ App::init() // Update project last activity if (!$project->isEmpty() && $project->getId() !== 'console') { - $accessedAt = $project->getAttribute('accessedAt', ''); + $accessedAt = $project->getAttribute('accessedAt', 0); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) { $project->setAttribute('accessedAt', DateTime::now()); Authorization::skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project)); @@ -320,7 +320,7 @@ App::init() // Update user last activity if (!empty($user->getId())) { - $accessedAt = $user->getAttribute('accessedAt', ''); + $accessedAt = $user->getAttribute('accessedAt', 0); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_USER_ACCESS)) > $accessedAt) { $user->setAttribute('accessedAt', DateTime::now()); @@ -800,7 +800,7 @@ App::shutdown() $key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER); $signature = md5($data['payload']); $cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key)); - $accessedAt = $cacheLog->getAttribute('accessedAt', ''); + $accessedAt = $cacheLog->getAttribute('accessedAt', 0); $now = DateTime::now(); if ($cacheLog->isEmpty()) { Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([ diff --git a/app/init/resources.php b/app/init/resources.php index 25659ed545..da013cd2ce 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -807,7 +807,7 @@ App::setResource('devKey', function (Request $request, Document $project, array } // update access time - $accessedAt = $key->getAttribute('accessedAt', ''); + $accessedAt = $key->getAttribute('accessedAt', 0); if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) { $key->setAttribute('accessedAt', DatabaseDateTime::now()); Authorization::skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key)); diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php index ebc0c1bcbb..c933c5be93 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php @@ -66,10 +66,6 @@ class Get extends Action throw new Exception(Exception::KEY_NOT_FOUND); } - if ($key === false || $key->isEmpty()) { - throw new Exception(Exception::KEY_NOT_FOUND); - } - $response->dynamic($key, Response::MODEL_DEV_KEY); } } diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index 303e03cb8c..88f98fa76a 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -43,7 +43,7 @@ abstract class ScheduleBase extends Action protected function updateProjectAccess(Document $project, Database $dbForPlatform): void { if (!$project->isEmpty() && $project->getId() !== 'console') { - $accessedAt = $project->getAttribute('accessedAt', ''); + $accessedAt = $project->getAttribute('accessedAt', 0); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) { $project->setAttribute('accessedAt', DateTime::now()); Authorization::skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project)); From 1a33b75d4217ba258c23de28f10617d9a524f172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 22 Apr 2025 10:48:25 +0200 Subject: [PATCH 109/110] Fix html error page test --- tests/e2e/Services/Projects/ProjectsConsoleClientTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 4adf4b6847..c4a0975f06 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -4560,7 +4560,7 @@ class ProjectsConsoleClientTest extends Scope 'failure' => 'https://example.com' ]); $this->assertEquals(400, $response['headers']['status-code']); - $this->assertEquals('Invalid `success` param: URL host must be one of: localhost, appwrite.io, *.appwrite.io', $response['body']['message']); + $this->assertStringContainsString('Invalid `success` param: URL host must be one of: localhost, appwrite.io, *.appwrite.io', $response['body']); /** Test oauth2 with devKey and now get oauth2 is disabled */ $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, [ From cb7aad9885c63e72aac3e0eab0a2304055bd5a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 22 Apr 2025 11:08:19 +0200 Subject: [PATCH 110/110] Improve code rabbit --- .coderabbit.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 0b8534b7c9..28656d3229 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -1,4 +1,9 @@ reviews: + path_filters: + - "!app/config/specs/**" + - "!docs/examples/**" + - "!docs/references/**" + - "!docs/sdks/**" auto_review: base_branches: - main