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..0d905838a7 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
@@ -17,7 +17,7 @@ use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Scope\HTTP;
-use Utopia\Validator\Nullable;
+use Utopia\Validator\Text;
class Create extends Action
{
@@ -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 Text(100), 'Token expiry date', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -70,6 +70,13 @@ class Create extends Action
public function action(string $bucketId, string $fileId, ?string $expire, Response $response, Database $dbForProject, Event $queueForEvents): void
{
+ if (!is_null($expire)) {
+ $validator = new DatetimeValidator(requireDateInFuture: true, precision: DateTimeValidator::PRECISION_DAYS, offset: 0);
+ if (!$validator->isValid($expire)) {
+ throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Token expiry date must be a valid date, and at least 1 day from now');
+ }
+ }
+
/**
* @var Document $bucket
diff --git a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Update.php b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Update.php
index 7a15708011..b1b670532d 100644
--- a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Update.php
+++ b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Update.php
@@ -14,7 +14,7 @@ use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
-use Utopia\Validator\Nullable;
+use Utopia\Validator\Text;
class Update extends Action
{
@@ -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 Text(100), 'File token expiry date', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -66,6 +66,13 @@ class Update extends Action
public function action(string $tokenId, ?string $expire, Response $response, Database $dbForProject, Event $queueForEvents)
{
+ if (!is_null($expire)) {
+ $validator = new DatetimeValidator(requireDateInFuture: true, precision: DateTimeValidator::PRECISION_DAYS, offset: 0);
+ if (!$validator->isValid($expire)) {
+ throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Token expiry date must be a valid date, and at least 1 day from now');
+ }
+ }
+
$token = $dbForProject->getDocument('resourceTokens', $tokenId);
if ($token->isEmpty()) {
diff --git a/tests/e2e/Services/Tokens/TokensConsoleClientTest.php b/tests/e2e/Services/Tokens/TokensConsoleClientTest.php
index c0f94a55bf..ab3790abca 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->assertEquals('Token expiry date must be a valid date, and at least 1 day from now', $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->assertEquals('Token expiry date must be a valid date, and at least 1 day from now', $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..eaf30049f9 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->assertEquals('Token expiry date must be a valid date, and at least 1 day from now', $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->assertEquals('Token expiry date must be a valid date, and at least 1 day from now', $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'],