From 07f83fc57ebf3090775d3e5659c31444ae8f09a2 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Sun, 1 Dec 2019 19:57:42 +0100 Subject: [PATCH] more test cleanups --- Admin/Install/db.json | 10 +++++-- Controller/ApiController.php | 10 +++++-- Models/Media.php | 58 +++++++++++++++++++++++++++++------- Models/MediaMapper.php | 3 +- Models/UploadFile.php | 35 +++++++++++++++++++--- Models/UploadStatus.php | 1 + 6 files changed, 96 insertions(+), 21 deletions(-) diff --git a/Admin/Install/db.json b/Admin/Install/db.json index f658ecf..c1e2936 100644 --- a/Admin/Install/db.json +++ b/Admin/Install/db.json @@ -52,8 +52,14 @@ "type": "TINYINT", "null": false }, - "media_encryptionhash": { - "name": "media_encryptionhash", + "media_nonce": { + "name": "media_nonce", + "type": "VARCHAR(255)", + "null": true, + "default": null + }, + "media_password": { + "name": "media_password", "type": "VARCHAR(255)", "null": true, "default": null diff --git a/Controller/ApiController.php b/Controller/ApiController.php index 40f6261..52d751d 100644 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -83,7 +83,9 @@ final class ApiController extends Controller $request->getFiles(), $request->getHeader()->getAccount(), (string) ($request->getData('path') ?? __DIR__ . '/../../../Modules/Media/Files'), - (string) ($request->getData('virtualPath') ?? '/') + (string) ($request->getData('virtualPath') ?? '/'), + (string) ($request->getData('password') ?? ''), + (string) ($request->getData('encrypt') ?? '') ); $ids = []; @@ -109,7 +111,9 @@ final class ApiController extends Controller array $files, int $account, string $basePath = 'Modules/Media/Files', - string $virtualPath = '/' + string $virtualPath = '/', + string $password = '', + string $encryptionKey = '' ) : array { $mediaCreated = []; @@ -118,7 +122,7 @@ final class ApiController extends Controller $upload = new UploadFile(); $upload->setOutputDir(self::createMediaPath($basePath)); - $status = $upload->upload($files, $name); + $status = $upload->upload($files, $name, $encryptionKey); $mediaCreated = $this->createDbEntries($status, $account, $virtualPath); } diff --git a/Models/Media.php b/Models/Media.php index e2bf5fd..0c7fb2a 100644 --- a/Models/Media.php +++ b/Models/Media.php @@ -122,12 +122,20 @@ class Media implements \JsonSerializable protected string $descriptionRaw = ''; /** - * Media encryption hash. + * Media encryption nonce. * * @var null|string * @since 1.0.0 */ - protected ?string $encryptionHash = null; + protected ?string $nonce = null; + + /** + * Media password hash. + * + * @var null|string + * @since 1.0.0 + */ + protected ?string $password = null; /** * Constructor. @@ -182,17 +190,17 @@ class Media implements \JsonSerializable } /** - * Set encryption hash + * Set encryption nonce * - * @param null|string $encryptionHash Hash from encryption password + * @param null|string $nonce Nonce from encryption password * * @return void * * @since 1.0.0 */ - public function setEncryptionHash(?string $encryptionHash) : void + public function setNonce(?string $nonce) : void { - $this->encryptionHash = $encryptionHash; + $this->nonce = $nonce; } /** @@ -204,21 +212,49 @@ class Media implements \JsonSerializable */ public function isEncrypted() : bool { - return $this->encryptionHash !== null; + return $this->nonce !== null; } /** - * Compare hash with encryption hash of the media file + * Set encryption password * - * @param string $hash User hash + * @param null|string $password Password + * + * @return void + * + * @since 1.0.0 + */ + public function setPassword(?string $password) : void + { + $this->password = $password; + } + + /** + * Compare user password with password of the media file + * + * @param string $password User password * * @return bool * * @since 1.0.0 */ - public function compareEncryptionHash(string $hash) : bool + public function comparePassword(string $password) : bool { - return \hash_equals($this->encryptionHash, $hash); + return \password_verify($password, $this->password ?? ''); + } + + /** + * Compare nonce with encryption nonce of the media file + * + * @param string $nonce User nonce + * + * @return bool + * + * @since 1.0.0 + */ + public function compareNonce(string $nonce) : bool + { + return \hash_equals($this->nonce, $nonce); } /** diff --git a/Models/MediaMapper.php b/Models/MediaMapper.php index ca2d4a7..a138810 100644 --- a/Models/MediaMapper.php +++ b/Models/MediaMapper.php @@ -44,7 +44,8 @@ class MediaMapper extends DataMapperAbstract 'media_file' => ['name' => 'media_file', 'type' => 'string', 'internal' => 'path', 'autocomplete' => true], 'media_virtual' => ['name' => 'media_virtual', 'type' => 'string', 'internal' => 'virtualPath', 'autocomplete' => true], 'media_absolute' => ['name' => 'media_absolute', 'type' => 'bool', 'internal' => 'isAbsolute'], - 'media_encryptionhash' => ['name' => 'media_encryptionhash', 'type' => 'string', 'internal' => 'encryptionHash'], + 'media_nonce' => ['name' => 'media_nonce', 'type' => 'string', 'internal' => 'nonce'], + 'media_password' => ['name' => 'media_password', 'type' => 'string', 'internal' => 'password'], 'media_extension' => ['name' => 'media_extension', 'type' => 'string', 'internal' => 'extension'], 'media_size' => ['name' => 'media_size', 'type' => 'int', 'internal' => 'size'], 'media_created_by' => ['name' => 'media_created_by', 'type' => 'int', 'internal' => 'createdBy'], diff --git a/Models/UploadFile.php b/Models/UploadFile.php index 749775f..6770159 100644 --- a/Models/UploadFile.php +++ b/Models/UploadFile.php @@ -87,9 +87,10 @@ class UploadFile /** * Upload file to server. * - * @param array $files File data ($_FILE) - * @param string $name File name - * @param string $encoding Encoding used for uploaded file. Empty string will not convert file content. + * @param array $files File data ($_FILE) + * @param string $name File name + * @param string $encryptionKey Encryption key + * @param string $encoding Encoding used for uploaded file. Empty string will not convert file content. * * @return array * @@ -97,7 +98,7 @@ class UploadFile * * @since 1.0.0 */ - public function upload(array $files, string $name = '', string $encoding = 'UTF-8') : array + public function upload(array $files, string $name = '', string $encryptionKey = '', string $encoding = 'UTF-8') : array { $result = []; @@ -179,6 +180,32 @@ class UploadFile return $result; } + if ($encryptionKey !== '') { + $nonce = \sodium_randombytes_buf(24); + + $fpSource = \fopen($dest, 'r+'); + $fpEncoded = \fopen($dest . '.tmp', 'w'); + + if ($fpSource === false || $fpEncoded === false) { + $result[$key]['status'] = UploadStatus::NOT_ENCRYPTABLE; + + return $result; + } + + while (($buffer = \fgets($fpSource, 4096)) !== false) { + $encrypted = \sodium_crypto_secretbox($buffer, $nonce, $encryptionKey); + + \fwrite($fpEncoded, $encrypted); + } + + \fclose($fpSource); + \fclose($fpEncoded); + + \unlink($dest); + \rename($dest . '.tmp', $dest); + $result[$key]['nonce'] = $nonce; + } + /* if ($this->isInterlaced && \in_array($extension, FileUtils::IMAGE_EXTENSION)) { // todo: interlacing somehow messes up some images (tested with logo.png from assets) diff --git a/Models/UploadStatus.php b/Models/UploadStatus.php index f7adf95..6fff5ce 100644 --- a/Models/UploadStatus.php +++ b/Models/UploadStatus.php @@ -36,4 +36,5 @@ abstract class UploadStatus extends Enum public const NOT_UPLOADED = -7; public const NOT_MOVABLE = -8; public const FAILED_HASHING = -9; + public const NOT_ENCRYPTABLE = -10; }