mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
restructure endpoints
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Tokens\Http\Tokens\Buckets\Files;
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Platform\Action as UtopiaAction;
|
||||
|
||||
class Action extends UtopiaAction
|
||||
{
|
||||
protected function getFileAndBucket(Database $dbForProject, string $bucketId, string $fileId): array
|
||||
{
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization(Database::PERMISSION_READ);
|
||||
$valid = $validator->isValid($bucket->getRead());
|
||||
if (!$fileSecurity && !$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ($fileSecurity && !$valid) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
} else {
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
}
|
||||
|
||||
if ($file->isEmpty()) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
}
|
||||
return [
|
||||
'bucket' => $bucket,
|
||||
'file' => $file,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Tokens\Http\Tokens\Buckets\Files;
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
use Utopia\Database\Validator\Permissions;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class CreateFileToken extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'createFileToken';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/tokens/buckets/:bucketId/files/:fileId')
|
||||
->desc('Create file token')
|
||||
->groups(['api', 'token'])
|
||||
->label('scope', 'tokens.write')
|
||||
->label('audits.event', 'token.create')
|
||||
->label('event', 'tokens.[tokenId].create')
|
||||
->label('audits.resource', 'token/{response.$id}')
|
||||
->label('usage.metric', 'tokens.{scope}.requests.create')
|
||||
->label('usage.params', ['resourceId:{request.resourceId}', 'resourceType:{request.resourceType}'])
|
||||
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
|
||||
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT)
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'tokens')
|
||||
->label('sdk.method', 'createFileToken')
|
||||
->label('sdk.description', '/docs/references/tokens/create_file_token.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_RESOURCE_TOKEN)
|
||||
->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('permissions', [], new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permission strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('user')
|
||||
->inject('queueForEvents')
|
||||
->callback(fn ($bucketId, $fileId, $expire, $permissions, $response, $dbForProject, $user, $queueForEvents) => $this->action($bucketId, $fileId, $expire, $permissions, $response, $dbForProject, $user, $queueForEvents));
|
||||
}
|
||||
|
||||
public function action(string $bucketId, string $fileId, ?string $expire, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents)
|
||||
{
|
||||
|
||||
['bucket' => $bucket, 'file' => $file] = $this->getFileAndBucket($dbForProject, $bucketId, $fileId);
|
||||
|
||||
$token = $dbForProject->createDocument('resourceTokens', new Document([
|
||||
'$id' => ID::unique(),
|
||||
'secret' => Auth::tokenGenerator(128),
|
||||
'resourceId' => $bucketId . ':' . $fileId,
|
||||
'resourceInternalId' => $bucket->getInternalId() . ':' . $file->getInternalId(),
|
||||
'resourceType' => 'file',
|
||||
'expire' => $expire,
|
||||
'$permissions' => $permissions
|
||||
]));
|
||||
|
||||
$queueForEvents
|
||||
->setParam('bucketId', $bucket->getId())
|
||||
->setParam('fileId', $file->getId())
|
||||
->setParam('tokenId', $token->getId())
|
||||
->setContext('bucket', $bucket)
|
||||
;
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($token, Response::MODEL_RESOURCE_TOKEN);
|
||||
}
|
||||
}
|
||||
+13
-7
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Tokens\Http\Tokens;
|
||||
namespace Appwrite\Platform\Modules\Tokens\Http\Tokens\Buckets\Files;
|
||||
|
||||
use Appwrite\Extend\Exception as ExtendException;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\FileTokens;
|
||||
@@ -9,23 +9,23 @@ use Exception;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
|
||||
class ListTokens extends Action
|
||||
class ListFileTokens extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'listTokens';
|
||||
return 'listFileTokens';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/storage/buckets/:bucketId/files/:fileId/tokens')
|
||||
->setHttpPath('/v1/tokens/buckets/:bucketId/files/:fileId')
|
||||
->desc('List tokens')
|
||||
->groups(['api', 'tokens'])
|
||||
->label('scope', 'tokens.read')
|
||||
@@ -37,15 +37,21 @@ class ListTokens extends Action
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_RESOURCE_TOKEN_LIST)
|
||||
->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('queries', [], new FileTokens(), '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(', ', FileTokens::ALLOWED_ATTRIBUTES), true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->callback(fn ($queries, $response, $dbForProject) => $this->action($queries, $response, $dbForProject));
|
||||
->callback(fn ($bucketId, $fileId, $queries, $response, $dbForProject) => $this->action($bucketId, $fileId, $queries, $response, $dbForProject));
|
||||
}
|
||||
|
||||
public function action(array $queries, Response $response, Database $dbForProject)
|
||||
public function action(string $bucketId, string $fileId, array $queries, Response $response, Database $dbForProject)
|
||||
{
|
||||
['bucket' => $bucket, 'file' => $file] = $this->getFileAndBucket($dbForProject, $bucketId, $fileId);
|
||||
|
||||
$queries = Query::parseQueries($queries);
|
||||
$queries[] = Query::equal('resourceType', ["files"]);
|
||||
$queries[] = Query::equal('resourceId', [$bucket->getInternalId() . ':' . $file->getInternalId()]);
|
||||
// Get cursor document if there was a cursor query
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
@@ -1,123 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Tokens\Http\Tokens;
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
use Utopia\Database\Validator\Permissions;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Nullable;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
class CreateToken extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'createToken';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/tokens')
|
||||
->desc('Create token')
|
||||
->groups(['api', 'token'])
|
||||
->label('scope', 'tokens.write')
|
||||
->label('audits.event', 'token.create')
|
||||
->label('event', 'tokens.[tokenId].create')
|
||||
->label('audits.resource', 'token/{response.$id}')
|
||||
->label('usage.metric', 'tokens.{scope}.requests.create')
|
||||
->label('usage.params', ['resourceId:{request.resourceId}', 'resourceType:{request.resourceType}'])
|
||||
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
|
||||
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT)
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'tokens')
|
||||
->label('sdk.method', 'create')
|
||||
->label('sdk.description', '/docs/references/tokens/create.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_RESOURCE_TOKEN)
|
||||
->param('resourceType', '', new WhiteList(['files']), 'Resource type one of [files].')
|
||||
->param('resourceId', '', new UID(), 'Unique resource ID.')
|
||||
->param('expire', null, new Nullable(new DatetimeValidator()), 'Token expiry date', true)
|
||||
->param('permissions', [], new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permission strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('user')
|
||||
->inject('queueForEvents')
|
||||
->callback(fn ($resourceType, $resourceId, $expire, $permissions, $response, $dbForProject, $user, $queueForEvents) => $this->action($resourceType, $resourceId, $expire, $permissions, $response, $dbForProject, $user, $queueForEvents));
|
||||
}
|
||||
|
||||
public function action(string $resourceType, string $resourceId, ?string $expire, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents)
|
||||
{
|
||||
|
||||
if ($resourceType === 'files') {
|
||||
$ids = explode(':', $resourceId);
|
||||
if (count($ids) !== 2) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid resource id');
|
||||
}
|
||||
$bucketId = $ids[0];
|
||||
$fileId = $ids[1];
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization(Database::PERMISSION_READ);
|
||||
$valid = $validator->isValid($bucket->getRead());
|
||||
if (!$fileSecurity && !$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ($fileSecurity && !$valid) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
} else {
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
}
|
||||
|
||||
if ($file->isEmpty()) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$token = $dbForProject->createDocument('resourceTokens', new Document([
|
||||
'$id' => ID::unique(),
|
||||
'secret' => Auth::tokenGenerator(128),
|
||||
'resourceId' => $bucketId . ':' . $fileId,
|
||||
'resourceInternalId' => $bucket->getInternalId() . ':' . $file->getInternalId(),
|
||||
'resourceType' => 'file',
|
||||
'expire' => $expire,
|
||||
'$permissions' => $permissions
|
||||
]));
|
||||
|
||||
$queueForEvents
|
||||
->setParam('bucketId', $bucket->getId())
|
||||
->setParam('fileId', $file->getId())
|
||||
->setParam('tokenId', $token->getId())
|
||||
->setContext('bucket', $bucket)
|
||||
;
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($token, Response::MODEL_RESOURCE_TOKEN);
|
||||
} else {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid resource type');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,15 +24,15 @@ class GetTokenJWT extends Action
|
||||
public function __construct()
|
||||
{
|
||||
$this->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/storage/buckets/:bucketId/files/:fileId/tokens/:tokenId/jwt')
|
||||
->desc('Get file token jwt')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.read')
|
||||
->setHttpPath('/v1/tokens/:tokenId/jwt')
|
||||
->desc('Get token as JWT')
|
||||
->groups(['api', 'tokens'])
|
||||
->label('scope', 'tokens.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('usage.metric', 'fileTokens.{scope}.requests.read')
|
||||
->label('usage.params', ['bucketId:{request.bucketId}','fileId:{request.fileId}'])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getFileTokenJWT')
|
||||
->label('usage.metric', 'tokens.{scope}.requests.read')
|
||||
->label('usage.params', ['tokenId:{request.tokenId}'])
|
||||
->label('sdk.namespace', 'tokens')
|
||||
->label('sdk.method', 'getJWT')
|
||||
->label('sdk.description', '/docs/references/storage/get-file-token-jwt.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
|
||||
Reference in New Issue
Block a user