Merge remote-tracking branch 'origin/0.13.x' into feat-permissions-improvements

This commit is contained in:
Torsten Dittmann
2022-02-22 15:16:17 +01:00
1805 changed files with 39450 additions and 2935 deletions
+1 -1
View File
@@ -47,7 +47,7 @@ class Detector
*/
public function getClient(): array
{
if (strpos($this->userAgent, 'Appwrite CLI') !== false) {
if (strpos($this->userAgent, 'AppwriteCLI') !== false) {
$version = explode(' ', $this->userAgent)[0];
$version = explode('/', $version)[1];
$client = [
+189
View File
@@ -0,0 +1,189 @@
<?php
namespace Appwrite\Extend;
class Exception extends \Exception
{
/**
* Error Codes
*
* Naming the error types based on the following convention
* <ENTITY>_<ERROR_TYPE>
*
* Appwrite has the follwing entities:
* - General
* - Users
* - Teams
* - Memberships
* - Avatars
* - Storage
* - Functions
* - Deployments
* - Executions
* - Collections
* - Documents
* - Attributes
* - Indexes
* - Projects
* - Webhooks
* - Keys
* - Platform
* - Domain
*/
/** General */
const GENERAL_UNKNOWN = 'general_unknown';
const GENERAL_MOCK = 'general_mock';
const GENERAL_ACCESS_FORBIDDEN = 'general_access_forbidden';
const GENERAL_UNKNOWN_ORIGIN = 'general_unknown_origin';
const GENERAL_SERVICE_DISABLED = 'general_service_disabled';
const GENERAL_UNAUTHORIZED_SCOPE = 'general_unauthorized_scope';
const GENERAL_RATE_LIMIT_EXCEEDED = 'general_rate_limit_exceeded';
const GENERAL_SMTP_DISABLED = 'general_smtp_disabled';
const GENERAL_ARGUMENT_INVALID = 'general_argument_invalid';
const GENERAL_QUERY_LIMIT_EXCEEDED = 'general_query_limit_exceeded';
const GENERAL_QUERY_INVALID = 'general_query_invalid';
const GENERAL_ROUTE_NOT_FOUND = 'general_route_not_found';
const GENERAL_CURSOR_NOT_FOUND = 'general_cursor_not_found';
const GENERAL_SERVER_ERROR = 'general_server_error';
/** Users */
const USER_COUNT_EXCEEDED = 'user_count_exceeded';
const USER_JWT_INVALID = 'user_jwt_invalid';
const USER_ALREADY_EXISTS = 'user_already_exists';
const USER_BLOCKED = 'user_blocked';
const USER_INVALID_TOKEN = 'user_invalid_token';
const USER_PASSWORD_RESET_REQUIRED = 'user_password_reset_required';
const USER_EMAIL_NOT_WHITELISTED = 'user_email_not_whitelisted';
const USER_IP_NOT_WHITELISTED = 'user_ip_not_whitelisted';
const USER_INVALID_CREDENTIALS = 'user_invalid_credentials';
const USER_ANONYMOUS_CONSOLE_PROHIBITED = 'user_anonymous_console_prohibited';
const USER_SESSION_ALREADY_EXISTS = 'user_session_already_exists';
const USER_NOT_FOUND = 'user_not_found';
const USER_EMAIL_ALREADY_EXISTS = 'user_email_already_exists';
const USER_PASSWORD_MISMATCH = 'user_password_mismatch';
const USER_SESSION_NOT_FOUND = 'user_session_not_found';
const USER_UNAUTHORIZED = 'user_unauthorized';
const USER_AUTH_METHOD_UNSUPPORTED = 'user_auth_method_unsupported';
/** Teams */
const TEAM_NOT_FOUND = 'team_not_found';
const TEAM_INVITE_ALREADY_EXISTS = 'team_invite_already_exists';
const TEAM_INVITE_NOT_FOUND = 'team_invite_not_found';
const TEAM_INVALID_SECRET = 'team_invalid_secret';
const TEAM_MEMBERSHIP_MISMATCH = 'team_membership_mismatch';
const TEAM_INVITE_MISMATCH = 'team_invite_mismatch';
/** Membership */
const MEMBERSHIP_NOT_FOUND = 'membership_not_found';
/** Avatars */
const AVATAR_SET_NOT_FOUND = 'avatar_set_not_found';
const AVATAR_NOT_FOUND = 'avatar_not_found';
const AVATAR_IMAGE_NOT_FOUND = 'avatar_image_not_found';
const AVATAR_REMOTE_URL_FAILED = 'avatar_remote_url_failed';
const AVATAR_ICON_NOT_FOUND = 'avatar_icon_not_found';
/** Storage */
const STORAGE_FILE_NOT_FOUND = 'storage_file_not_found';
const STORAGE_DEVICE_NOT_FOUND = 'storage_device_not_found';
const STORAGE_FILE_EMPTY = 'storage_file_empty';
const STORAGE_FILE_TYPE_UNSUPPORTED = 'storage_file_type_unsupported';
const STORAGE_INVALID_FILE_SIZE = 'storage_invalid_file_size';
const STORAGE_INVALID_FILE = 'storage_invalid_file';
const STORAGE_BUCKET_ALREADY_EXISTS = 'storage_bucket_already_exists';
const STORAGE_BUCKET_NOT_FOUND = 'storage_bucket_not_found';
const STORAGE_INVALID_CONTENT_RANGE = 'storage_invalid_content_range';
const STORAGE_INVALID_RANGE = 'storage_invalid_range';
/** Functions */
const FUNCTION_NOT_FOUND = 'function_not_found';
/** Deployments */
const DEPLOYMENT_NOT_FOUND = 'deployment_not_found';
/** Execution */
const EXECUTION_NOT_FOUND = 'execution_not_found';
/** Collections */
const COLLECTION_NOT_FOUND = 'collection_not_found';
const COLLECTION_ALREADY_EXISTS = 'collection_already_exists';
const COLLECTION_LIMIT_EXCEEDED = 'collection_limit_exceeded';
/** Documents */
const DOCUMENT_NOT_FOUND = 'document_not_found';
const DOCUMENT_INVALID_STRUCTURE = 'document_invalid_structure';
const DOCUMENT_MISSING_PAYLOAD = 'document_missing_payload';
const DOCUMENT_ALREADY_EXISTS = 'document_already_exists';
/** Attribute */
const ATTRIBUTE_NOT_FOUND = 'attribute_not_found';
const ATTRIBUTE_UNKNOWN = 'attribute_unknown';
const ATTRIBUTE_NOT_AVAILABLE = 'attribute_not_available';
const ATTRIBUTE_FORMAT_UNSUPPORTED = 'attribute_format_unsupported';
const ATTRIBUTE_DEFAULT_UNSUPPORTED = 'attribute_default_unsupported';
const ATTRIBUTE_ALREADY_EXISTS = 'attribute_already_exists';
const ATTRIBUTE_LIMIT_EXCEEDED = 'attribute_limit_exceeded';
const ATTRIBUTE_VALUE_INVALID = 'attribute_value_invalid';
/** Indexes */
const INDEX_NOT_FOUND = 'index_not_found';
const INDEX_LIMIT_EXCEEDED = 'index_limit_exceeded';
const INDEX_ALREADY_EXISTS = 'index_already_exists';
/** Projects */
const PROJECT_NOT_FOUND = 'project_not_found';
const PROJECT_UNKNOWN = 'project_unknown';
const PROJECT_PROVIDER_DISABLED = 'project_provider_disabled';
const PROJECT_PROVIDER_UNSUPPORTED = 'project_provider_unsupported';
const PROJECT_INVALID_SUCCESS_URL = 'project_invalid_success_url';
const PROJECT_INVALID_FAILURE_URL = 'project_invalid_failure_url';
const PROJECT_MISSING_USER_ID = 'project_missing_user_id';
/** Webhooks */
const WEBHOOK_NOT_FOUND = 'webhook_not_found';
/** Keys */
const KEY_NOT_FOUND = 'key_not_found';
/** Platform */
const PLATFORM_NOT_FOUND = 'platform_not_found';
/** Domain */
const DOMAIN_NOT_FOUND = 'domain_not_found';
const DOMAIN_ALREADY_EXISTS = 'domain_already_exists';
const DOMAIN_VERIFICATION_FAILED = 'domain_verification_failed';
private $type = '';
public function __construct(string $message, int $code = 0, string $type = Exception::GENERAL_UNKNOWN, \Throwable $previous = null)
{
$this->type = $type;
parent::__construct($message, $code, $previous);
}
/**
* Get the type of the exception.
*
* @return string
*/
public function getType(): string
{
return $this->type;
}
/**
* Set the type of the exception.
*
* @param string $type
*
* @return void
*/
public function setType(string $type): void
{
$this->type = $type;
}
}
+8 -4
View File
@@ -240,7 +240,7 @@ class Realtime extends Adapter
* @param Document|null $project
* @return array
*/
public static function fromPayload(string $event, Document $payload, Document $project = null, Document $collection = null): array
public static function fromPayload(string $event, Document $payload, Document $project = null, Document $collection = null, Document $bucket = null): array
{
$channels = [];
$roles = [];
@@ -285,7 +285,7 @@ class Realtime extends Adapter
break;
case strpos($event, 'database.documents.') === 0:
if ($collection->isEmpty()) {
throw new \Exception('Collection need to be passed to to Realtime for Document events in the Database.');
throw new \Exception('Collection needs to be passed to Realtime for Document events in the Database.');
}
$channels[] = 'documents';
@@ -295,9 +295,13 @@ class Realtime extends Adapter
$roles = ($collection->getAttribute('permission') === 'collection') ? $collection->getRead() : $payload->getRead();
break;
case strpos($event, 'storage.') === 0:
case strpos($event, 'storage.files') === 0:
if($bucket->isEmpty()) {
throw new \Exception('Bucket needs to be pased to Realtime for File events in the Storage.');
}
$channels[] = 'files';
$channels[] = 'files.' . $payload->getId();
$channels[] = 'buckets.' . $payload->getAttribute('bucketId') . '.files';
$channels[] = 'buckets.' . $payload->getAttribute('bucketId') . '.files.' . $payload->getId();
$roles = $payload->getRead();
break;
+1 -1
View File
@@ -7,7 +7,7 @@ use Utopia\Database\Document;
use Utopia\Database\Database;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Exception;
use Exception;
abstract class Migration
{
+11 -1
View File
@@ -275,7 +275,13 @@ class OpenAPI3 extends Format
$node['schema']['x-example'] = false;
break;
case 'Utopia\Database\Validator\UID':
$node['schema']['type'] = $validator->getType();
$node['schema']['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
break;
case 'Appwrite\Utopia\Database\Validator\CustomId':
if($route->getLabel('sdk.methodType', '') === 'upload') {
$node['schema']['x-upload-id'] = true;
}
$node['schema']['type'] = $validator->getType();
$node['schema']['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
break;
@@ -372,9 +378,13 @@ class OpenAPI3 extends Format
$body['content'][$consumes[0]]['schema']['properties'][$name] = [
'type' => $node['schema']['type'],
'description' => $node['description'],
'x-example' => $node['x-example'] ?? null,
'x-example' => $node['schema']['x-example'] ?? null
];
if($node['schema']['x-upload-id'] ?? false) {
$body['content'][$consumes[0]]['schema']['properties'][$name]['x-upload-id'] = $node['schema']['x-upload-id'];
}
if(isset($node['default'])) {
$body['content'][$consumes[0]]['schema']['properties'][$name]['default'] = $node['default'];
}
@@ -262,8 +262,14 @@ class Swagger2 extends Format
$node['type'] = $validator->getType();
$node['x-example'] = false;
break;
case 'Utopia\Database\Validator\UID':
case 'Appwrite\Utopia\Database\Validator\CustomId':
if($route->getLabel('sdk.methodType', '') === 'upload') {
$node['x-upload-id'] = true;
}
$node['type'] = $validator->getType();
$node['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
break;
case 'Utopia\Database\Validator\UID':
$node['type'] = $validator->getType();
$node['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
break;
+4
View File
@@ -132,6 +132,10 @@ class Stats
}
$storageMertics = [
'storage.buckets.create',
'storage.buckets.read',
'storage.buckets.update',
'storage.buckets.delete',
'storage.files.create',
'storage.files.read',
'storage.files.update',
+5 -1
View File
@@ -32,6 +32,7 @@ use Appwrite\Utopia\Response\Model\Error;
use Appwrite\Utopia\Response\Model\ErrorDev;
use Appwrite\Utopia\Response\Model\Execution;
use Appwrite\Utopia\Response\Model\File;
use Appwrite\Utopia\Response\Model\Bucket;
use Appwrite\Utopia\Response\Model\Func;
use Appwrite\Utopia\Response\Model\Index;
use Appwrite\Utopia\Response\Model\JWT;
@@ -123,7 +124,8 @@ class Response extends SwooleResponse
// Storage
const MODEL_FILE = 'file';
const MODEL_FILE_LIST = 'fileList';
const MODEL_BUCKET = 'bucket'; // - Missing
const MODEL_BUCKET = 'bucket';
const MODEL_BUCKET_LIST = 'bucketList';
// Locale
const MODEL_LOCALE = 'locale';
@@ -213,6 +215,7 @@ class Response extends SwooleResponse
->setModel(new BaseList('Sessions List', self::MODEL_SESSION_LIST, 'sessions', self::MODEL_SESSION))
->setModel(new BaseList('Logs List', self::MODEL_LOG_LIST, 'logs', self::MODEL_LOG))
->setModel(new BaseList('Files List', self::MODEL_FILE_LIST, 'files', self::MODEL_FILE))
->setModel(new BaseList('Buckets List', self::MODEL_BUCKET_LIST, 'buckets', self::MODEL_BUCKET))
->setModel(new BaseList('Teams List', self::MODEL_TEAM_LIST, 'teams', self::MODEL_TEAM))
->setModel(new BaseList('Memberships List', self::MODEL_MEMBERSHIP_LIST, 'memberships', self::MODEL_MEMBERSHIP))
->setModel(new BaseList('Functions List', self::MODEL_FUNCTION_LIST, 'functions', self::MODEL_FUNCTION))
@@ -252,6 +255,7 @@ class Response extends SwooleResponse
->setModel(new JWT())
->setModel(new Locale())
->setModel(new File())
->setModel(new Bucket())
->setModel(new Team())
->setModel(new Membership())
->setModel(new Func())
@@ -0,0 +1,111 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Bucket extends Model
{
public function __construct()
{
$this
->addRule('$id', [
'type' => self::TYPE_STRING,
'description' => 'Bucket ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('$read', [
'type' => self::TYPE_STRING,
'description' => 'File read permissions.',
'default' => [],
'example' => ['role:all'],
'array' => true,
])
->addRule('$write', [
'type' => self::TYPE_STRING,
'description' => 'File write permissions.',
'default' => [],
'example' => ['user:608f9da25e7e1'],
'array' => true,
])
->addRule('permission', [
'type' => self::TYPE_STRING,
'description' => 'Bucket permission model. Possible values: `bucket` or `file`',
'default' => '',
'example' => 'file',
])
->addRule('dateCreated', [
'type' => self::TYPE_INTEGER,
'description' => 'Bucket creation date in Unix timestamp.',
'default' => 0,
'example' => 1592981250,
])
->addRule('dateUpdated', [
'type' => self::TYPE_INTEGER,
'description' => 'Bucket update date in Unix timestamp.',
'default' => 0,
'example' => 1592981250,
])
->addRule('name', [
'type' => self::TYPE_STRING,
'description' => 'Bucket name.',
'default' => '',
'example' => 'Documents',
])
->addRule('enabled', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Bucket enabled.',
'default' => true,
'example' => false,
])
->addRule('maximumFileSize', [
'type' => self::TYPE_INTEGER,
'description' => 'Maximum file size supported.',
'default' => 0,
'example' => 100,
])
->addRule('allowedFileExtensions', [
'type' => self::TYPE_STRING,
'description' => 'Allowed file extensions.',
'default' => [],
'example' => ['jpg', 'png'],
'array' => true
])
->addRule('encryption', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Bucket is encrypted.',
'default' => true,
'example' => false,
])
->addRule('antivirus', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Virus scanning is enabled.',
'default' => true,
'example' => false,
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'Bucket';
}
/**
* Get Collection
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_BUCKET;
}
}
@@ -22,6 +22,12 @@ class Error extends Model
'default' => '',
'example' => '404',
])
->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Error type. You can learn more about all the error types at https://appwrite.io/docs/error-codes#errorTypes',
'default' => 'unknown',
'example' => 'not_found',
])
->addRule('version', [
'type' => self::TYPE_STRING,
'description' => 'Server version number.',
@@ -16,6 +16,12 @@ class File extends Model
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('bucketId', [
'type' => self::TYPE_STRING,
'description' => 'Bucket ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('$read', [
'type' => self::TYPE_STRING,
'description' => 'File read permissions.',
@@ -60,6 +66,18 @@ class File extends Model
'default' => 0,
'example' => 17890,
])
->addRule('chunksTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Total number of chunks available',
'default' => 0,
'example' => 17890,
])
->addRule('chunksUploaded', [
'type' => self::TYPE_INTEGER,
'description' => 'Total number of chunks uploaded',
'default' => 0,
'example' => 17890,
])
;
}
@@ -23,6 +23,13 @@ class UsageBuckets extends Model
'example' => new \stdClass,
'array' => true
])
->addRule('filesStorage', [
'type' => Response::MODEL_METRIC_LIST,
'description' => 'Aggregated stats for total storage of files in this bucket.',
'default' => [],
'example' => new \stdClass,
'array' => true
])
->addRule('filesCreate', [
'type' => Response::MODEL_METRIC_LIST,
'description' => 'Aggregated stats for files created.',
@@ -16,20 +16,90 @@ class UsageStorage extends Model
'default' => '',
'example' => '30d',
])
->addRule('storage', [
->addRule('filesStorage', [
'type' => Response::MODEL_METRIC_LIST,
'description' => 'Aggregated stats for the occupied storage size (in bytes).',
'description' => 'Aggregated stats for the occupied storage size by files (in bytes).',
'default' => [],
'example' => new \stdClass,
'array' => true
])
->addRule('files', [
->addRule('tagsStorage', [
'type' => Response::MODEL_METRIC_LIST,
'description' => 'Aggregated stats for the occupied storage size by tags (in bytes).',
'default' => [],
'example' => new \stdClass,
'array' => true
])
->addRule('filesCount', [
'type' => Response::MODEL_METRIC_LIST,
'description' => 'Aggregated stats for total number of files.',
'default' => [],
'example' => new \stdClass,
'array' => true
])
->addRule('bucketsCount', [
'type' => Response::MODEL_METRIC_LIST,
'description' => 'Aggregated stats for total number of buckets.',
'default' => [],
'example' => new \stdClass,
'array' => true
])
->addRule('bucketsCreate', [
'type' => Response::MODEL_METRIC_LIST,
'description' => 'Aggregated stats for buckets created.',
'default' => [],
'example' => new \stdClass,
'array' => true
])
->addRule('bucketsRead', [
'type' => Response::MODEL_METRIC_LIST,
'description' => 'Aggregated stats for buckets read.',
'default' => [],
'example' => new \stdClass,
'array' => true
])
->addRule('bucketsUpdate', [
'type' => Response::MODEL_METRIC_LIST,
'description' => 'Aggregated stats for buckets updated.',
'default' => [],
'example' => new \stdClass,
'array' => true
])
->addRule('bucketsDelete', [
'type' => Response::MODEL_METRIC_LIST,
'description' => 'Aggregated stats for buckets deleted.',
'default' => [],
'example' => new \stdClass,
'array' => true
])
->addRule('filesCreate', [
'type' => Response::MODEL_METRIC_LIST,
'description' => 'Aggregated stats for files created.',
'default' => [],
'example' => new \stdClass,
'array' => true
])
->addRule('filesRead', [
'type' => Response::MODEL_METRIC_LIST,
'description' => 'Aggregated stats for files read.',
'default' => [],
'example' => new \stdClass,
'array' => true
])
->addRule('filesUpdate', [
'type' => Response::MODEL_METRIC_LIST,
'description' => 'Aggregated stats for files updated.',
'default' => [],
'example' => new \stdClass,
'array' => true
])
->addRule('filesDelete', [
'type' => Response::MODEL_METRIC_LIST,
'description' => 'Aggregated stats for files deleted.',
'default' => [],
'example' => new \stdClass,
'array' => true
])
;
}