- | = $type->getId(); ?>
+ | = $type->id; ?>
| = $this->printHtml($type->name); ?>
| = $this->printHtml($type->getL11n()); ?>
diff --git a/Controller/ApiController.php b/Controller/ApiController.php
index e8a2cfd..3af3682 100755
--- a/Controller/ApiController.php
+++ b/Controller/ApiController.php
@@ -90,7 +90,7 @@ final class ApiController extends Controller
basePath: __DIR__ . '/../../../Modules/Media/Files' . \urldecode($request->getDataString('path') ?? ''),
virtualPath: \urldecode($request->getDataString('virtualpath') ?? ''),
password: $request->getDataString('password') ?? '',
- encryptionKey: $request->getDataString('encrypt') ?? '',
+ encryptionKey: $request->getDataString('encryption') ?? ($request->getDataBool('isencrypted') === true && !empty($_SERVER['OMS_PRIVATE_KEY_I'] ?? '') ? $_SERVER['OMS_PRIVATE_KEY_I'] : ''),
pathSettings: $request->getDataInt('pathsettings') ?? PathSettings::RANDOM_PATH, // IMPORTANT!!!
hasAccountRelation: $request->getDataBool('link_account') ?? false,
readContent: $request->getDataBool('parse_content') ?? false,
@@ -99,7 +99,7 @@ final class ApiController extends Controller
$ids = [];
foreach ($uploads as $file) {
- $ids[] = $file->getId();
+ $ids[] = $file->id;
// add media types
if (!empty($types = $request->getDataJson('types'))) {
@@ -123,7 +123,7 @@ final class ApiController extends Controller
$this->createModelRelation(
$request->header->account,
- $file->getId(),
+ $file->id,
$tId,
MediaMapper::class,
'types',
@@ -156,7 +156,7 @@ final class ApiController extends Controller
$this->createModelRelation(
$request->header->account,
- $file->getId(),
+ $file->id,
$tId,
MediaMapper::class,
'tags',
@@ -321,7 +321,9 @@ final class ApiController extends Controller
$virtualPath,
app: $hasAccountRelation ? $this->app : null,
readContent: $readContent,
- unit: $unit
+ unit: $unit,
+ password: $password,
+ isEncrypted: !empty($encryptionKey)
);
}
@@ -390,7 +392,9 @@ final class ApiController extends Controller
string $ip = '127.0.0.1',
ApplicationAbstract $app = null,
bool $readContent = false,
- int $unit = null
+ int $unit = null,
+ string $password = '',
+ bool $isEncrypted = false
) : Media
{
if (!isset($status['status']) || $status['status'] !== UploadStatus::OK) {
@@ -406,6 +410,8 @@ final class ApiController extends Controller
$media->extension = $status['extension'];
$media->unit = $unit;
$media->setVirtualPath($virtualPath);
+ $media->setPassword($password);
+ $media->isEncrypted = $isEncrypted;
if ($readContent && \is_file($media->getAbsolutePath())) {
$content = self::loadFileContent($media->getAbsolutePath(), $media->extension);
@@ -430,7 +436,7 @@ final class ApiController extends Controller
null, $media,
StringUtils::intHash(MediaMapper::class), 'Media-media-create',
self::NAME,
- (string) $media->getId(),
+ (string) $media->id,
'',
$ip
]
@@ -444,7 +450,7 @@ final class ApiController extends Controller
self::NAME,
self::NAME,
PermissionCategory::MEDIA,
- $media->getId(),
+ $media->id,
null,
PermissionType::READ | PermissionType::MODIFY | PermissionType::DELETE | PermissionType::PERMISSION
),
@@ -584,7 +590,7 @@ final class ApiController extends Controller
$media->setPath($request->getDataString('path') ?? $media->getPath());
$media->setVirtualPath(\urldecode($request->getDataString('virtualpath') ?? $media->getVirtualPath()));
- if ($media instanceof NullMedia
+ if ($media->id === 0
|| !$this->app->accountManager->get($request->header->account)->hasPermission(
PermissionType::MODIFY,
$this->app->unitId,
@@ -646,7 +652,7 @@ final class ApiController extends Controller
->where('name', \basename($request->getDataString('virtualpath') ?? ''))
->execute();
- $parentCollectionId = $parentCollection->getId();
+ $parentCollectionId = $parentCollection->id;
}
if (!$request->hasData('source')) {
@@ -656,13 +662,13 @@ final class ApiController extends Controller
->where('name', \basename($request->getDataString('child') ?? ''))
->execute();
- $request->setData('source', $child->getId());
+ $request->setData('source', $child->id);
}
$this->createModelRelation(
$request->header->account,
$parentCollectionId,
- $ref->getId(),
+ $ref->id,
CollectionMapper::class,
'sources',
'',
@@ -912,7 +918,7 @@ final class ApiController extends Controller
/** @var Collection $parentCollection */
$parentCollection = CollectionMapper::getParentCollection($temp)->execute();
- if ($parentCollection->getId() > 0) {
+ if ($parentCollection->id > 0) {
break;
}
@@ -930,8 +936,8 @@ final class ApiController extends Controller
$this->createModel($account, $childCollection, CollectionMapper::class, 'collection', '127.0.0.1');
$this->createModelRelation(
$account,
- $parentCollection->getId(),
- $childCollection->getId(),
+ $parentCollection->id,
+ $childCollection->id,
CollectionMapper::class,
'sources',
'',
@@ -1011,10 +1017,12 @@ final class ApiController extends Controller
$virtualPath,
$request->getOrigin(),
$this->app,
- unit: $request->getDataInt('unit')
+ unit: $request->getDataInt('unit'),
+ password: $request->getDataString('password') ?? '',
+ isEncrypted: $request->getDataBool('isencrypted') ?? $request->hasData('encryption')
);
- $ids[] = $created->getId();
+ $ids[] = $created->id;
}
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Media', 'Media successfully created.', $ids);
@@ -1057,15 +1065,16 @@ final class ApiController extends Controller
}
}
- if (!($media instanceof NullMedia)) {
- if ($request->header->account !== $media->createdBy->getId()
+ if ($media->id > 0) {
+ if (!($data['ignorePermission'] ?? false)
+ && $request->header->account !== $media->createdBy->id
&& !$this->app->accountManager->get($request->header->account)->hasPermission(
PermissionType::READ,
$this->app->unitId,
$this->app->appId,
self::NAME,
PermissionCategory::MEDIA,
- $media->getId()
+ $media->id
)
) {
$this->fillJsonResponse($request, $response, NotificationLevel::HIDDEN, '', '', []);
@@ -1095,7 +1104,7 @@ final class ApiController extends Controller
}
if ($media->hasPassword()
- && !$media->comparePassword((string) $request->getData('password'))
+ && !$media->comparePassword($request->getDataString('password'))
) {
$view = new View($this->app->l11nManager, $request, $response);
$view->setTemplate('/Modules/Media/Theme/Api/invalidPassword');
@@ -1103,6 +1112,17 @@ final class ApiController extends Controller
return;
}
+ if ($media->isEncrypted) {
+ $media = $this->prepareEncryptedMedia($media, $request);
+
+ if ($media->id === 0) {
+ $this->fillJsonResponse($request, $response, NotificationLevel::ERROR, 'Media', 'Media could not be exported. Please try again.', []);
+ $response->header->status = RequestStatusCode::R_500;
+
+ return;
+ }
+ }
+
$this->setMediaResponseHeader($media, $request, $response);
$view = $this->createView($media, $request, $response);
$view->setData('path', __DIR__ . '/../../../');
@@ -1110,6 +1130,38 @@ final class ApiController extends Controller
$response->set('export', $view);
}
+ private function prepareEncryptedMedia(Media $media, RequestAbstract $request) : Media
+ {
+ $path = '';
+ $absolutePath = '';
+
+ $counter = 0;
+ do {
+ $randomName = \sha1(\random_bytes(32));
+
+ $path = '../../../Temp/' . $randomName . '.' . $media->getExtension();
+ $absolutePath = __DIR__ . '/' . $path;
+ } while(!\is_file($absolutePath) && $counter < 1000);
+
+ if ($counter >= 1000) {
+ return new NullMedia();
+ }
+
+ $encryptionKey = $request->getDataBool('isencrypted') === true && !empty($_SESSION['OMS_PRIVATE_KEY_I'] ?? '')
+ ? $_SESSION['OMS_PRIVATE_KEY_I']
+ : $request->getDataString('encrpkey') ?? '';
+
+ $decrypted = $media->decrypt($encryptionKey, $absolutePath);
+
+ if (!$decrypted) {
+ return new NullMedia();
+ }
+
+ $media->path = $media->isAbsolute ? $absolutePath : $path;
+
+ return $media;
+ }
+
/**
* Routing end-point for application behaviour.
*
diff --git a/Controller/BackendController.php b/Controller/BackendController.php
index afb7d80..193679b 100755
--- a/Controller/BackendController.php
+++ b/Controller/BackendController.php
@@ -124,7 +124,7 @@ final class BackendController extends Controller
$collection = $collectionMapper->execute();
- if ((\is_array($collection) || $collection instanceof NullCollection) && \is_dir(__DIR__ . '/../Files' . $path)) {
+ if ((\is_array($collection) || $collection->id === 0) && \is_dir(__DIR__ . '/../Files' . $path)) {
$collection = new Collection();
$collection->name = \basename($path);
$collection->setVirtualPath(\dirname($path));
@@ -132,11 +132,11 @@ final class BackendController extends Controller
$collection->isAbsolute = false;
}
- if ($collection instanceof Collection && !($collection instanceof NullCollection)) {
+ if ($collection instanceof Collection && $collection->id > 0) {
$collectionSources = $collection->getSources();
foreach ($collectionSources as $source) {
foreach ($media as $obj) {
- if ($obj->getId() === $source->getId()) {
+ if ($obj->id === $source->id) {
continue 2;
}
}
@@ -214,6 +214,7 @@ final class BackendController extends Controller
if ($id === 0) {
$path = \urldecode($request->getDataString('path') ?? '');
$media = new NullMedia();
+
if (\is_file(__DIR__ . '/../Files' . $path)) {
$name = \explode('.', \basename($path));
@@ -236,14 +237,6 @@ final class BackendController extends Controller
->where('tags/title/language', $request->getLanguage())
->execute();
- if ($media->hasPassword()
- && !$media->comparePassword((string) $request->getData('password'))
- ) {
- $view->setTemplate('/Modules/Media/Theme/Backend/Components/Media/invalidPassword');
-
- return $view;
- }
-
if ($media->class === MediaClass::COLLECTION) {
/** @var \Modules\Media\Models\Media[] $files */
$files = MediaMapper::getByVirtualPath(
@@ -272,7 +265,7 @@ final class BackendController extends Controller
->with('tags')
->with('tags/title')
->with('content')
- ->where('id', $media->source?->getId() ?? 0)
+ ->where('id', $media->source?->id ?? 0)
->where('tags/title/language', $request->getLanguage())
->execute();
@@ -311,6 +304,16 @@ final class BackendController extends Controller
return $view;
}
+ if ($media->isEncrypted) {
+ $media = $this->app->moduleManager->get('Media', 'Api')->prepareEncryptedMedia($media, $request);
+
+ if ($media->id === 0) {
+ $view->setTemplate('/Modules/Media/Theme/Backend/Components/Media/invalidPassword');
+
+ return $view;
+ }
+ }
+
switch (\strtolower($media->extension)) {
case 'pdf':
$view->setTemplate('/Modules/Media/Theme/Backend/Components/Media/pdf');
@@ -395,9 +398,7 @@ final class BackendController extends Controller
$id = $request->getDataString('id') ?? '';
$settings = SettingMapper::getAll()->where('module', $id)->execute();
- if (!($settings instanceof NullSetting)) {
- $view->setData('settings', !\is_array($settings) ? [$settings] : $settings);
- }
+ $view->setData('settings', $settings);
$types = MediaTypeMapper::getAll()->with('title')->where('title/language', $response->getLanguage())->execute();
$view->setData('types', $types);
@@ -434,7 +435,7 @@ final class BackendController extends Controller
$view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1007501001, $request, $response));
$view->addData('type', $type);
- $l11n = MediaTypeL11nMapper::getAll()->where('type', $type->getId())->execute();
+ $l11n = MediaTypeL11nMapper::getAll()->where('type', $type->id)->execute();
$view->addData('l11n', $l11n);
return $view;
diff --git a/Models/Media.php b/Models/Media.php
index 98c221c..4245607 100755
--- a/Models/Media.php
+++ b/Models/Media.php
@@ -18,6 +18,7 @@ use Modules\Admin\Models\Account;
use Modules\Admin\Models\NullAccount;
use Modules\Tag\Models\NullTag;
use Modules\Tag\Models\Tag;
+use phpOMS\Security\EncryptionHelper;
/**
* Media class.
@@ -35,7 +36,7 @@ class Media implements \JsonSerializable
* @var int
* @since 1.0.0
*/
- protected int $id = 0;
+ public int $id = 0;
/**
* Name.
@@ -99,7 +100,7 @@ class Media implements \JsonSerializable
* @var string
* @since 1.0.0
*/
- protected string $path = '';
+ public string $path = '';
/**
* Virtual path.
@@ -107,7 +108,7 @@ class Media implements \JsonSerializable
* @var string
* @since 1.0.0
*/
- protected string $virtualPath = '/';
+ public string $virtualPath = '/';
/**
* Is path absolute?
@@ -150,12 +151,12 @@ class Media implements \JsonSerializable
public ?Media $source = null;
/**
- * Media encryption nonce.
+ * Is encrypted.
*
- * @var null|string
+ * @var bool
* @since 1.0.0
*/
- protected ?string $nonce = null;
+ public bool $isEncrypted = false;
/**
* Media password hash.
@@ -163,7 +164,7 @@ class Media implements \JsonSerializable
* @var null|string
* @since 1.0.0
*/
- protected ?string $password = null;
+ public ?string $password = null;
/**
* Media is hidden.
@@ -195,7 +196,7 @@ class Media implements \JsonSerializable
* @var Tag[]
* @since 1.0.0
*/
- protected array $tags = [];
+ public array $tags = [];
/**
* Language.
@@ -203,7 +204,7 @@ class Media implements \JsonSerializable
* @var null|string
* @since 1.0.0
*/
- protected ?string $language = null;
+ public ?string $language = null;
/**
* Country.
@@ -211,7 +212,7 @@ class Media implements \JsonSerializable
* @var null|string
* @since 1.0.0
*/
- protected ?string $country = null;
+ public ?string $country = null;
/**
* Constructor.
@@ -237,57 +238,31 @@ class Media implements \JsonSerializable
/**
* Encrypt the media file
*
- * @param string $password Password to encrypt the file with
+ * @param string $key Password to encrypt the file with
* @param null|string $outputPath Output path of the encryption (null = replace file)
*
- * @return string
- *
- * @since 1.0.0
- */
- public function encrypt(string $password, string $outputPath = null) : string
- {
- return '';
- }
-
- /**
- * Decrypt the media file
- *
- * @param string $password Password to encrypt the file with
- * @param null|string $outputPath Output path of the encryption (null = replace file)
- *
- * @return string
- *
- * @since 1.0.0
- */
- public function decrypt(string $password, string $outputPath = null) : string
- {
- return '';
- }
-
- /**
- * Set encryption nonce
- *
- * @param null|string $nonce Nonce from encryption password
- *
- * @return void
- *
- * @since 1.0.0
- */
- public function setNonce(?string $nonce) : void
- {
- $this->nonce = $nonce;
- }
-
- /**
- * Is media file encrypted?
- *
* @return bool
*
* @since 1.0.0
*/
- public function isEncrypted() : bool
+ public function encrypt(string $key, string $outputPath = null) : bool
{
- return $this->nonce !== null;
+ return EncryptionHelper::encryptFile($this->getAbsolutePath(), $outputPath, $key);
+ }
+
+ /**
+ * Decrypt the media file
+ *
+ * @param string $key Password to encrypt the file with
+ * @param null|string $outputPath Output path of the encryption (null = replace file)
+ *
+ * @return bool
+ *
+ * @since 1.0.0
+ */
+ public function decrypt(string $key, string $outputPath = null) : bool
+ {
+ return EncryptionHelper::decryptFile($this->getAbsolutePath(), $outputPath, $key);
}
/**
@@ -332,20 +307,6 @@ class Media implements \JsonSerializable
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 $this->nonce === null ? false : \hash_equals($this->nonce, $nonce);
- }
-
/**
* Get the media path
*
@@ -358,6 +319,22 @@ class Media implements \JsonSerializable
return $this->isAbsolute ? $this->path : \ltrim($this->path, '\\/');
}
+ public function getFileName() : string
+ {
+ return \basename($this->path);
+ }
+
+ public function getExtension() : string
+ {
+ $pos = \strrpos('.', $this->path);
+
+ if ($pos === false) {
+ return '';
+ }
+
+ return \substr($this->path, $pos + 1);
+ }
+
/**
* Get the absolute media path
*
diff --git a/Models/MediaContent.php b/Models/MediaContent.php
index f2ea857..f945d32 100755
--- a/Models/MediaContent.php
+++ b/Models/MediaContent.php
@@ -30,7 +30,7 @@ class MediaContent implements \JsonSerializable
* @var int
* @since 1.0.0
*/
- protected int $id = 0;
+ public int $id = 0;
public string $content = '';
diff --git a/Models/MediaMapper.php b/Models/MediaMapper.php
index ba61f52..ddaf4f5 100755
--- a/Models/MediaMapper.php
+++ b/Models/MediaMapper.php
@@ -49,7 +49,7 @@ class MediaMapper extends DataMapperFactory
'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_nonce' => ['name' => 'media_nonce', 'type' => 'string', 'internal' => 'nonce'],
+ 'media_encrypted' => ['name' => 'media_encrypted', 'type' => 'bool', 'internal' => 'isEncrypted'],
'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'],
diff --git a/Models/MediaType.php b/Models/MediaType.php
index a290c59..684828d 100755
--- a/Models/MediaType.php
+++ b/Models/MediaType.php
@@ -33,7 +33,7 @@ class MediaType implements \JsonSerializable
* @var int
* @since 1.0.0
*/
- protected int $id = 0;
+ public int $id = 0;
/**
* Name.
diff --git a/Models/UploadFile.php b/Models/UploadFile.php
index 7bd5502..3a846ca 100755
--- a/Models/UploadFile.php
+++ b/Models/UploadFile.php
@@ -17,6 +17,7 @@ declare(strict_types=1);
namespace Modules\Media\Models;
use phpOMS\Log\FileLogger;
+use phpOMS\Security\EncryptionHelper;
use phpOMS\System\File\Local\Directory;
use phpOMS\System\File\Local\File;
@@ -202,29 +203,13 @@ class UploadFile
}
if ($encryptionKey !== '') {
- $nonce = \sodium_randombytes_buf(24);
+ $isEncrypted = EncryptionHelper::encryptFile($dest, $dest, $encryptionKey);
- $fpSource = \fopen($dest, 'r+');
- $fpEncoded = \fopen($dest . '.tmp', 'w');
-
- if ($fpSource === false || $fpEncoded === false) {
+ if (!$isEncrypted) {
$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;
}
/*
diff --git a/Theme/Backend/Components/InlinePreview/BaseView.php b/Theme/Backend/Components/InlinePreview/BaseView.php
index 041e088..6ad0516 100755
--- a/Theme/Backend/Components/InlinePreview/BaseView.php
+++ b/Theme/Backend/Components/InlinePreview/BaseView.php
@@ -68,7 +68,7 @@ class BaseView extends View
* @var bool
* @since 1.0.0
*/
- private bool $isRequired = false;
+ public bool $isRequired = false;
/**
* {@inheritdoc}
diff --git a/Theme/Backend/Components/InlinePreview/inline-preview.tpl.php b/Theme/Backend/Components/InlinePreview/inline-preview.tpl.php
index b28d136..6eb154e 100755
--- a/Theme/Backend/Components/InlinePreview/inline-preview.tpl.php
+++ b/Theme/Backend/Components/InlinePreview/inline-preview.tpl.php
@@ -1,10 +1,10 @@
-
-
-
+
- |