diff options
author | Lukas Reschke <lukas@owncloud.com> | 2015-09-24 16:35:47 +0300 |
---|---|---|
committer | Lukas Reschke <lukas@owncloud.com> | 2015-09-24 16:35:47 +0300 |
commit | 6e9f11c70ff6624742b3d390204805cfb7673e9e (patch) | |
tree | 5ee42f4cc8b878af33d2d3a6478bbdb4da7b2d42 | |
parent | e6421754b61a4491329d0458b37311d5d9b73ea0 (diff) | |
parent | f67d0c2d78b4f28347727ca5384948bdb4e88f55 (diff) |
Merge pull request #361 from owncloud/secure-mimetypes
Secure mimetypes
22 files changed, 493 insertions, 217 deletions
diff --git a/appinfo/application.php b/appinfo/application.php index d9eb4614..67b1c63c 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -90,7 +90,6 @@ class Application extends App { $c->query('AppName'), $c->query('Request'), $c->query('ConfigService'), - $c->query('PreviewService'), $c->query('Logger') ); } @@ -101,7 +100,6 @@ class Application extends App { $c->query('AppName'), $c->query('Request'), $c->query('ConfigService'), - $c->query('PreviewService'), $c->query('Logger') ); } @@ -112,7 +110,6 @@ class Application extends App { $c->query('AppName'), $c->query('Request'), $c->query('ConfigService'), - $c->query('PreviewService'), $c->query('Logger') ); } @@ -165,6 +162,7 @@ class Application extends App { $c->query('AppName'), $c->query('Request'), $c->query('OCP\IURLGenerator'), + $c->query('ConfigService'), $c->query('ThumbnailService'), $c->query('PreviewService'), $c->query('DownloadService'), @@ -179,6 +177,7 @@ class Application extends App { $c->query('AppName'), $c->query('Request'), $c->query('OCP\IURLGenerator'), + $c->query('ConfigService'), $c->query('ThumbnailService'), $c->query('PreviewService'), $c->query('DownloadService'), @@ -193,6 +192,7 @@ class Application extends App { $c->query('AppName'), $c->query('Request'), $c->query('OCP\IURLGenerator'), + $c->query('ConfigService'), $c->query('ThumbnailService'), $c->query('PreviewService'), $c->query('DownloadService'), @@ -326,6 +326,7 @@ class Application extends App { $c->query('AppName'), $c->query('Environment'), $c->query('ConfigParser'), + $c->query('CustomPreviewManager'), $c->query('Logger') ); } diff --git a/controller/config.php b/controller/config.php index b46e4edf..45c7a48c 100644 --- a/controller/config.php +++ b/controller/config.php @@ -17,7 +17,6 @@ use OCP\ILogger; use OCP\AppFramework\Http; use OCA\Gallery\Service\ConfigService; -use OCA\Gallery\Service\PreviewService; /** * Trait Config @@ -31,10 +30,6 @@ trait Config { */ private $configService; /** - * @var PreviewService - */ - private $previewService; - /** * @var ILogger */ private $logger; @@ -55,7 +50,7 @@ trait Config { $nativeSvgSupport = $this->isNativeSvgActivated($features); $mediaTypes = - $this->previewService->getSupportedMediaTypes($extraMediaTypes, $nativeSvgSupport); + $this->configService->getSupportedMediaTypes($extraMediaTypes, $nativeSvgSupport); return ['features' => $features, 'mediatypes' => $mediaTypes]; } diff --git a/controller/configapicontroller.php b/controller/configapicontroller.php index 529ee8e7..9d180b8d 100644 --- a/controller/configapicontroller.php +++ b/controller/configapicontroller.php @@ -19,7 +19,6 @@ use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; use OCA\Gallery\Service\ConfigService; -use OCA\Gallery\Service\PreviewService; /** * Class ConfigApiController @@ -37,20 +36,17 @@ class ConfigApiController extends ApiController { * @param string $appName * @param IRequest $request * @param ConfigService $configService - * @param PreviewService $previewService * @param ILogger $logger */ public function __construct( $appName, IRequest $request, ConfigService $configService, - PreviewService $previewService, ILogger $logger ) { parent::__construct($appName, $request); $this->configService = $configService; - $this->previewService = $previewService; $this->logger = $logger; } diff --git a/controller/configcontroller.php b/controller/configcontroller.php index ba3cd05f..07e4a12c 100644 --- a/controller/configcontroller.php +++ b/controller/configcontroller.php @@ -19,7 +19,6 @@ use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCA\Gallery\Service\ConfigService; -use OCA\Gallery\Service\PreviewService; /** * Class ConfigController @@ -37,20 +36,17 @@ class ConfigController extends Controller { * @param string $appName * @param IRequest $request * @param ConfigService $configService - * @param PreviewService $previewService * @param ILogger $logger */ public function __construct( $appName, IRequest $request, ConfigService $configService, - PreviewService $previewService, ILogger $logger ) { parent::__construct($appName, $request); $this->configService = $configService; - $this->previewService = $previewService; $this->logger = $logger; } diff --git a/controller/files.php b/controller/files.php index 14372b38..1777523e 100644 --- a/controller/files.php +++ b/controller/files.php @@ -134,6 +134,7 @@ trait Files { private function getDownload($fileId, $filename) { /** @type File $file */ $file = $this->downloadService->getResourceFromId($fileId); + $this->configService->validateMimeType($file->getMimeType()); $download = $this->downloadService->downloadFile($file); if (is_null($filename)) { $filename = $file->getName(); diff --git a/controller/filesapicontroller.php b/controller/filesapicontroller.php index 53bbef64..057b38f0 100644 --- a/controller/filesapicontroller.php +++ b/controller/filesapicontroller.php @@ -127,6 +127,11 @@ class FilesApiController extends ApiController { return new RedirectResponse($url); } + // That's the only exception out of all the image media types + if ($download['mimetype'] === 'image/svg+xml') { + $download['mimetype'] = 'text/plain'; + } + return new ImageResponse($download); } diff --git a/controller/filescontroller.php b/controller/filescontroller.php index c64bc021..81269bba 100644 --- a/controller/filescontroller.php +++ b/controller/filescontroller.php @@ -126,6 +126,11 @@ class FilesController extends Controller { return $response; } + // That's the only exception out of all the image media types we serve + if ($download['mimetype'] === 'image/svg+xml') { + $download['mimetype'] = 'text/plain'; + } + return new ImageResponse($download); } diff --git a/controller/preview.php b/controller/preview.php index 886b4b42..6e0914c8 100644 --- a/controller/preview.php +++ b/controller/preview.php @@ -22,6 +22,7 @@ use OCP\AppFramework\Http; use OCA\Gallery\Service\ServiceException; use OCA\Gallery\Service\NotFoundServiceException; +use OCA\Gallery\Service\ConfigService; use OCA\Gallery\Service\ThumbnailService; use OCA\Gallery\Service\PreviewService; use OCA\Gallery\Service\DownloadService; @@ -37,6 +38,8 @@ trait Preview { /** @var IURLGenerator */ private $urlGenerator; + /** @var ConfigService */ + private $configService; /** @var ThumbnailService */ private $thumbnailService; /** @var PreviewService */ @@ -112,14 +115,14 @@ trait Preview { $fileId, $width, $height, $keepAspect = true, $animatedPreview = true, $base64Encode = false ) { /** @type File $file */ - $file = $this->getFile($fileId); + list($file, $status) = $this->getFile($fileId); try { if (!is_null($file)) { $data = $this->getPreviewData( $file, $animatedPreview, $width, $height, $keepAspect, $base64Encode ); } else { - $data = $this->getErrorData(Http::STATUS_NOT_FOUND); + $data = $this->getErrorData($status); } } catch (ServiceException $exception) { $data = $this->getExceptionData($exception); @@ -137,14 +140,17 @@ trait Preview { * @return array<File|int|null> */ private function getFile($fileId) { + $status = Http::STATUS_OK; try { /** @type File $file */ $file = $this->previewService->getResourceFromId($fileId); + $this->configService->validateMimeType($file->getMimeType()); } catch (ServiceException $exception) { $file = null; + $status = $this->getHttpStatusCode($exception); } - return $file; + return [$file, $status]; } /** diff --git a/controller/previewapicontroller.php b/controller/previewapicontroller.php index 9701c7fb..8ccaaa8a 100644 --- a/controller/previewapicontroller.php +++ b/controller/previewapicontroller.php @@ -22,6 +22,7 @@ use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; use OCA\Gallery\Http\ImageResponse; +use OCA\Gallery\Service\ConfigService; use OCA\Gallery\Service\ThumbnailService; use OCA\Gallery\Service\PreviewService; use OCA\Gallery\Service\DownloadService; @@ -45,6 +46,7 @@ class PreviewApiController extends ApiController { * @param string $appName * @param IRequest $request * @param IURLGenerator $urlGenerator + * @param ConfigService $configService * @param ThumbnailService $thumbnailService * @param PreviewService $previewService * @param DownloadService $downloadService @@ -55,6 +57,7 @@ class PreviewApiController extends ApiController { $appName, IRequest $request, IURLGenerator $urlGenerator, + ConfigService $configService, ThumbnailService $thumbnailService, PreviewService $previewService, DownloadService $downloadService, @@ -64,6 +67,7 @@ class PreviewApiController extends ApiController { parent::__construct($appName, $request); $this->urlGenerator = $urlGenerator; + $this->configService = $configService; $this->thumbnailService = $thumbnailService; $this->previewService = $previewService; $this->downloadService = $downloadService; @@ -113,10 +117,11 @@ class PreviewApiController extends ApiController { * @param int $fileId the ID of the file of which we need a large preview of * @param int $width * @param int $height + * @param bool $nativesvg This is a GET parameter, so no camelCase * * @return ImageResponse|Http\JSONResponse */ - public function getPreview($fileId, $width, $height) { + public function getPreview($fileId, $width, $height, $nativesvg = false) { /** @type File $file */ list($file, $preview, $status) = $this->getData($fileId, $width, $height); @@ -130,6 +135,11 @@ class PreviewApiController extends ApiController { } $preview['name'] = $file->getName(); + // That's the only exception out of all the image media types we serve + if ($preview['mimetype'] === 'image/svg+xml' && !$nativesvg) { + $preview['mimetype'] = 'text/plain'; + } + return new ImageResponse($preview, $status); } diff --git a/controller/previewcontroller.php b/controller/previewcontroller.php index c779e3fb..66982dff 100644 --- a/controller/previewcontroller.php +++ b/controller/previewcontroller.php @@ -22,6 +22,7 @@ use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; use OCA\Gallery\Http\ImageResponse; +use OCA\Gallery\Service\ConfigService; use OCA\Gallery\Service\ThumbnailService; use OCA\Gallery\Service\PreviewService; use OCA\Gallery\Service\DownloadService; @@ -45,6 +46,7 @@ class PreviewController extends Controller { * @param string $appName * @param IRequest $request * @param IURLGenerator $urlGenerator + * @param ConfigService $configService * @param ThumbnailService $thumbnailService * @param PreviewService $previewService * @param DownloadService $downloadService @@ -55,6 +57,7 @@ class PreviewController extends Controller { $appName, IRequest $request, IURLGenerator $urlGenerator, + ConfigService $configService, ThumbnailService $thumbnailService, PreviewService $previewService, DownloadService $downloadService, @@ -64,6 +67,7 @@ class PreviewController extends Controller { parent::__construct($appName, $request); $this->urlGenerator = $urlGenerator; + $this->configService = $configService; $this->thumbnailService = $thumbnailService; $this->previewService = $previewService; $this->downloadService = $downloadService; diff --git a/service/configservice.php b/service/configservice.php index ba123c06..0ee75200 100644 --- a/service/configservice.php +++ b/service/configservice.php @@ -18,6 +18,7 @@ use OCP\ILogger; use OCA\Gallery\Config\ConfigParser; use OCA\Gallery\Config\ConfigException; use OCA\Gallery\Environment\Environment; +use OCA\Gallery\Preview\Preview; /** * Finds configurations files and returns a configuration array @@ -45,6 +46,35 @@ class ConfigService extends FilesService { * @var ConfigParser */ private $configParser; + /** @var Preview */ + private $previewManager; + /** + * @todo This hard-coded array could be replaced by admin settings + * + * @var string[] + */ + private $baseMimeTypes = [ + 'image/png', + 'image/jpeg', + 'image/gif', + 'image/x-xbitmap', + 'image/bmp', + 'image/tiff', + 'image/x-dcraw', + 'application/x-photoshop', + 'application/illustrator', + 'application/postscript', + ]; + /** + * These types are useful for files preview in the files app, but + * not for the gallery side + * + * @var string[] + */ + private $slideshowMimeTypes = [ + 'application/font-sfnt', + 'application/x-font', + ]; /** * Constructor @@ -52,17 +82,20 @@ class ConfigService extends FilesService { * @param string $appName * @param Environment $environment * @param ConfigParser $configParser + * @param Preview $previewManager * @param ILogger $logger */ public function __construct( $appName, Environment $environment, ConfigParser $configParser, + Preview $previewManager, ILogger $logger ) { parent::__construct($appName, $environment, $logger); $this->configParser = $configParser; + $this->previewManager = $previewManager; } /** @@ -87,6 +120,36 @@ class ConfigService extends FilesService { } /** + * This builds and returns a list of all supported media types + * + * @todo Native SVG could be disabled via admin settings + * + * @param bool $extraMediaTypes + * @param bool $nativeSvgSupport + * + * @return string[] all supported media types + */ + public function getSupportedMediaTypes($extraMediaTypes, $nativeSvgSupport) { + $supportedMimes = []; + $wantedMimes = $this->baseMimeTypes; + if ($extraMediaTypes) { + $wantedMimes = array_merge($wantedMimes, $this->slideshowMimeTypes); + } + foreach ($wantedMimes as $wantedMime) { + // Let's see if a preview of files of that media type can be generated + if ($this->isMimeSupported($wantedMime)) { + // We store the media type + $supportedMimes[] = $wantedMime; + } + } + $supportedMimes = $this->addSvgSupport($supportedMimes, $nativeSvgSupport); + + //$this->logger->debug("Supported Mimes: {mimes}", ['mimes' => $supportedMimes]); + + return $supportedMimes; + } + + /** * Returns information about the currently selected folder * * * privacy setting @@ -121,6 +184,19 @@ class ConfigService extends FilesService { } /** + * Throws an exception if the media type of the file is not part of what the app allows + * + * @param $mimeType + * + * @throws ForbiddenServiceException + */ + public function validateMimeType($mimeType) { + if (!in_array($mimeType, $this->getSupportedMediaTypes(true, true))) { + throw new ForbiddenServiceException('Media type not allowed'); + } + } + + /** * Determines if we have a configuration file to work with * * @param Folder $rootFolder the virtual root folder @@ -132,6 +208,44 @@ class ConfigService extends FilesService { } /** + * Adds the SVG media type if it's not already there + * + * If it's enabled, but doesn't work, an exception will be raised when trying to generate a + * preview. If it's disabled, we support it via the browser's native support + * + * @param string[] $supportedMimes + * @param bool $nativeSvgSupport + * + * @return string[] + */ + private function addSvgSupport($supportedMimes, $nativeSvgSupport) { + if (!in_array('image/svg+xml', $supportedMimes) && $nativeSvgSupport) { + $supportedMimes[] = 'image/svg+xml'; + } + + return $supportedMimes; + } + + /** + * Returns true if the passed mime type is supported + * + * In case of a failure, we just return that the media type is not supported + * + * @param string $mimeType + * + * @return boolean + */ + private function isMimeSupported($mimeType = '*') { + try { + return $this->previewManager->isMimeSupported($mimeType); + } catch (\Exception $exception) { + unset($exception); + + return false; + } + } + + /** * Returns an album configuration array * * Goes through all the parent folders until either we're told the album is private or we've diff --git a/service/previewservice.php b/service/previewservice.php index 8edf48a1..27157e0f 100644 --- a/service/previewservice.php +++ b/service/previewservice.php @@ -29,33 +29,6 @@ class PreviewService extends Service { /** @var Preview */ private $previewManager; - /** - * @todo This hard-coded array could be replaced by admin settings - * - * @var string[] - */ - private $baseMimeTypes = [ - 'image/png', - 'image/jpeg', - 'image/gif', - 'image/x-xbitmap', - 'image/bmp', - 'image/tiff', - 'image/x-dcraw', - 'application/x-photoshop', - 'application/illustrator', - 'application/postscript', - ]; - /** - * These types are useful for files preview in the files app, but - * not for the gallery side - * - * @var string[] - */ - private $slideshowMimeTypes = [ - 'application/font-sfnt', - 'application/x-font', - ]; /** * Constructor @@ -77,36 +50,6 @@ class PreviewService extends Service { } /** - * This builds and returns a list of all supported media types - * - * @todo Native SVG could be disabled via admin settings - * - * @param bool $extraMediaTypes - * @param bool $nativeSvgSupport - * - * @return string[] all supported media types - */ - public function getSupportedMediaTypes($extraMediaTypes, $nativeSvgSupport) { - $supportedMimes = []; - $wantedMimes = $this->baseMimeTypes; - if ($extraMediaTypes) { - $wantedMimes = array_merge($wantedMimes, $this->slideshowMimeTypes); - } - foreach ($wantedMimes as $wantedMime) { - // Let's see if a preview of files of that media type can be generated - if ($this->isMimeSupported($wantedMime)) { - // We store the media type - $supportedMimes[] = $wantedMime; - } - } - $supportedMimes = $this->addSvgSupport($supportedMimes, $nativeSvgSupport); - - //$this->logger->debug("Supported Mimes: {mimes}", ['mimes' => $supportedMimes]); - - return $supportedMimes; - } - - /** * Decides if we should download the file instead of generating a preview * * @param File $file @@ -199,25 +142,6 @@ class PreviewService extends Service { } /** - * Adds the SVG media type if it's not already there - * - * If it's enabled, but doesn't work, an exception will be raised when trying to generate a - * preview. If it's disabled, we support it via the browser's native support - * - * @param string[] $supportedMimes - * @param bool $nativeSvgSupport - * - * @return string[] - */ - private function addSvgSupport($supportedMimes, $nativeSvgSupport) { - if (!in_array('image/svg+xml', $supportedMimes) && $nativeSvgSupport) { - $supportedMimes[] = 'image/svg+xml'; - } - - return $supportedMimes; - } - - /** * Returns true if the passed mime type is supported * * In case of a failure, we just return that the media type is not supported diff --git a/tests/unit/GalleryUnitTest.php b/tests/unit/GalleryUnitTest.php index b506e3f5..44b75913 100644 --- a/tests/unit/GalleryUnitTest.php +++ b/tests/unit/GalleryUnitTest.php @@ -13,6 +13,8 @@ namespace Test; use OCP\ILogger; +use OCP\Files\File; +use OCP\Files\Folder; use OCA\Gallery\Environment\Environment; use OCA\Gallery\Service\ServiceException; @@ -52,7 +54,7 @@ abstract class GalleryUnitTest extends \Test\TestCase { * * @param object $mockedObject * @param int $fileId - * @param \PHPUnit_Framework_MockObject_MockObject $answer + * @param File|Folder $answer */ protected function mockGetResourceFromId($mockedObject, $fileId, $answer) { $mockedObject->expects($this->once()) diff --git a/tests/unit/controller/ConfigApiControllerTest.php b/tests/unit/controller/ConfigApiControllerTest.php index 35c65ab8..c6221c14 100644 --- a/tests/unit/controller/ConfigApiControllerTest.php +++ b/tests/unit/controller/ConfigApiControllerTest.php @@ -27,7 +27,6 @@ class ConfigApiControllerTest extends ConfigControllerTest { $this->appName, $this->request, $this->configService, - $this->previewService, $this->logger ); } diff --git a/tests/unit/controller/ConfigControllerTest.php b/tests/unit/controller/ConfigControllerTest.php index 0d5932e6..95ecbbd9 100644 --- a/tests/unit/controller/ConfigControllerTest.php +++ b/tests/unit/controller/ConfigControllerTest.php @@ -77,7 +77,6 @@ class ConfigControllerTest extends \Test\TestCase { $this->appName, $this->request, $this->configService, - $this->previewService, $this->logger ); } @@ -202,7 +201,7 @@ class ConfigControllerTest extends \Test\TestCase { * @param $mimeTypes */ private function mockSupportedMediaTypes($slideshow, $nativeSvgSupport, $mimeTypes) { - $this->previewService->expects($this->any()) + $this->configService->expects($this->any()) ->method('getSupportedMediaTypes') ->with( $this->equalTo($slideshow), diff --git a/tests/unit/controller/ConfigPublicControllerTest.php b/tests/unit/controller/ConfigPublicControllerTest.php index dfd6e271..ea1e4e24 100644 --- a/tests/unit/controller/ConfigPublicControllerTest.php +++ b/tests/unit/controller/ConfigPublicControllerTest.php @@ -27,7 +27,6 @@ class ConfigPublicControllerTest extends ConfigControllerTest { $this->appName, $this->request, $this->configService, - $this->previewService, $this->logger ); } diff --git a/tests/unit/controller/FilesControllerTest.php b/tests/unit/controller/FilesControllerTest.php index 00b45734..a9e45860 100644 --- a/tests/unit/controller/FilesControllerTest.php +++ b/tests/unit/controller/FilesControllerTest.php @@ -15,6 +15,7 @@ namespace OCA\Gallery\Controller; use OCA\Gallery\Service\ServiceException; use OCP\IRequest; use OCP\IURLGenerator; +use OCP\Files\File; use OCP\ILogger; use OCP\AppFramework\IAppContainer; @@ -105,18 +106,35 @@ class FilesControllerTest extends \Test\GalleryUnitTest { ); } - public function testDownload() { - $fileId = 1234; - $filename = null; + /** + * @return array + */ + public function providesTestDownloadData() { + return [ + [1234, $this->mockJpgFile(1234), 'image/jpeg'], + [4567, $this->mockSvgFile(4567), 'text/plain'] + ]; + } - $download = $this->mockGetDownload($fileId, $filename); + /** + * @dataProvider providesTestDownloadData + * + * @param int $fileId + * @param File $file + * @param string $expectedMimeType + * + * @internal param string $type + */ + public function testDownload($fileId, $file, $expectedMimeType) { + $filename = null; + $download = $this->mockGetDownload($fileId, $file, $filename); /** @type ImageResponse $response */ $response = $this->controller->download($fileId, $filename); $this->assertEquals(Http::STATUS_OK, $response->getStatus()); $this->assertEquals( - $download['mimetype'] . '; charset=utf-8', $response->getHeaders()['Content-type'] + $expectedMimeType . '; charset=utf-8', $response->getHeaders()['Content-type'] ); $this->assertEquals($download['preview'], $response->render()); } @@ -262,9 +280,7 @@ class FilesControllerTest extends \Test\GalleryUnitTest { * * @return array */ - private function mockGetDownload($fileId, $filename) { - $file = $this->mockFile($fileId); - + private function mockGetDownload($fileId, $file, $filename) { $this->mockGetResourceFromId($this->downloadService, $fileId, $file); $download = $this->mockDownloadData($file, $filename); diff --git a/tests/unit/controller/PreviewApiControllerTest.php b/tests/unit/controller/PreviewApiControllerTest.php index 1029fa2f..914889d8 100644 --- a/tests/unit/controller/PreviewApiControllerTest.php +++ b/tests/unit/controller/PreviewApiControllerTest.php @@ -14,6 +14,12 @@ namespace OCA\Gallery\Controller; require_once __DIR__ . '/PreviewControllerTest.php'; +use OCP\Files\File; + +use OCP\AppFramework\Http; + +use OCA\Gallery\Http\ImageResponse; + /** * Class PreviewApiControllerTest * @@ -21,12 +27,16 @@ require_once __DIR__ . '/PreviewControllerTest.php'; */ class PreviewApiControllerTest extends PreviewControllerTest { + /** @var PreviewApiController */ + protected $controller; + public function setUp() { parent::setUp(); $this->controller = new PreviewApiController( $this->appName, $this->request, $this->urlGenerator, + $this->configService, $this->thumbnailService, $this->previewService, $this->downloadService, @@ -35,4 +45,45 @@ class PreviewApiControllerTest extends PreviewControllerTest { ); } + /** + * @return array + */ + public function providesTestDownloadData() { + return [ + [1234, $this->mockSvgFile(1234), true, 'image/svg+xml'], + [4567, $this->mockSvgFile(4567), false, 'text/plain'] + ]; + } + + /** + * @dataProvider providesTestDownloadData + * + * @param int $fileId + * @param File $file + * @param string $nativeSvg + * @param string $expectedMimeType + * + * @internal param string $type + */ + public function testGetPreviewOfSvg($fileId, $file, $nativeSvg, $expectedMimeType) { + $width = 1024; + $height = 768; + + /** @type File $file */ + $preview = $this->mockGetData( + $fileId, $file, $width, $height, $keepAspect = true, $animatedPreview = true, + $base64Encode = false, $previewRequired = false + ); + $preview['name'] = $file->getName(); + + /** @type ImageResponse $response */ + $response = $this->controller->getPreview($fileId, $width, $height, $nativeSvg); + + $this->assertEquals(Http::STATUS_OK, $response->getStatus()); + + $this->assertEquals( + $expectedMimeType . '; charset=utf-8', $response->getHeaders()['Content-type'] + ); + } + } diff --git a/tests/unit/controller/PreviewControllerTest.php b/tests/unit/controller/PreviewControllerTest.php index e3b0caca..d8f9f44b 100644 --- a/tests/unit/controller/PreviewControllerTest.php +++ b/tests/unit/controller/PreviewControllerTest.php @@ -23,6 +23,7 @@ use OCP\AppFramework\Http\JSONResponse; use OCA\Gallery\AppInfo\Application; use OCA\Gallery\Http\ImageResponse; +use OCA\Gallery\Service\ConfigService; use OCA\Gallery\Service\ThumbnailService; use OCA\Gallery\Service\PreviewService; use OCA\Gallery\Service\DownloadService; @@ -47,6 +48,8 @@ class PreviewControllerTest extends \Test\GalleryUnitTest { protected $controller; /** @var IURLGenerator */ protected $urlGenerator; + /** @var ConfigService */ + protected $configService; /** @var ThumbnailService */ protected $thumbnailService; /** @var PreviewService */ @@ -75,6 +78,9 @@ class PreviewControllerTest extends \Test\GalleryUnitTest { $this->urlGenerator = $this->getMockBuilder('\OCP\IURLGenerator') ->disableOriginalConstructor() ->getMock(); + $this->configService = $this->getMockBuilder('\OCA\Gallery\Service\ConfigService') + ->disableOriginalConstructor() + ->getMock(); $this->thumbnailService = $this->getMockBuilder('\OCA\Gallery\Service\ThumbnailService') ->disableOriginalConstructor() ->getMock(); @@ -94,6 +100,7 @@ class PreviewControllerTest extends \Test\GalleryUnitTest { $this->appName, $this->request, $this->urlGenerator, + $this->configService, $this->thumbnailService, $this->previewService, $this->downloadService, @@ -110,6 +117,9 @@ class PreviewControllerTest extends \Test\GalleryUnitTest { $scale = 2.5; $thumbnailId = 1234; + $file = $this->mockJpgFile($thumbnailId); + $this->mockGetResourceFromId($this->previewService, $thumbnailId, $file); + $this->controller->getThumbnails($thumbnailId, $square, $scale); } @@ -147,10 +157,11 @@ class PreviewControllerTest extends \Test\GalleryUnitTest { $base64Encode ]; $this->mockGetThumbnailSpecs($square, $scale, $thumbnailSpecs); - - list($file, $mockedPreview) = + /** @type File $file */ + $file = $this->mockJpgFile($thumbnailId); + $mockedPreview = $this->mockGetData( - $thumbnailId, $width, $height, $aspect, $animatedPreview, $base64Encode + $thumbnailId, $file, $width, $height, $aspect, $animatedPreview, $base64Encode ); $this->mockPreviewValidator($square, $base64Encode, $mockedPreview['preview']); @@ -221,7 +232,8 @@ class PreviewControllerTest extends \Test\GalleryUnitTest { $height = 768; /** @type File $file */ - list($file, $preview) = $this->mockGetData($fileId, $width, $height); + $file = $this->mockJpgFile($fileId); + $preview = $this->mockGetData($fileId, $file, $width, $height); $preview['name'] = $file->getName(); /** @type ImageResponse $response */ @@ -272,26 +284,34 @@ class PreviewControllerTest extends \Test\GalleryUnitTest { * Mocks Preview->getData * * @param int $fileId the ID of the file of which we need a large preview of + * @param File $file * @param int $width * @param int $height * @param bool $keepAspect * @param bool $animatedPreview * @param bool $base64Encode + * @param bool $previewRequired * * @return array */ - private function mockGetData( - $fileId, $width, $height, $keepAspect = true, $animatedPreview = true, $base64Encode = false + protected function mockGetData( + $fileId, $file, $width, $height, $keepAspect = true, $animatedPreview = true, + $base64Encode = false, $previewRequired = true ) { - $file = $this->mockJpgFile($fileId); $this->mockGetResourceFromId($this->previewService, $fileId, $file); - $this->mockIsPreviewRequired($file, $animatedPreview, true); - $previewData = $this->mockPreviewData($file); + $this->mockIsPreviewRequired($file, $animatedPreview, $previewRequired); + $previewData = $this->mockPreviewData($file, $previewRequired); - $this->mockCreatePreview($file, $width, $height, $keepAspect, $base64Encode, $previewData); + if ($previewRequired) { + $this->mockCreatePreview( + $file, $width, $height, $keepAspect, $base64Encode, $previewData + ); + } else { + $this->mockDownloadFile($file, $base64Encode, $previewData); + } - return [$file, $previewData]; + return $previewData; } /** @@ -389,14 +409,17 @@ class PreviewControllerTest extends \Test\GalleryUnitTest { } /** - * @param object|\PHPUnit_Framework_MockObject_MockObject $file + * @param File $file + * @param bool $previewRequired * - * @return array<string,mixed> + * @return array <string,mixed> */ - private function mockPreviewData($file) { + private function mockPreviewData($file, $previewRequired) { + $mimeType = $previewRequired ? 'image/png' : $file->getMimeType(); + $preview = [ 'preview' => $file->getContent(), // Not a real preview, but it's not important - 'mimetype' => 'image/png', //Most previews are PNGs + 'mimetype' => $mimeType, ]; return $preview; @@ -463,6 +486,21 @@ class PreviewControllerTest extends \Test\GalleryUnitTest { } /** + * @param $file + * @param $base64Encode + * @param $preview + */ + private function mockDownloadFile($file, $base64Encode, $preview) { + $this->downloadService->expects($this->once()) + ->method('downloadFile') + ->with( + $this->equalTo($file), + $this->equalTo($base64Encode) + ) + ->willReturn($preview); + } + + /** * @param $event * @param $data * @param $message diff --git a/tests/unit/controller/PreviewPublicControllerTest.php b/tests/unit/controller/PreviewPublicControllerTest.php index 17dbd514..70a2ed3b 100644 --- a/tests/unit/controller/PreviewPublicControllerTest.php +++ b/tests/unit/controller/PreviewPublicControllerTest.php @@ -27,6 +27,7 @@ class PreviewPublicControllerTest extends PreviewControllerTest { $this->appName, $this->request, $this->urlGenerator, + $this->configService, $this->thumbnailService, $this->previewService, $this->downloadService, diff --git a/tests/unit/service/ConfigServiceTest.php b/tests/unit/service/ConfigServiceTest.php index 1f8d9850..db772413 100644 --- a/tests/unit/service/ConfigServiceTest.php +++ b/tests/unit/service/ConfigServiceTest.php @@ -15,6 +15,8 @@ namespace OCA\Gallery\Service; use OCA\Gallery\Config\ConfigParser; use OCA\Gallery\Config\ConfigException; +use OCA\Gallery\Preview\Preview; + /** * Class ConfigServiceTest * @@ -26,6 +28,8 @@ class ConfigServiceTest extends \Test\GalleryUnitTest { protected $service; /** @var ConfigParser */ protected $configParser; + /** @var Preview */ + protected $previewManager; /** * Test set up @@ -36,14 +40,193 @@ class ConfigServiceTest extends \Test\GalleryUnitTest { $this->configParser = $this->getMockBuilder('\OCA\Gallery\Config\ConfigParser') ->disableOriginalConstructor() ->getMock(); + $this->previewManager = $this->getMockBuilder('\OCA\Gallery\Preview\Preview') + ->disableOriginalConstructor() + ->getMock(); $this->service = new ConfigService ( $this->appName, $this->environment, $this->configParser, + $this->previewManager, $this->logger ); } + public function providesGetSupportedMediaTypesData() { + $baseMimeTypes = [ + 'image/jpeg', + ]; + + $slideshowMimes = array_merge( + $baseMimeTypes, + [ + 'application/font-sfnt', + 'application/x-font', + ] + ); + + $baseMimeTypesWithSvg = array_merge( + $baseMimeTypes, + [ + 'image/svg+xml', + ] + ); + + $slideshowMimesWithSvg = array_merge( + $slideshowMimes, + [ + 'image/svg+xml', + ] + ); + + return [ + [$baseMimeTypes, false, false, $baseMimeTypes], + [$baseMimeTypes, false, true, $baseMimeTypesWithSvg], + [$baseMimeTypes, true, true, $slideshowMimesWithSvg], + [$baseMimeTypes, true, false, $slideshowMimes], + ]; + } + + /** + * @dataProvider providesGetSupportedMediaTypesData + * + * @param $baseMimeTypes + * @param $extraMediaTypes + * @param $nativeSvgSupport + * @param $expectedResult + */ + public function testGetSupportedMediaTypes( + $baseMimeTypes, $extraMediaTypes, $nativeSvgSupport, $expectedResult + ) { + + $this->assertSame( + $baseMimeTypes, self::invokePrivate($this->service, 'baseMimeTypes', [$baseMimeTypes]) + ); + + $this->mockIsMimeSupported($nativeSvgSupport); + + $response = $this->service->getSupportedMediaTypes($extraMediaTypes, $nativeSvgSupport); + + $this->assertSame($expectedResult, $response); + } + + public function testGetSupportedMediaTypesWithBrokenPreviewSystem() { + // We only support 1 media type: GIF + self::invokePrivate($this->service, 'baseMimeTypes', [['image/gif']]); + + // Unfortunately, the GIF preview is broken + $this->mockIsMimeSupportedWithBrokenSystem('image/gif'); + + $response = $this->service->getSupportedMediaTypes(false, false); + + // 1-1 = 0 + $this->assertEmpty($response); + } + + public function providesValidateMimeTypeData() { + return [ + ['image/png'], + ['image/jpeg'], + ['image/gif'], + ['application/postscript'], + ['application/x-font'] + ]; + } + + /** + * @dataProvider providesValidateMimeTypeData + * + * @param $mimeType + * + */ + public function testValidateMimeType($mimeType) { + $supportedMimeTypes = [ + 'image/png', + 'image/jpeg', + 'image/gif', + 'application/postscript', + 'application/x-font' + ]; + + $this->assertSame( + $supportedMimeTypes, + self::invokePrivate($this->service, 'baseMimeTypes', [$supportedMimeTypes]) + ); + $this->mockIsMimeSupported($nativeSvgSupport = true); + + $this->service->validateMimeType($mimeType); + } + + public function providesValidateMimeTypeWithForbiddenMimeData() { + return [ + ['text/plain'], + ['application/javascript'], + ['application/json'], + ['text/markdown'], + ['application/yaml'], + ['application/xml'], + ]; + } + + /** + * @dataProvider providesValidateMimeTypeWithForbiddenMimeData + * + * @param $mimeType + * + * @expectedException \OCA\Gallery\Service\ForbiddenServiceException + */ + public function testValidateMimeTypeWithForbiddenMime($mimeType) { + $supportedMimeTypes = [ + 'image/png', + 'image/jpeg', + 'image/gif', + 'image/x-xbitmap', + 'image/bmp', + 'application/postscript', + 'application/x-font' + ]; + + $this->assertSame( + $supportedMimeTypes, + self::invokePrivate($this->service, 'baseMimeTypes', [$supportedMimeTypes]) + ); + $this->mockIsMimeSupported($nativeSvgSupport = true); + + $this->service->validateMimeType($mimeType); + } + + public function providesAddSvgSupportData() { + $supportedMimes = [ + 'image/png', + 'image/jpeg', + 'image/gif' + ]; + + $supportedMimesWithSvg = array_merge($supportedMimes, ['image/svg+xml']); + + return [ + [$supportedMimes, true, $supportedMimesWithSvg], + [$supportedMimes, false, $supportedMimes], + [$supportedMimesWithSvg, true, $supportedMimesWithSvg], + [$supportedMimesWithSvg, false, $supportedMimesWithSvg], + ]; + } + + /** + * @dataProvider providesAddSvgSupportData + * + * @param array $supportedMimes + * @param bool $nativeSvgSupport + * @param array $expectedResult + */ + public function testAddSvgSupport($supportedMimes, $nativeSvgSupport, $expectedResult) { + $response = self::invokePrivate( + $this->service, 'addSvgSupport', [$supportedMimes, $nativeSvgSupport] + ); + + $this->assertSame($expectedResult, $response); + } + public function testBuildFolderConfigWithBrokenSetup() { $nodeId = 65965; $files = []; @@ -116,6 +299,29 @@ class ConfigServiceTest extends \Test\GalleryUnitTest { $this->assertSame($modifiedAlbumConfig, $response); } + private function mockIsMimeSupported($mimeSupported) { + $map = [ + ['image/png', true], + ['image/jpeg', true], + ['application/postscript', true], + ['application/font-sfnt', true], + ['application/x-font', true], + ['image/svg+xml', $mimeSupported], + ['image/gif', $mimeSupported] + ]; + $this->previewManager->method('isMimeSupported') + ->will( + $this->returnValueMap($map) + ); + } + + private function mockIsMimeSupportedWithBrokenSystem($mimeType) { + $this->previewManager->expects($this->once()) + ->method('isMimeSupported') + ->with($mimeType) + ->willThrowException(new \Exception('Boom')); + } + private function mockGetFolderConfigWithBrokenSetup( $folder, $configName, $config, $configItems, $level, $exception ) { diff --git a/tests/unit/service/PreviewServiceTest.php b/tests/unit/service/PreviewServiceTest.php index c9c28152..fa1c1e31 100644 --- a/tests/unit/service/PreviewServiceTest.php +++ b/tests/unit/service/PreviewServiceTest.php @@ -48,64 +48,6 @@ class PreviewServiceTest extends \Test\GalleryUnitTest { ); } - public function providesGetSupportedMediaTypesData() { - $baseMimeTypes = [ - 'image/jpeg', - ]; - - $slideshowMimes = array_merge( - $baseMimeTypes, - [ - 'application/font-sfnt', - 'application/x-font', - ] - ); - - $baseMimeTypesWithSvg = array_merge( - $baseMimeTypes, - [ - 'image/svg+xml', - ] - ); - - $slideshowMimesWithSvg = array_merge( - $slideshowMimes, - [ - 'image/svg+xml', - ] - ); - - return [ - [$baseMimeTypes, false, false, $baseMimeTypes], - [$baseMimeTypes, false, true, $baseMimeTypesWithSvg], - [$baseMimeTypes, true, true, $slideshowMimesWithSvg], - [$baseMimeTypes, true, false, $slideshowMimes], - ]; - } - - /** - * @dataProvider providesGetSupportedMediaTypesData - * - * @param $baseMimeTypes - * @param $extraMediaTypes - * @param $nativeSvgSupport - * @param $expectedResult - */ - public function testGetSupportedMediaTypes( - $baseMimeTypes, $extraMediaTypes, $nativeSvgSupport, $expectedResult - ) { - - $this->assertSame( - $baseMimeTypes, self::invokePrivate($this->service, 'baseMimeTypes', [$baseMimeTypes]) - ); - - $this->mockIsMimeSupported($nativeSvgSupport); - - $response = $this->service->getSupportedMediaTypes($extraMediaTypes, $nativeSvgSupport); - - $this->assertSame($expectedResult, $response); - } - public function providesIsPreviewRequiredData() { return [ [true], @@ -238,40 +180,6 @@ class PreviewServiceTest extends \Test\GalleryUnitTest { $this->service->previewValidator($square, $base64Encode); } - - public function providesAddSvgSupportData() { - $supportedMimes = [ - 'image/png', - 'image/jpeg', - 'image/gif' - ]; - - $supportedMimesWithSvg = array_merge($supportedMimes, ['image/svg+xml']); - - return [ - [$supportedMimes, true, $supportedMimesWithSvg], - [$supportedMimes, false, $supportedMimes], - [$supportedMimesWithSvg, true, $supportedMimesWithSvg], - [$supportedMimesWithSvg, false, $supportedMimesWithSvg], - ]; - } - - /** - * @dataProvider providesAddSvgSupportData - * - * @param array $supportedMimes - * @param bool $nativeSvgSupport - * @param array $expectedResult - */ - public function testAddSvgSupport($supportedMimes, $nativeSvgSupport, $expectedResult) { - $response = self::invokePrivate( - $this->service, 'addSvgSupport', [$supportedMimes, $nativeSvgSupport] - ); - - $this->assertSame($expectedResult, $response); - } - - private function mockIsMimeSupported($mimeSupported) { $map = [ ['image/jpeg', true], |