diff --git a/phpunit.xml b/phpunit.xml
index 4c4e55ea4e..a8578995c1 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -31,6 +31,7 @@
./tests/e2e/Services/Locale
./tests/e2e/Services/Projects
./tests/e2e/Services/Storage
+ ./tests/e2e/Services/Tokens
./tests/e2e/Services/Webhooks
./tests/e2e/Services/Messaging
./tests/e2e/Services/Migrations
diff --git a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php
index fe7a0187e9..e4de4c1380 100644
--- a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php
+++ b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php
@@ -61,7 +61,7 @@ class Create extends Action
))
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File unique ID.')
- ->param('expire', null, new Nullable(new DatetimeValidator()), 'Token expiry date', true)
+ ->param('expire', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Token expiry date', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -70,7 +70,6 @@ class Create extends Action
public function action(string $bucketId, string $fileId, ?string $expire, Response $response, Database $dbForProject, Event $queueForEvents): void
{
-
/**
* @var Document $bucket
* @var Document $file
diff --git a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Update.php b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Update.php
index 7a15708011..fef2c38a81 100644
--- a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Update.php
+++ b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Update.php
@@ -57,7 +57,7 @@ class Update extends Action
contentType: ContentType::JSON
))
->param('tokenId', '', new UID(), 'Token unique ID.')
- ->param('expire', null, new Nullable(new DatetimeValidator()), 'File token expiry date', true)
+ ->param('expire', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'File token expiry date', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
diff --git a/tests/e2e/Services/Tokens/TokensConsoleClientTest.php b/tests/e2e/Services/Tokens/TokensConsoleClientTest.php
index c0f94a55bf..f1480faba0 100644
--- a/tests/e2e/Services/Tokens/TokensConsoleClientTest.php
+++ b/tests/e2e/Services/Tokens/TokensConsoleClientTest.php
@@ -9,7 +9,6 @@ use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
-use Utopia\Database\DateTime;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
@@ -63,10 +62,23 @@ class TokensConsoleClientTest extends Scope
$fileId = $file['body']['$id'];
+ // Failure case: Expire date is in the past
$token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
- ], $this->getHeaders()));
+ ], $this->getHeaders()), [
+ 'expire' => '2022-11-02',
+ ]);
+ $this->assertEquals(400, $token['headers']['status-code']);
+ $this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']);
+
+ // Success case: No expire date
+ $token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id']
+ ], $this->getHeaders()), [
+ 'expire' => null,
+ ]);
$this->assertEquals(201, $token['headers']['status-code']);
$this->assertEquals('files', $token['body']['resourceType']);
@@ -107,8 +119,19 @@ class TokensConsoleClientTest extends Scope
{
$tokenId = $data['tokenId'];
+ // Failure case: Expire date is in the past
+ $token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, [
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey'],
+ ], [
+ 'expire' => '2022-11-02',
+ ]);
+ $this->assertEquals(400, $token['headers']['status-code']);
+ $this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']);
+
// Finite expiry
- $expiry = DateTime::addSeconds(new \DateTime(), 3600);
+ $expiry = date('Y-m-d', strtotime("tomorrow"));
$token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
diff --git a/tests/e2e/Services/Tokens/TokensCustomServerTest.php b/tests/e2e/Services/Tokens/TokensCustomServerTest.php
index fe8fa2bad9..779d5449b3 100644
--- a/tests/e2e/Services/Tokens/TokensCustomServerTest.php
+++ b/tests/e2e/Services/Tokens/TokensCustomServerTest.php
@@ -7,7 +7,6 @@ use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
-use Utopia\Database\DateTime;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
@@ -61,6 +60,17 @@ class TokensCustomServerTest extends Scope
$fileId = $file['body']['$id'];
+ // Failure case: Expire date is in the past
+ $token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id']
+ ], $this->getHeaders()), [
+ 'expire' => '2022-11-02',
+ ]);
+ $this->assertEquals(400, $token['headers']['status-code']);
+ $this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']);
+
+ // Success case: No expire date
$token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
@@ -83,8 +93,19 @@ class TokensCustomServerTest extends Scope
{
$tokenId = $data['tokenId'];
- // Finite expiry
- $expiry = DateTime::addSeconds(new \DateTime(), 3600);
+ // Failure case: Expire date is in the past
+ $token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, [
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey'],
+ ], [
+ 'expire' => '2022-11-02',
+ ]);
+ $this->assertEquals(400, $token['headers']['status-code']);
+ $this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']);
+
+ // Success case: Finite expiry
+ $expiry = date('Y-m-d', strtotime("tomorrow"));
$token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@@ -94,9 +115,10 @@ class TokensCustomServerTest extends Scope
]);
$dateValidator = new DatetimeValidator();
+ $this->assertEquals(200, $token['headers']['status-code']);
$this->assertTrue($dateValidator->isValid($token['body']['expire']));
- // Infinite expiry
+ // Success case: Infinite expiry
$token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],