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; }