From f2f613d72105df7c8b26bdd6e71565bf1830c26d Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Fri, 25 Jun 2021 16:28:37 +0545 Subject: [PATCH 01/10] backward compatibility for get requests --- app/controllers/api/storage.php | 242 ++------------------------------ 1 file changed, 12 insertions(+), 230 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index f66621b768..5c80ba8da9 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -1031,17 +1031,12 @@ App::get('/v1/storage/files') ->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true) ->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true) ->inject('response') - ->inject('dbForInternal') - ->action(function ($search, $limit, $offset, $orderType, $response, $dbForInternal) { + ->action(function ($search, $limit, $offset, $orderType, $response) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, $search)] : []; - - $response->dynamic2(new Document([ - 'files' => $dbForInternal->find('files', $queries, $limit, $offset, ['_id'], [$orderType]), - 'sum' => $dbForInternal->count('files', $queries, APP_LIMIT_COUNT), - ]), Response::MODEL_FILE_LIST); + $response->redirect('/v1/storage/buckets/default/files?=' + .\http_build_query(['search' => $search, 'limit' => $limit, 'offset' => $offset, 'orderType' => $orderType])); }); App::get('/v1/storage/files/:fileId') @@ -1057,18 +1052,11 @@ App::get('/v1/storage/files/:fileId') ->label('sdk.response.model', Response::MODEL_FILE) ->param('fileId', '', new UID(), 'File unique ID.') ->inject('response') - ->inject('dbForInternal') - ->action(function ($fileId, $response, $dbForInternal) { + ->action(function ($fileId, $response) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $file = $dbForInternal->getDocument('files', $fileId); - - if (empty($file->getId())) { - throw new Exception('File not found', 404); - } - - $response->dynamic2($file, Response::MODEL_FILE); + $response->redirect('/v1/storage/buckets/default/files/'.$fileId); }); App::get('/v1/storage/files/:fileId/preview') @@ -1094,135 +1082,15 @@ App::get('/v1/storage/files/:fileId/preview') ->param('rotation', 0, new Range(0,360), 'Preview image rotation in degrees. Pass an integer between 0 and 360.', true) ->param('background', '', new HexColor(), 'Preview image background color. Only works with transparent images (png). Use a valid HEX color, no # is needed for prefix.', true) ->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true) - ->inject('request') ->inject('response') - ->inject('project') - ->inject('dbForInternal') - ->action(function ($fileId, $width, $height, $gravity, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $request, $response, $project, $dbForInternal) { + ->action(function ($fileId, $width, $height, $gravity, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $response) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Database $dbForInternal */ - $storage = 'files'; - - if (!\extension_loaded('imagick')) { - throw new Exception('Imagick extension is missing', 500); - } - - if (!Storage::exists($storage)) { - throw new Exception('No such storage device', 400); - } - - if ((\strpos($request->getAccept(), 'image/webp') === false) && ('webp' == $output)) { // Fallback webp to jpeg when no browser support - $output = 'jpg'; - } - - $inputs = Config::getParam('storage-inputs'); - $outputs = Config::getParam('storage-outputs'); - $fileLogos = Config::getParam('storage-logos'); - - $date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache - $key = \md5($fileId.$width.$height.$quality.$borderWidth.$borderColor.$borderRadius.$opacity.$rotation.$background.$storage.$output); - - $file = $dbForInternal->getDocument('files', $fileId); - - if (empty($file->getId())) { - throw new Exception('File not found', 404); - } - - $path = $file->getAttribute('path'); - $type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION)); - $algorithm = $file->getAttribute('algorithm'); - $cipher = $file->getAttribute('openSSLCipher'); - $mime = $file->getAttribute('mimeType'); - - if (!\in_array($mime, $inputs)) { - $path = (\array_key_exists($mime, $fileLogos)) ? $fileLogos[$mime] : $fileLogos['default']; - $algorithm = null; - $cipher = null; - $background = (empty($background)) ? 'eceff1' : $background; - $type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION)); - $key = \md5($path.$width.$height.$quality.$borderWidth.$borderColor.$borderRadius.$opacity.$rotation.$background.$storage.$output); - } - - $compressor = new GZIP(); - $device = Storage::getDevice('files'); - - if (!\file_exists($path)) { - throw new Exception('File not found', 404); - } - - $cache = new Cache(new Filesystem(APP_STORAGE_CACHE.'/app-'.$project->getId())); // Limit file number or size - $data = $cache->load($key, 60 * 60 * 24 * 30 * 3 /* 3 months */); - - if ($data) { - $output = (empty($output)) ? $type : $output; - - return $response - ->setContentType((\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg']) - ->addHeader('Expires', $date) - ->addHeader('X-Appwrite-Cache', 'hit') - ->send($data) - ; - } - - $source = $device->read($path); - - if (!empty($cipher)) { // Decrypt - $source = OpenSSL::decrypt( - $source, - $file->getAttribute('openSSLCipher'), - App::getEnv('_APP_OPENSSL_KEY_V'.$file->getAttribute('openSSLVersion')), - 0, - \hex2bin($file->getAttribute('openSSLIV')), - \hex2bin($file->getAttribute('openSSLTag')) - ); - } - - if (!empty($algorithm)) { - $source = $compressor->decompress($source); - } - - $image = new Image($source); - - $image->crop((int) $width, (int) $height, $gravity); - - if (!empty($opacity) || $opacity==0) { - $image->setOpacity($opacity); - } - - if (!empty($background)) { - $image->setBackground('#'.$background); - } - - - if (!empty($borderWidth) ) { - $image->setBorder($borderWidth, '#'.$borderColor); - } - - if (!empty($borderRadius)) { - $image->setBorderRadius($borderRadius); - } - - if (!empty($rotation)) { - $image->setRotation($rotation); - } - - $output = (empty($output)) ? $type : $output; - - $data = $image->output($output, $quality); - - $cache->save($key, $data); - - $response - ->setContentType($outputs[$output]) - ->addHeader('Expires', $date) - ->addHeader('X-Appwrite-Cache', 'miss') - ->send($data) - ; - - unset($image); + $response->redirect('/v1/storage/buckets/default/files/'.$fileId.'/preview?' + .\http_build_query(['width' => $width, 'height' => $height, 'gravity' => $gravity, 'quality' => $quality, 'borderWidth' => $borderWidth, 'borderColor' => $borderColor, 'borderRadius' => $borderRadius, 'opacity' => $opacity, 'rotation' => $rotation, 'background' => $background, 'output' => $output])); }); App::get('/v1/storage/files/:fileId/download') @@ -1238,49 +1106,11 @@ App::get('/v1/storage/files/:fileId/download') ->label('sdk.methodType', 'location') ->param('fileId', '', new UID(), 'File unique ID.') ->inject('response') - ->inject('dbForInternal') - ->action(function ($fileId, $response, $dbForInternal) { + ->action(function ($fileId, $response) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $file = $dbForInternal->getDocument('files', $fileId); - - if (empty($file->getId())) { - throw new Exception('File not found', 404); - } - - $path = $file->getAttribute('path', ''); - - if (!\file_exists($path)) { - throw new Exception('File not found in '.$path, 404); - } - - $compressor = new GZIP(); - $device = Storage::getDevice('files'); - - $source = $device->read($path); - - if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt - $source = OpenSSL::decrypt( - $source, - $file->getAttribute('openSSLCipher'), - App::getEnv('_APP_OPENSSL_KEY_V'.$file->getAttribute('openSSLVersion')), - 0, - \hex2bin($file->getAttribute('openSSLIV')), - \hex2bin($file->getAttribute('openSSLTag')) - ); - } - - $source = $compressor->decompress($source); - - // Response - $response - ->setContentType($file->getAttribute('mimeType')) - ->addHeader('Content-Disposition', 'attachment; filename="'.$file->getAttribute('name', '').'"') - ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache - ->addHeader('X-Peak', \memory_get_peak_usage()) - ->send($source) - ; + $response->redirect('/v1/storage/buckets/default/files/'.$fileId.'/download'); }); App::get('/v1/storage/files/:fileId/view') @@ -1296,59 +1126,11 @@ App::get('/v1/storage/files/:fileId/view') ->label('sdk.methodType', 'location') ->param('fileId', '', new UID(), 'File unique ID.') ->inject('response') - ->inject('dbForInternal') - ->action(function ($fileId, $response, $dbForInternal) { + ->action(function ($fileId, $response) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $file = $dbForInternal->getDocument('files', $fileId); - $mimes = Config::getParam('storage-mimes'); - - if (empty($file->getId())) { - throw new Exception('File not found', 404); - } - - $path = $file->getAttribute('path', ''); - - if (!\file_exists($path)) { - throw new Exception('File not found in '.$path, 404); - } - - $compressor = new GZIP(); - $device = Storage::getDevice('files'); - - $contentType = 'text/plain'; - - if (\in_array($file->getAttribute('mimeType'), $mimes)) { - $contentType = $file->getAttribute('mimeType'); - } - - $source = $device->read($path); - - if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt - $source = OpenSSL::decrypt( - $source, - $file->getAttribute('openSSLCipher'), - App::getEnv('_APP_OPENSSL_KEY_V'.$file->getAttribute('openSSLVersion')), - 0, - \hex2bin($file->getAttribute('openSSLIV')), - \hex2bin($file->getAttribute('openSSLTag')) - ); - } - - $output = $compressor->decompress($source); - $fileName = $file->getAttribute('name', ''); - - // Response - $response - ->setContentType($contentType) - ->addHeader('Content-Security-Policy', 'script-src none;') - ->addHeader('X-Content-Type-Options', 'nosniff') - ->addHeader('Content-Disposition', 'inline; filename="'.$fileName.'"') - ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache - ->addHeader('X-Peak', \memory_get_peak_usage()) - ->send($output) - ; + $response->redirect('/v1/storage/buckets/default/files/'.$fileId.'/view'); }); App::put('/v1/storage/files/:fileId') From 0e4736038b005779159ce0a4fe319ffe3c02bd06 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Fri, 25 Jun 2021 16:40:26 +0545 Subject: [PATCH 02/10] create file backward compatibility --- app/controllers/api/storage.php | 64 ++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 5c80ba8da9..67bf955458 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -915,13 +915,27 @@ App::post('/v1/storage/files') /** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $usage */ + $bucketId = 'default'; + $bucket = $dbForInternal->getDocument('buckets', $bucketId); + + if($bucket->isEmpty()) { + throw new Exception('Bucket not found', 404); + } + $file = $request->getFiles('file'); /* * Validators */ - //$fileType = new FileType(array(FileType::FILE_TYPE_PNG, FileType::FILE_TYPE_GIF, FileType::FILE_TYPE_JPEG)); - $fileSize = new FileSize(App::getEnv('_APP_STORAGE_LIMIT', 0)); + $allowedFileExtensions = $bucket->getAttribute('allowedFileExtensions', []); + $fileExt = new FileExt($allowedFileExtensions); + + $maximumFileSize = $bucket->getAttribute('maximumFileSize', 0); + if($maximumFileSize > (int) App::getEnv('_APP_STORAGE_LIMIT',0)) { + throw new Exception('Server error', 500); + } + + $fileSize = new FileSize($maximumFileSize); $upload = new Upload(); if (empty($file)) { @@ -934,9 +948,9 @@ App::post('/v1/storage/files') $file['size'] = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size']; // Check if file type is allowed (feature for project settings?) - //if (!$fileType->isValid($file['tmp_name'])) { - //throw new Exception('File type not allowed', 400); - //} + if (!empty($allowedFileExtensions) && !$fileExt->isValid($file['name'])) { + throw new Exception('File extension not allowed', 400); + } if (!$fileSize->isValid($file['size'])) { // Check if file size is exceeding allowed limit throw new Exception('File size not allowed', 400); @@ -951,6 +965,7 @@ App::post('/v1/storage/files') // Save to storage $size = $device->getFileSize($file['tmp_name']); $path = $device->getPath(\uniqid().'.'.\pathinfo($file['name'], PATHINFO_EXTENSION)); + $path = $bucket->getId() . '/' . $path; if (!$device->upload($file['tmp_name'], $path)) { // TODO deprecate 'upload' and replace with 'move' throw new Exception('Failed moving file', 500); @@ -958,7 +973,7 @@ App::post('/v1/storage/files') $mimeType = $device->getFileMimeType($path); // Get mime-type before compression and encryption - if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled') { // Check if scans are enabled + if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled' && $bucket->getAttribute('antiVirus', true) && $size <= APP_LIMIT_ANTIVIRUS) { $antiVirus = new Network(App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'), (int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310)); @@ -969,12 +984,17 @@ App::post('/v1/storage/files') } // Compression - $compressor = new GZIP(); $data = $device->read($path); - $data = $compressor->compress($data); - $key = App::getEnv('_APP_OPENSSL_KEY_V1'); - $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); - $data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag); + if($size <= APP_LIMIT_COMPRESSION) { + $compressor = new GZIP(); + $data = $compressor->compress($data); + } + + if($bucket->getAttribute('encryption', true) && $size <= APP_LIMIT_ENCRYPTION) { + $key = App::getEnv('_APP_OPENSSL_KEY_V1'); + $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); + $data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag); + } if (!$device->write($path, $data, $mimeType)) { throw new Exception('Failed to save file', 500); @@ -982,24 +1002,29 @@ App::post('/v1/storage/files') $sizeActual = $device->getFileSize($path); - $file = $dbForInternal->createDocument('files', new Document([ + $data = [ '$read' => (is_null($read) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $read ?? [], // By default set read permissions for user '$write' => (is_null($write) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $write ?? [], // By default set write permissions for user 'dateCreated' => \time(), - 'bucketId' => '', + 'bucketId' => $bucket->getId(), 'name' => $file['name'], 'path' => $path, 'signature' => $device->getFileHash($path), 'mimeType' => $mimeType, 'sizeOriginal' => $size, 'sizeActual' => $sizeActual, - 'algorithm' => $compressor->getName(), + 'algorithm' => empty($compressor) ? '' : $compressor->getName(), 'comment' => '', - 'openSSLVersion' => '1', - 'openSSLCipher' => OpenSSL::CIPHER_AES_128_GCM, - 'openSSLTag' => \bin2hex($tag), - 'openSSLIV' => \bin2hex($iv), - ])); + ]; + + if($bucket->getAttribute('encryption', true) && $size <= APP_LIMIT_ENCRYPTION) { + $data['openSSLVersion'] = '1'; + $data['openSSLCipher'] = OpenSSL::CIPHER_AES_128_GCM; + $data['openSSLTag'] = \bin2hex($tag); + $data['openSSLIV'] = \bin2hex($iv); + } + + $file = $dbForInternal->createDocument('files', new Document($data)); $audits ->setParam('event', 'storage.files.create') @@ -1012,7 +1037,6 @@ App::post('/v1/storage/files') $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic2($file, Response::MODEL_FILE); - ; }); App::get('/v1/storage/files') From bae1fd5cfb6b2728b668729c00ff77dec970d44e Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Fri, 25 Jun 2021 16:41:39 +0545 Subject: [PATCH 03/10] update and delete backward compatibility --- app/controllers/api/storage.php | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 67bf955458..22d0cb79dd 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -1180,17 +1180,23 @@ App::put('/v1/storage/files/:fileId') /** @var Utopia\Database\Database $dbForInternal */ /** @var Appwrite\Event\Event $audits */ + $bucketId = 'default'; + $bucket = $dbForInternal->getDocument('buckets', $bucketId); + + if($bucket->isEmpty()) { + throw new Exception('Bucket not found', 404); + } + $file = $dbForInternal->getDocument('files', $fileId); - if (empty($file->getId())) { + if ($file->isEmpty() || $file->getAttribute('bucketId') != $bucketId) { throw new Exception('File not found', 404); } - $file = $dbForInternal->updateDocument('files', $fileId, new Document(\array_merge($file->getArrayCopy(), [ - '$read' => $read, - '$write' => $write, - 'bucketId' => '', - ]))); + $file = $dbForInternal->updateDocument('files', $fileId, $file + ->setAttribute('$read', $read) + ->setAttribute('$write', $write) + ); $audits ->setParam('event', 'storage.files.update') @@ -1224,9 +1230,16 @@ App::delete('/v1/storage/files/:fileId') /** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $usage */ + $bucketId = 'default'; + $bucket = $dbForInternal->getDocument('buckets', $bucketId); + + if($bucket->isEmpty()) { + throw new Exception('Bucket not found', 404); + } + $file = $dbForInternal->getDocument('files', $fileId); - if (empty($file->getId())) { + if ($file->isEmpty() || $file->getAttribute('bucketId') != $bucketId) { throw new Exception('File not found', 404); } From 8c458383d346b6e76374e00271ee309999fec250 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Fri, 25 Jun 2021 16:28:37 +0545 Subject: [PATCH 04/10] backward compatibility for get requests --- app/controllers/api/storage.php | 242 ++------------------------------ 1 file changed, 12 insertions(+), 230 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 19546bb732..f1b9ad6751 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -1031,17 +1031,12 @@ App::get('/v1/storage/files') ->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true) ->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true) ->inject('response') - ->inject('dbForInternal') - ->action(function ($search, $limit, $offset, $orderType, $response, $dbForInternal) { + ->action(function ($search, $limit, $offset, $orderType, $response) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, $search)] : []; - - $response->dynamic2(new Document([ - 'files' => $dbForInternal->find('files', $queries, $limit, $offset, ['_id'], [$orderType]), - 'sum' => $dbForInternal->count('files', $queries, APP_LIMIT_COUNT), - ]), Response::MODEL_FILE_LIST); + $response->redirect('/v1/storage/buckets/default/files?=' + .\http_build_query(['search' => $search, 'limit' => $limit, 'offset' => $offset, 'orderType' => $orderType])); }); App::get('/v1/storage/files/:fileId') @@ -1057,18 +1052,11 @@ App::get('/v1/storage/files/:fileId') ->label('sdk.response.model', Response::MODEL_FILE) ->param('fileId', '', new UID(), 'File unique ID.') ->inject('response') - ->inject('dbForInternal') - ->action(function ($fileId, $response, $dbForInternal) { + ->action(function ($fileId, $response) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $file = $dbForInternal->getDocument('files', $fileId); - - if (empty($file->getId())) { - throw new Exception('File not found', 404); - } - - $response->dynamic2($file, Response::MODEL_FILE); + $response->redirect('/v1/storage/buckets/default/files/'.$fileId); }); App::get('/v1/storage/files/:fileId/preview') @@ -1094,135 +1082,15 @@ App::get('/v1/storage/files/:fileId/preview') ->param('rotation', 0, new Range(0,360), 'Preview image rotation in degrees. Pass an integer between 0 and 360.', true) ->param('background', '', new HexColor(), 'Preview image background color. Only works with transparent images (png). Use a valid HEX color, no # is needed for prefix.', true) ->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true) - ->inject('request') ->inject('response') - ->inject('project') - ->inject('dbForInternal') - ->action(function ($fileId, $width, $height, $gravity, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $request, $response, $project, $dbForInternal) { + ->action(function ($fileId, $width, $height, $gravity, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $response) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Database $dbForInternal */ - $storage = 'files'; - - if (!\extension_loaded('imagick')) { - throw new Exception('Imagick extension is missing', 500); - } - - if (!Storage::exists($storage)) { - throw new Exception('No such storage device', 400); - } - - if ((\strpos($request->getAccept(), 'image/webp') === false) && ('webp' == $output)) { // Fallback webp to jpeg when no browser support - $output = 'jpg'; - } - - $inputs = Config::getParam('storage-inputs'); - $outputs = Config::getParam('storage-outputs'); - $fileLogos = Config::getParam('storage-logos'); - - $date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache - $key = \md5($fileId.$width.$height.$quality.$borderWidth.$borderColor.$borderRadius.$opacity.$rotation.$background.$storage.$output); - - $file = $dbForInternal->getDocument('files', $fileId); - - if (empty($file->getId())) { - throw new Exception('File not found', 404); - } - - $path = $file->getAttribute('path'); - $type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION)); - $algorithm = $file->getAttribute('algorithm'); - $cipher = $file->getAttribute('openSSLCipher'); - $mime = $file->getAttribute('mimeType'); - - if (!\in_array($mime, $inputs)) { - $path = (\array_key_exists($mime, $fileLogos)) ? $fileLogos[$mime] : $fileLogos['default']; - $algorithm = null; - $cipher = null; - $background = (empty($background)) ? 'eceff1' : $background; - $type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION)); - $key = \md5($path.$width.$height.$quality.$borderWidth.$borderColor.$borderRadius.$opacity.$rotation.$background.$storage.$output); - } - - $compressor = new GZIP(); - $device = Storage::getDevice('files'); - - if (!\file_exists($path)) { - throw new Exception('File not found', 404); - } - - $cache = new Cache(new Filesystem(APP_STORAGE_CACHE.'/app-'.$project->getId())); // Limit file number or size - $data = $cache->load($key, 60 * 60 * 24 * 30 * 3 /* 3 months */); - - if ($data) { - $output = (empty($output)) ? $type : $output; - - return $response - ->setContentType((\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg']) - ->addHeader('Expires', $date) - ->addHeader('X-Appwrite-Cache', 'hit') - ->send($data) - ; - } - - $source = $device->read($path); - - if (!empty($cipher)) { // Decrypt - $source = OpenSSL::decrypt( - $source, - $file->getAttribute('openSSLCipher'), - App::getEnv('_APP_OPENSSL_KEY_V'.$file->getAttribute('openSSLVersion')), - 0, - \hex2bin($file->getAttribute('openSSLIV')), - \hex2bin($file->getAttribute('openSSLTag')) - ); - } - - if (!empty($algorithm)) { - $source = $compressor->decompress($source); - } - - $image = new Image($source); - - $image->crop((int) $width, (int) $height, $gravity); - - if (!empty($opacity) || $opacity==0) { - $image->setOpacity($opacity); - } - - if (!empty($background)) { - $image->setBackground('#'.$background); - } - - - if (!empty($borderWidth) ) { - $image->setBorder($borderWidth, '#'.$borderColor); - } - - if (!empty($borderRadius)) { - $image->setBorderRadius($borderRadius); - } - - if (!empty($rotation)) { - $image->setRotation($rotation); - } - - $output = (empty($output)) ? $type : $output; - - $data = $image->output($output, $quality); - - $cache->save($key, $data); - - $response - ->setContentType($outputs[$output]) - ->addHeader('Expires', $date) - ->addHeader('X-Appwrite-Cache', 'miss') - ->send($data) - ; - - unset($image); + $response->redirect('/v1/storage/buckets/default/files/'.$fileId.'/preview?' + .\http_build_query(['width' => $width, 'height' => $height, 'gravity' => $gravity, 'quality' => $quality, 'borderWidth' => $borderWidth, 'borderColor' => $borderColor, 'borderRadius' => $borderRadius, 'opacity' => $opacity, 'rotation' => $rotation, 'background' => $background, 'output' => $output])); }); App::get('/v1/storage/files/:fileId/download') @@ -1238,49 +1106,11 @@ App::get('/v1/storage/files/:fileId/download') ->label('sdk.methodType', 'location') ->param('fileId', '', new UID(), 'File unique ID.') ->inject('response') - ->inject('dbForInternal') - ->action(function ($fileId, $response, $dbForInternal) { + ->action(function ($fileId, $response) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $file = $dbForInternal->getDocument('files', $fileId); - - if (empty($file->getId())) { - throw new Exception('File not found', 404); - } - - $path = $file->getAttribute('path', ''); - - if (!\file_exists($path)) { - throw new Exception('File not found in '.$path, 404); - } - - $compressor = new GZIP(); - $device = Storage::getDevice('files'); - - $source = $device->read($path); - - if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt - $source = OpenSSL::decrypt( - $source, - $file->getAttribute('openSSLCipher'), - App::getEnv('_APP_OPENSSL_KEY_V'.$file->getAttribute('openSSLVersion')), - 0, - \hex2bin($file->getAttribute('openSSLIV')), - \hex2bin($file->getAttribute('openSSLTag')) - ); - } - - $source = $compressor->decompress($source); - - // Response - $response - ->setContentType($file->getAttribute('mimeType')) - ->addHeader('Content-Disposition', 'attachment; filename="'.$file->getAttribute('name', '').'"') - ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache - ->addHeader('X-Peak', \memory_get_peak_usage()) - ->send($source) - ; + $response->redirect('/v1/storage/buckets/default/files/'.$fileId.'/download'); }); App::get('/v1/storage/files/:fileId/view') @@ -1296,59 +1126,11 @@ App::get('/v1/storage/files/:fileId/view') ->label('sdk.methodType', 'location') ->param('fileId', '', new UID(), 'File unique ID.') ->inject('response') - ->inject('dbForInternal') - ->action(function ($fileId, $response, $dbForInternal) { + ->action(function ($fileId, $response) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $file = $dbForInternal->getDocument('files', $fileId); - $mimes = Config::getParam('storage-mimes'); - - if (empty($file->getId())) { - throw new Exception('File not found', 404); - } - - $path = $file->getAttribute('path', ''); - - if (!\file_exists($path)) { - throw new Exception('File not found in '.$path, 404); - } - - $compressor = new GZIP(); - $device = Storage::getDevice('files'); - - $contentType = 'text/plain'; - - if (\in_array($file->getAttribute('mimeType'), $mimes)) { - $contentType = $file->getAttribute('mimeType'); - } - - $source = $device->read($path); - - if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt - $source = OpenSSL::decrypt( - $source, - $file->getAttribute('openSSLCipher'), - App::getEnv('_APP_OPENSSL_KEY_V'.$file->getAttribute('openSSLVersion')), - 0, - \hex2bin($file->getAttribute('openSSLIV')), - \hex2bin($file->getAttribute('openSSLTag')) - ); - } - - $output = $compressor->decompress($source); - $fileName = $file->getAttribute('name', ''); - - // Response - $response - ->setContentType($contentType) - ->addHeader('Content-Security-Policy', 'script-src none;') - ->addHeader('X-Content-Type-Options', 'nosniff') - ->addHeader('Content-Disposition', 'inline; filename="'.$fileName.'"') - ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache - ->addHeader('X-Peak', \memory_get_peak_usage()) - ->send($output) - ; + $response->redirect('/v1/storage/buckets/default/files/'.$fileId.'/view'); }); App::put('/v1/storage/files/:fileId') From ee580add1a60b3397877b6ef0b7356e5237d632c Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Fri, 25 Jun 2021 16:40:26 +0545 Subject: [PATCH 05/10] create file backward compatibility --- app/controllers/api/storage.php | 64 ++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index f1b9ad6751..0abaf65573 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -915,13 +915,27 @@ App::post('/v1/storage/files') /** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $usage */ + $bucketId = 'default'; + $bucket = $dbForInternal->getDocument('buckets', $bucketId); + + if($bucket->isEmpty()) { + throw new Exception('Bucket not found', 404); + } + $file = $request->getFiles('file'); /* * Validators */ - //$fileType = new FileType(array(FileType::FILE_TYPE_PNG, FileType::FILE_TYPE_GIF, FileType::FILE_TYPE_JPEG)); - $fileSize = new FileSize(App::getEnv('_APP_STORAGE_LIMIT', 0)); + $allowedFileExtensions = $bucket->getAttribute('allowedFileExtensions', []); + $fileExt = new FileExt($allowedFileExtensions); + + $maximumFileSize = $bucket->getAttribute('maximumFileSize', 0); + if($maximumFileSize > (int) App::getEnv('_APP_STORAGE_LIMIT',0)) { + throw new Exception('Server error', 500); + } + + $fileSize = new FileSize($maximumFileSize); $upload = new Upload(); if (empty($file)) { @@ -934,9 +948,9 @@ App::post('/v1/storage/files') $file['size'] = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size']; // Check if file type is allowed (feature for project settings?) - //if (!$fileType->isValid($file['tmp_name'])) { - //throw new Exception('File type not allowed', 400); - //} + if (!empty($allowedFileExtensions) && !$fileExt->isValid($file['name'])) { + throw new Exception('File extension not allowed', 400); + } if (!$fileSize->isValid($file['size'])) { // Check if file size is exceeding allowed limit throw new Exception('File size not allowed', 400); @@ -951,6 +965,7 @@ App::post('/v1/storage/files') // Save to storage $size = $device->getFileSize($file['tmp_name']); $path = $device->getPath(\uniqid().'.'.\pathinfo($file['name'], PATHINFO_EXTENSION)); + $path = $bucket->getId() . '/' . $path; if (!$device->upload($file['tmp_name'], $path)) { // TODO deprecate 'upload' and replace with 'move' throw new Exception('Failed moving file', 500); @@ -958,7 +973,7 @@ App::post('/v1/storage/files') $mimeType = $device->getFileMimeType($path); // Get mime-type before compression and encryption - if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled') { // Check if scans are enabled + if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled' && $bucket->getAttribute('antiVirus', true) && $size <= APP_LIMIT_ANTIVIRUS) { $antiVirus = new Network(App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'), (int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310)); @@ -969,12 +984,17 @@ App::post('/v1/storage/files') } // Compression - $compressor = new GZIP(); $data = $device->read($path); - $data = $compressor->compress($data); - $key = App::getEnv('_APP_OPENSSL_KEY_V1'); - $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); - $data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag); + if($size <= APP_LIMIT_COMPRESSION) { + $compressor = new GZIP(); + $data = $compressor->compress($data); + } + + if($bucket->getAttribute('encryption', true) && $size <= APP_LIMIT_ENCRYPTION) { + $key = App::getEnv('_APP_OPENSSL_KEY_V1'); + $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); + $data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag); + } if (!$device->write($path, $data, $mimeType)) { throw new Exception('Failed to save file', 500); @@ -982,24 +1002,29 @@ App::post('/v1/storage/files') $sizeActual = $device->getFileSize($path); - $file = $dbForInternal->createDocument('files', new Document([ + $data = [ '$read' => (is_null($read) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $read ?? [], // By default set read permissions for user '$write' => (is_null($write) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $write ?? [], // By default set write permissions for user 'dateCreated' => \time(), - 'bucketId' => '', + 'bucketId' => $bucket->getId(), 'name' => $file['name'], 'path' => $path, 'signature' => $device->getFileHash($path), 'mimeType' => $mimeType, 'sizeOriginal' => $size, 'sizeActual' => $sizeActual, - 'algorithm' => $compressor->getName(), + 'algorithm' => empty($compressor) ? '' : $compressor->getName(), 'comment' => '', - 'openSSLVersion' => '1', - 'openSSLCipher' => OpenSSL::CIPHER_AES_128_GCM, - 'openSSLTag' => \bin2hex($tag), - 'openSSLIV' => \bin2hex($iv), - ])); + ]; + + if($bucket->getAttribute('encryption', true) && $size <= APP_LIMIT_ENCRYPTION) { + $data['openSSLVersion'] = '1'; + $data['openSSLCipher'] = OpenSSL::CIPHER_AES_128_GCM; + $data['openSSLTag'] = \bin2hex($tag); + $data['openSSLIV'] = \bin2hex($iv); + } + + $file = $dbForInternal->createDocument('files', new Document($data)); $audits ->setParam('event', 'storage.files.create') @@ -1012,7 +1037,6 @@ App::post('/v1/storage/files') $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic2($file, Response::MODEL_FILE); - ; }); App::get('/v1/storage/files') From d82594cfcd570c014fe0badeb12f0df979d52afd Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Fri, 25 Jun 2021 16:41:39 +0545 Subject: [PATCH 06/10] update and delete backward compatibility --- app/controllers/api/storage.php | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 0abaf65573..b78b96a3e0 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -1180,17 +1180,23 @@ App::put('/v1/storage/files/:fileId') /** @var Utopia\Database\Database $dbForInternal */ /** @var Appwrite\Event\Event $audits */ + $bucketId = 'default'; + $bucket = $dbForInternal->getDocument('buckets', $bucketId); + + if($bucket->isEmpty()) { + throw new Exception('Bucket not found', 404); + } + $file = $dbForInternal->getDocument('files', $fileId); - if (empty($file->getId())) { + if ($file->isEmpty() || $file->getAttribute('bucketId') != $bucketId) { throw new Exception('File not found', 404); } - $file = $dbForInternal->updateDocument('files', $fileId, new Document(\array_merge($file->getArrayCopy(), [ - '$read' => $read, - '$write' => $write, - 'bucketId' => '', - ]))); + $file = $dbForInternal->updateDocument('files', $fileId, $file + ->setAttribute('$read', $read) + ->setAttribute('$write', $write) + ); $audits ->setParam('event', 'storage.files.update') @@ -1224,9 +1230,16 @@ App::delete('/v1/storage/files/:fileId') /** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $usage */ + $bucketId = 'default'; + $bucket = $dbForInternal->getDocument('buckets', $bucketId); + + if($bucket->isEmpty()) { + throw new Exception('Bucket not found', 404); + } + $file = $dbForInternal->getDocument('files', $fileId); - if (empty($file->getId())) { + if ($file->isEmpty() || $file->getAttribute('bucketId') != $bucketId) { throw new Exception('File not found', 404); } From 37239edad54c9d64798613d154e2e8514d8dc605 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 4 Jul 2021 18:15:47 +0545 Subject: [PATCH 07/10] wip --- tests/e2e/Services/Storage/StorageBase.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index bf1fd9b287..71a27c4551 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -376,6 +376,18 @@ trait StorageBase /** * Test for SUCCESS */ + $bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], $this->getHeaders()), [ + 'name' => 'Test Bucket', + 'maximumFileSize' => 2000000, //2MB + 'allowedFileExtensions' => ["jpg", "png"], + 'read' => ['role:all'], + 'write' => ['role:all'], + ]); + $file = $this->client->call(Client::METHOD_POST, '/storage/files', array_merge([ 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $this->getProject()['$id'], From 6249529de19bf8068569650984057bb909f7ff7a Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 5 Jul 2021 13:48:01 +0545 Subject: [PATCH 08/10] update utopia-framework --- composer.json | 2 +- composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 0712b4b5ad..f0652c4403 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "appwrite/php-clamav": "1.1.*", "appwrite/php-runtimes": "0.4.*", - "utopia-php/framework": "0.14.*", + "utopia-php/framework": "0.15.*", "utopia-php/abuse": "dev-feat-utopia-db-integration", "utopia-php/analytics": "0.2.*", "utopia-php/audit": "dev-feat-utopia-db-integration", diff --git a/composer.lock b/composer.lock index c0fbb00ce8..4553027855 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ca8cf356c884e8d332b04bebf9d3b11b", + "content-hash": "14172d7cbfa5a5b7353d3f8b24e44b81", "packages": [ { "name": "adhocore/jwt", @@ -2079,16 +2079,16 @@ }, { "name": "utopia-php/framework", - "version": "0.14.1", + "version": "0.15.0", "source": { "type": "git", "url": "https://github.com/utopia-php/framework.git", - "reference": "632113288bebe41cbef79f0d355bd91609767b8c" + "reference": "3916871b173d6bc509c4e1bb236349382b8a84ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/632113288bebe41cbef79f0d355bd91609767b8c", - "reference": "632113288bebe41cbef79f0d355bd91609767b8c", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/3916871b173d6bc509c4e1bb236349382b8a84ab", + "reference": "3916871b173d6bc509c4e1bb236349382b8a84ab", "shasum": "" }, "require": { @@ -2122,9 +2122,9 @@ ], "support": { "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.14.1" + "source": "https://github.com/utopia-php/framework/tree/0.15.0" }, - "time": "2021-05-21T06:41:45+00:00" + "time": "2021-07-04T12:55:46+00:00" }, { "name": "utopia-php/image", From cd53af537544b259a188cb927ddd5e70852a1d70 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 5 Jul 2021 13:52:45 +0545 Subject: [PATCH 09/10] route aliases for backward compatibility --- app/controllers/api/storage.php | 391 +------------------------------- 1 file changed, 8 insertions(+), 383 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index a565d5acc5..eec8a6b766 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -247,6 +247,7 @@ App::delete('/v1/storage/buckets/:bucketId') }); App::post('/v1/storage/buckets/:bucketId/files') + ->alias('/v1/storage/files',['bucketId' => 'default']) ->desc('Create File') ->groups(['api', 'storage']) ->label('scope', 'files.write') @@ -402,6 +403,7 @@ App::post('/v1/storage/buckets/:bucketId/files') }); App::get('/v1/storage/buckets/:bucketId/files') + ->alias('/v1/storage/files', ['bucketId' => 'default']) ->desc('List Files') ->groups(['api', 'storage']) ->label('scope', 'files.read') @@ -442,6 +444,7 @@ App::get('/v1/storage/buckets/:bucketId/files') }); App::get('/v1/storage/buckets/:bucketId/files/:fileId') + ->alias('/v1/storage/files/:fileId', ['bucketId' => 'default']) ->desc('Get File') ->groups(['api', 'storage']) ->label('scope', 'files.read') @@ -476,6 +479,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') }); App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') + ->alias('/v1/storage/files/:fileId/preview', ['bucketId' => 'default']) ->desc('Get File Preview') ->groups(['api', 'storage']) ->label('scope', 'files.read') @@ -635,6 +639,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') }); App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') + ->alias('/v1/storage/files/:fileId/download', ['bucketId' => 'default']) ->desc('Get File for Download') ->groups(['api', 'storage']) ->label('scope', 'files.read') @@ -700,6 +705,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') }); App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') + ->alias('/v1/storage/files/:fileId/view', ['bucketId' => 'default']) ->desc('Get File for View') ->groups(['api', 'storage']) ->label('scope', 'files.read') @@ -775,6 +781,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') }); App::put('/v1/storage/buckets/:bucketId/files/:fileId') + ->alias('/v1/storage/files/:fileId', ['bucketId' => 'default']) ->desc('Update File') ->groups(['api', 'storage']) ->label('scope', 'files.write') @@ -824,6 +831,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') }); App::delete('/v1/storage/buckets/:bucketId/files/:fileId') + ->alias('/v1/storage/files/:fileId', ['bucketId' => 'default']) ->desc('Delete File') ->groups(['api', 'storage']) ->label('scope', 'files.write') @@ -883,386 +891,3 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') $response->noContent(); }); - -App::post('/v1/storage/files') - ->desc('Create File') - ->groups(['api', 'storage']) - ->label('scope', 'files.write') - ->label('event', 'storage.files.create') - ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) - ->label('sdk.namespace', 'storage') - ->label('sdk.method', 'createFile') - ->label('sdk.description', '/docs/references/storage/create-file.md') - ->label('sdk.request.type', 'multipart/form-data') - ->label('sdk.methodType', 'upload') - ->label('sdk.response.code', Response::STATUS_CODE_CREATED) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_FILE) - ->param('file', [], new File(), 'Binary file.', false) - ->param('read', null, new ArrayList(new Text(64)), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true) - ->param('write', null, new ArrayList(new Text(64)), 'An array of strings with write permissions. By default only the current user is granted with write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true) - ->inject('request') - ->inject('response') - ->inject('dbForInternal') - ->inject('user') - ->inject('audits') - ->inject('usage') - ->action(function ($file, $read, $write, $request, $response, $dbForInternal, $user, $audits, $usage) { - /** @var Utopia\Swoole\Request $request */ - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForInternal */ - /** @var Appwrite\Database\Document $user */ - /** @var Appwrite\Event\Event $audits */ - /** @var Appwrite\Event\Event $usage */ - - $bucketId = 'default'; - $bucket = $dbForInternal->getDocument('buckets', $bucketId); - - if($bucket->isEmpty()) { - throw new Exception('Bucket not found', 404); - } - - $file = $request->getFiles('file'); - - /* - * Validators - */ - $allowedFileExtensions = $bucket->getAttribute('allowedFileExtensions', []); - $fileExt = new FileExt($allowedFileExtensions); - - $maximumFileSize = $bucket->getAttribute('maximumFileSize', 0); - if($maximumFileSize > (int) App::getEnv('_APP_STORAGE_LIMIT',0)) { - throw new Exception('Server error', 500); - } - - $fileSize = new FileSize($maximumFileSize); - $upload = new Upload(); - - if (empty($file)) { - throw new Exception('No file sent', 400); - } - - // Make sure we handle a single file and multiple files the same way - $file['name'] = (\is_array($file['name']) && isset($file['name'][0])) ? $file['name'][0] : $file['name']; - $file['tmp_name'] = (\is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name']; - $file['size'] = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size']; - - // Check if file type is allowed (feature for project settings?) - if (!empty($allowedFileExtensions) && !$fileExt->isValid($file['name'])) { - throw new Exception('File extension not allowed', 400); - } - - if (!$fileSize->isValid($file['size'])) { // Check if file size is exceeding allowed limit - throw new Exception('File size not allowed', 400); - } - - $device = Storage::getDevice('files'); - - if (!$upload->isValid($file['tmp_name'])) { - throw new Exception('Invalid file', 403); - } - - // Save to storage - $size = $device->getFileSize($file['tmp_name']); - $path = $device->getPath(\uniqid().'.'.\pathinfo($file['name'], PATHINFO_EXTENSION)); - $path = $bucket->getId() . '/' . $path; - - if (!$device->upload($file['tmp_name'], $path)) { // TODO deprecate 'upload' and replace with 'move' - throw new Exception('Failed moving file', 500); - } - - $mimeType = $device->getFileMimeType($path); // Get mime-type before compression and encryption - - if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled' && $bucket->getAttribute('antiVirus', true) && $size <= APP_LIMIT_ANTIVIRUS) { - $antiVirus = new Network(App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'), - (int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310)); - - if (!$antiVirus->fileScan($path)) { - $device->delete($path); - throw new Exception('Invalid file', 403); - } - } - - // Compression - $data = $device->read($path); - if($size <= APP_LIMIT_COMPRESSION) { - $compressor = new GZIP(); - $data = $compressor->compress($data); - } - - if($bucket->getAttribute('encryption', true) && $size <= APP_LIMIT_ENCRYPTION) { - $key = App::getEnv('_APP_OPENSSL_KEY_V1'); - $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); - $data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag); - } - - if (!$device->write($path, $data, $mimeType)) { - throw new Exception('Failed to save file', 500); - } - - $sizeActual = $device->getFileSize($path); - - $data = [ - '$read' => (is_null($read) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $read ?? [], // By default set read permissions for user - '$write' => (is_null($write) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $write ?? [], // By default set write permissions for user - 'dateCreated' => \time(), - 'bucketId' => $bucket->getId(), - 'name' => $file['name'], - 'path' => $path, - 'signature' => $device->getFileHash($path), - 'mimeType' => $mimeType, - 'sizeOriginal' => $size, - 'sizeActual' => $sizeActual, - 'algorithm' => empty($compressor) ? '' : $compressor->getName(), - 'comment' => '', - ]; - - if($bucket->getAttribute('encryption', true) && $size <= APP_LIMIT_ENCRYPTION) { - $data['openSSLVersion'] = '1'; - $data['openSSLCipher'] = OpenSSL::CIPHER_AES_128_GCM; - $data['openSSLTag'] = \bin2hex($tag); - $data['openSSLIV'] = \bin2hex($iv); - } - - $file = $dbForInternal->createDocument('files', new Document($data)); - - $audits - ->setParam('event', 'storage.files.create') - ->setParam('resource', 'storage/files/'.$file->getId()) - ; - - $usage - ->setParam('storage', $sizeActual) - ; - - $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($file, Response::MODEL_FILE); - }); - -App::get('/v1/storage/files') - ->desc('List Files') - ->groups(['api', 'storage']) - ->label('scope', 'files.read') - ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) - ->label('sdk.namespace', 'storage') - ->label('sdk.method', 'listFiles') - ->label('sdk.description', '/docs/references/storage/list-files.md') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_FILE_LIST) - ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) - ->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true) - ->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true) - ->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true) - ->inject('response') - ->action(function ($search, $limit, $offset, $orderType, $response) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForInternal */ - - $response->redirect('/v1/storage/buckets/default/files?=' - .\http_build_query(['search' => $search, 'limit' => $limit, 'offset' => $offset, 'orderType' => $orderType])); - }); - -App::get('/v1/storage/files/:fileId') - ->desc('Get File') - ->groups(['api', 'storage']) - ->label('scope', 'files.read') - ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) - ->label('sdk.namespace', 'storage') - ->label('sdk.method', 'getFile') - ->label('sdk.description', '/docs/references/storage/get-file.md') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_FILE) - ->param('fileId', '', new UID(), 'File unique ID.') - ->inject('response') - ->action(function ($fileId, $response) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForInternal */ - - $response->redirect('/v1/storage/buckets/default/files/'.$fileId); - }); - -App::get('/v1/storage/files/:fileId/preview') - ->desc('Get File Preview') - ->groups(['api', 'storage']) - ->label('scope', 'files.read') - ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) - ->label('sdk.namespace', 'storage') - ->label('sdk.method', 'getFilePreview') - ->label('sdk.description', '/docs/references/storage/get-file-preview.md') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE) - ->label('sdk.methodType', 'location') - ->param('fileId', '', new UID(), 'File unique ID') - ->param('width', 0, new Range(0, 4000), 'Resize preview image width, Pass an integer between 0 to 4000.', true) - ->param('height', 0, new Range(0, 4000), 'Resize preview image height, Pass an integer between 0 to 4000.', true) - ->param('gravity', Image::GRAVITY_CENTER, new WhiteList(Image::getGravityTypes()), 'Image crop gravity. Can be one of ' . implode(",", Image::getGravityTypes()), true) - ->param('quality', 100, new Range(0, 100), 'Preview image quality. Pass an integer between 0 to 100. Defaults to 100.', true) - ->param('borderWidth', 0, new Range(0, 100), 'Preview image border in pixels. Pass an integer between 0 to 100. Defaults to 0.', true) - ->param('borderColor', '', new HexColor(), 'Preview image border color. Use a valid HEX color, no # is needed for prefix.', true) - ->param('borderRadius', 0, new Range(0, 4000), 'Preview image border radius in pixels. Pass an integer between 0 to 4000.', true) - ->param('opacity', 1, new Range(0,1, Range::TYPE_FLOAT), 'Preview image opacity. Only works with images having an alpha channel (like png). Pass a number between 0 to 1.', true) - ->param('rotation', 0, new Range(0,360), 'Preview image rotation in degrees. Pass an integer between 0 and 360.', true) - ->param('background', '', new HexColor(), 'Preview image background color. Only works with transparent images (png). Use a valid HEX color, no # is needed for prefix.', true) - ->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true) - ->inject('response') - ->action(function ($fileId, $width, $height, $gravity, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $response) { - /** @var Utopia\Swoole\Request $request */ - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Document $project */ - /** @var Utopia\Database\Database $dbForInternal */ - - $response->redirect('/v1/storage/buckets/default/files/'.$fileId.'/preview?' - .\http_build_query(['width' => $width, 'height' => $height, 'gravity' => $gravity, 'quality' => $quality, 'borderWidth' => $borderWidth, 'borderColor' => $borderColor, 'borderRadius' => $borderRadius, 'opacity' => $opacity, 'rotation' => $rotation, 'background' => $background, 'output' => $output])); - }); - -App::get('/v1/storage/files/:fileId/download') - ->desc('Get File for Download') - ->groups(['api', 'storage']) - ->label('scope', 'files.read') - ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) - ->label('sdk.namespace', 'storage') - ->label('sdk.method', 'getFileDownload') - ->label('sdk.description', '/docs/references/storage/get-file-download.md') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', '*/*') - ->label('sdk.methodType', 'location') - ->param('fileId', '', new UID(), 'File unique ID.') - ->inject('response') - ->action(function ($fileId, $response) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForInternal */ - - $response->redirect('/v1/storage/buckets/default/files/'.$fileId.'/download'); - }); - -App::get('/v1/storage/files/:fileId/view') - ->desc('Get File for View') - ->groups(['api', 'storage']) - ->label('scope', 'files.read') - ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) - ->label('sdk.namespace', 'storage') - ->label('sdk.method', 'getFileView') - ->label('sdk.description', '/docs/references/storage/get-file-view.md') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', '*/*') - ->label('sdk.methodType', 'location') - ->param('fileId', '', new UID(), 'File unique ID.') - ->inject('response') - ->action(function ($fileId, $response) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForInternal */ - - $response->redirect('/v1/storage/buckets/default/files/'.$fileId.'/view'); - }); - -App::put('/v1/storage/files/:fileId') - ->desc('Update File') - ->groups(['api', 'storage']) - ->label('scope', 'files.write') - ->label('event', 'storage.files.update') - ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) - ->label('sdk.namespace', 'storage') - ->label('sdk.method', 'updateFile') - ->label('sdk.description', '/docs/references/storage/update-file.md') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_FILE) - ->param('fileId', '', new UID(), 'File unique ID.') - ->param('read', [], new ArrayList(new Text(64)), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.') - ->param('write', [], new ArrayList(new Text(64)), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.') - ->inject('response') - ->inject('dbForInternal') - ->inject('audits') - ->action(function ($fileId, $read, $write, $response, $dbForInternal, $audits) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForInternal */ - /** @var Appwrite\Event\Event $audits */ - - $bucketId = 'default'; - $bucket = $dbForInternal->getDocument('buckets', $bucketId); - - if($bucket->isEmpty()) { - throw new Exception('Bucket not found', 404); - } - - $file = $dbForInternal->getDocument('files', $fileId); - - if ($file->isEmpty() || $file->getAttribute('bucketId') != $bucketId) { - throw new Exception('File not found', 404); - } - - $file = $dbForInternal->updateDocument('files', $fileId, $file - ->setAttribute('$read', $read) - ->setAttribute('$write', $write) - ); - - $audits - ->setParam('event', 'storage.files.update') - ->setParam('resource', 'storage/files/'.$file->getId()) - ; - - $response->dynamic2($file, Response::MODEL_FILE); - }); - -App::delete('/v1/storage/files/:fileId') - ->desc('Delete File') - ->groups(['api', 'storage']) - ->label('scope', 'files.write') - ->label('event', 'storage.files.delete') - ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) - ->label('sdk.namespace', 'storage') - ->label('sdk.method', 'deleteFile') - ->label('sdk.description', '/docs/references/storage/delete-file.md') - ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) - ->label('sdk.response.model', Response::MODEL_NONE) - ->param('fileId', '', new UID(), 'File unique ID.') - ->inject('response') - ->inject('dbForInternal') - ->inject('events') - ->inject('audits') - ->inject('usage') - ->action(function ($fileId, $response, $dbForInternal, $events, $audits, $usage) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForInternal */ - /** @var Appwrite\Event\Event $events */ - /** @var Appwrite\Event\Event $audits */ - /** @var Appwrite\Event\Event $usage */ - - $bucketId = 'default'; - $bucket = $dbForInternal->getDocument('buckets', $bucketId); - - if($bucket->isEmpty()) { - throw new Exception('Bucket not found', 404); - } - - $file = $dbForInternal->getDocument('files', $fileId); - - if ($file->isEmpty() || $file->getAttribute('bucketId') != $bucketId) { - throw new Exception('File not found', 404); - } - - $device = Storage::getDevice('files'); - - if ($device->delete($file->getAttribute('path', ''))) { - if (!$dbForInternal->deleteDocument('files', $fileId)) { - throw new Exception('Failed to remove file from DB', 500); - } - } - - $audits - ->setParam('event', 'storage.files.delete') - ->setParam('resource', 'storage/files/'.$file->getId()) - ; - - $usage - ->setParam('storage', $file->getAttribute('size', 0) * -1) - ; - - $events - ->setParam('eventData', $response->output2($file, Response::MODEL_FILE)) - ; - - $response->noContent(); - }); From 8d40a1b3cee3816a7e8350285460a3b1a217042f Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 5 Jul 2021 13:57:47 +0545 Subject: [PATCH 10/10] removing existing storage tests without buckets --- tests/e2e/Services/Storage/StorageBase.php | 245 --------------------- 1 file changed, 245 deletions(-) diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index 71a27c4551..9f35e0fe0f 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -371,249 +371,4 @@ trait StorageBase return $data; } - public function testCreateFile(): array - { - /** - * Test for SUCCESS - */ - $bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], $this->getHeaders()), [ - 'name' => 'Test Bucket', - 'maximumFileSize' => 2000000, //2MB - 'allowedFileExtensions' => ["jpg", "png"], - 'read' => ['role:all'], - 'write' => ['role:all'], - ]); - - $file = $this->client->call(Client::METHOD_POST, '/storage/files', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'), - 'read' => ['role:all'], - 'write' => ['role:all'], - ]); - - $this->assertEquals($file['headers']['status-code'], 201); - $this->assertNotEmpty($file['body']['$id']); - $this->assertIsInt($file['body']['dateCreated']); - $this->assertEquals('logo.png', $file['body']['name']); - $this->assertEquals('image/png', $file['body']['mimeType']); - $this->assertEquals(47218, $file['body']['sizeOriginal']); - - /** - * Test for FAILURE - */ - return ['fileId' => $file['body']['$id']]; - } - - /** - * @depends testCreateFile - */ - public function testGetFile(array $data): array - { - /** - * Test for SUCCESS - */ - $file1 = $this->client->call(Client::METHOD_GET, '/storage/files/' . $data['fileId'], array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals($file1['headers']['status-code'], 200); - $this->assertNotEmpty($file1['body']['$id']); - $this->assertIsInt($file1['body']['dateCreated']); - $this->assertEquals('logo.png', $file1['body']['name']); - $this->assertEquals('image/png', $file1['body']['mimeType']); - $this->assertEquals(47218, $file1['body']['sizeOriginal']); - //$this->assertEquals(54944, $file1['body']['sizeActual']); - //$this->assertEquals('gzip', $file1['body']['algorithm']); - //$this->assertEquals('1', $file1['body']['fileOpenSSLVersion']); - //$this->assertEquals('aes-128-gcm', $file1['body']['fileOpenSSLCipher']); - //$this->assertNotEmpty($file1['body']['fileOpenSSLTag']); - //$this->assertNotEmpty($file1['body']['fileOpenSSLIV']); - $this->assertIsArray($file1['body']['$read']); - $this->assertIsArray($file1['body']['$write']); - $this->assertCount(1, $file1['body']['$read']); - $this->assertCount(1, $file1['body']['$write']); - - $file2 = $this->client->call(Client::METHOD_GET, '/storage/files/' . $data['fileId'] . '/preview', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals(200, $file2['headers']['status-code']); - $this->assertEquals('image/png', $file2['headers']['content-type']); - $this->assertNotEmpty($file2['body']); - - //new image preview features - $file3 = $this->client->call(Client::METHOD_GET, '/storage/files/' . $data['fileId'] . '/preview', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'width' => 300, - 'height' => 100, - 'borderRadius' => '50', - 'opacity' => '0.5', - 'output' => 'png', - 'rotation' => '45', - ]); - - $this->assertEquals(200, $file3['headers']['status-code']); - $this->assertEquals('image/png', $file3['headers']['content-type']); - $this->assertNotEmpty($file3['body']); - - $image = new \Imagick(); - $image->readImageBlob($file3['body']); - $original = new \Imagick(__DIR__ . '/../../../resources/logo-after.png'); - - $this->assertEquals($image->getImageWidth(), $original->getImageWidth()); - $this->assertEquals($image->getImageHeight(), $original->getImageHeight()); - $this->assertEquals('PNG', $image->getImageFormat()); - - $file4 = $this->client->call(Client::METHOD_GET, '/storage/files/' . $data['fileId'] . '/preview', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'width' => 200, - 'height' => 80, - 'borderWidth' => '5', - 'borderColor' => 'ff0000', - 'output' => 'jpg', - ]); - - $this->assertEquals(200, $file4['headers']['status-code']); - $this->assertEquals('image/jpeg', $file4['headers']['content-type']); - $this->assertNotEmpty($file4['body']); - - $image = new \Imagick(); - $image->readImageBlob($file4['body']); - $original = new \Imagick(__DIR__ . '/../../../resources/logo-after.jpg'); - - $this->assertEquals($image->getImageWidth(), $original->getImageWidth()); - $this->assertEquals($image->getImageHeight(), $original->getImageHeight()); - $this->assertEquals('JPEG', $image->getImageFormat()); - - $file5 = $this->client->call(Client::METHOD_GET, '/storage/files/' . $data['fileId'] . '/download', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals(200, $file5['headers']['status-code']); - $this->assertEquals('attachment; filename="logo.png"', $file5['headers']['content-disposition']); - $this->assertEquals('image/png', $file5['headers']['content-type']); - $this->assertNotEmpty($file5['body']); - - $file6 = $this->client->call(Client::METHOD_GET, '/storage/files/' . $data['fileId'] . '/view', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals(200, $file6['headers']['status-code']); - $this->assertEquals('image/png', $file6['headers']['content-type']); - $this->assertNotEmpty($file6['body']); - - /** - * Test for FAILURE - */ - - return $data; - } - - /** - * @depends testGetFile - */ - public function testListFiles(array $data): array - { - /** - * Test for SUCCESS - */ - $files = $this->client->call(Client::METHOD_GET, '/storage/files', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals(200, $files['headers']['status-code']); - $this->assertGreaterThan(0, $files['body']['sum']); - $this->assertGreaterThan(0, count($files['body']['files'])); - - /** - * Test for FAILURE - */ - - return $data; - } - - /** - * @depends testListFiles - */ - public function testUpdateFile(array $data): array - { - /** - * Test for SUCCESS - */ - $file = $this->client->call(Client::METHOD_PUT, '/storage/files/' . $data['fileId'], array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'read' => ['role:all'], - 'write' => ['role:all'], - ]); - - $this->assertEquals(200, $file['headers']['status-code']); - $this->assertNotEmpty($file['body']['$id']); - $this->assertIsInt($file['body']['dateCreated']); - $this->assertEquals('logo.png', $file['body']['name']); - $this->assertEquals('image/png', $file['body']['mimeType']); - $this->assertEquals(47218, $file['body']['sizeOriginal']); - //$this->assertEquals(54944, $file['body']['sizeActual']); - //$this->assertEquals('gzip', $file['body']['algorithm']); - //$this->assertEquals('1', $file['body']['fileOpenSSLVersion']); - //$this->assertEquals('aes-128-gcm', $file['body']['fileOpenSSLCipher']); - //$this->assertNotEmpty($file['body']['fileOpenSSLTag']); - //$this->assertNotEmpty($file['body']['fileOpenSSLIV']); - $this->assertIsArray($file['body']['$read']); - $this->assertIsArray($file['body']['$write']); - $this->assertCount(1, $file['body']['$read']); - $this->assertCount(1, $file['body']['$write']); - - /** - * Test for FAILURE - */ - - return $data; - } - - /** - * @depends testUpdateFile - */ - public function testDeleteFile(array $data): array - { - /** - * Test for SUCCESS - */ - $file = $this->client->call(Client::METHOD_DELETE, '/storage/files/' . $data['fileId'], array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals(204, $file['headers']['status-code']); - $this->assertEmpty($file['body']); - - $file = $this->client->call(Client::METHOD_GET, '/storage/files/' . $data['fileId'], array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals(404, $file['headers']['status-code']); - - /** - * Test for FAILURE - */ - - return $data; - } }