mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-02 21:13:57 +00:00
Add TempUrl signature validation to local storage
Implemented local storage-based file handling with TempUrl signature validation for upload and retrieval. Added validation checks for parameters like max file size/count, expiration, and signature integrity. Included unit tests for TempUrl signature validation and adjusted configuration for local storage.
This commit is contained in:
@@ -12,9 +12,11 @@ declare(strict_types=1);
|
||||
namespace Chill\DocStoreBundle\Controller;
|
||||
|
||||
use Chill\DocStoreBundle\AsyncUpload\Driver\LocalStorage\StoredObjectManager;
|
||||
use Chill\DocStoreBundle\AsyncUpload\Driver\LocalStorage\TempUrlLocalStorageGenerator;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
@@ -26,6 +28,7 @@ final readonly class StoredObjectContentToLocalStorageController
|
||||
{
|
||||
public function __construct(
|
||||
private StoredObjectManager $storedObjectManager,
|
||||
private TempUrlLocalStorageGenerator $tempUrlLocalStorageGenerator,
|
||||
) {}
|
||||
|
||||
#[Route('/public/stored-object/post', name: 'chill_docstore_storedobject_post', methods: ['POST'])]
|
||||
@@ -34,14 +37,51 @@ final readonly class StoredObjectContentToLocalStorageController
|
||||
$prefix = $request->query->get('prefix', '');
|
||||
|
||||
if ('' === $prefix) {
|
||||
throw new BadRequestHttpException('prefix parameter is missing');
|
||||
throw new BadRequestHttpException('Prefix parameter is missing');
|
||||
}
|
||||
|
||||
if (0 === $maxFileSize = $request->request->getInt('max_file_size', 0)) {
|
||||
throw new BadRequestHttpException('Max file size is not set or equal to zero');
|
||||
}
|
||||
|
||||
if (1 !== $maxFileCount = $request->request->getInt('max_file_count', 0)) {
|
||||
throw new BadRequestHttpException('Max file count is not set or equal to zero');
|
||||
}
|
||||
|
||||
if (0 === $expiration = $request->request->getInt('expires', 0)) {
|
||||
throw new BadRequestHttpException('Expiration is not set or equal to zero');
|
||||
}
|
||||
|
||||
if ('' === $signature = $request->request->get('signature', '')) {
|
||||
throw new BadRequestHttpException('Signature is not set or is a blank string');
|
||||
}
|
||||
|
||||
if (!$this->tempUrlLocalStorageGenerator->validateSignaturePost($signature, $prefix, $expiration, $maxFileSize, $maxFileCount)) {
|
||||
throw new AccessDeniedHttpException('Invalid signature');
|
||||
}
|
||||
|
||||
$keyFiles = $request->files->keys();
|
||||
|
||||
if ($maxFileCount < count($keyFiles)) {
|
||||
throw new AccessDeniedHttpException('More files than max file count');
|
||||
}
|
||||
|
||||
if (0 === count($keyFiles)) {
|
||||
throw new BadRequestHttpException('Zero files given');
|
||||
}
|
||||
|
||||
foreach ($keyFiles as $keyFile) {
|
||||
/** @var UploadedFile $file */
|
||||
$file = $request->files->get($keyFile);
|
||||
|
||||
if ($maxFileSize < strlen($file->getContent())) {
|
||||
throw new AccessDeniedHttpException('File is too big');
|
||||
}
|
||||
|
||||
if (!str_starts_with((string) $keyFile, $prefix)) {
|
||||
throw new AccessDeniedHttpException('Filename does not start with signed prefix');
|
||||
}
|
||||
|
||||
$this->storedObjectManager->writeContent($keyFile, $file->getContent());
|
||||
}
|
||||
|
||||
@@ -51,19 +91,30 @@ final readonly class StoredObjectContentToLocalStorageController
|
||||
#[Route('/public/stored-object/operate', name: 'chill_docstore_stored_object_operate', methods: ['GET', 'HEAD'])]
|
||||
public function contentOperate(Request $request): Response
|
||||
{
|
||||
$objectName = $request->query->get('object_name', '');
|
||||
if ('' === $objectName = $request->query->get('object_name', '')) {
|
||||
throw new BadRequestHttpException('Object name parameter is missing');
|
||||
}
|
||||
|
||||
if ('' === $objectName) {
|
||||
throw new BadRequestHttpException('object name parameter is missing');
|
||||
if (0 === $expiration = $request->query->getInt('exp', 0)) {
|
||||
throw new BadRequestHttpException('Expiration is not set or equal to zero');
|
||||
}
|
||||
|
||||
if ('' === $signature = $request->query->get('sig', '')) {
|
||||
throw new BadRequestHttpException('Signature is not set or is a blank string');
|
||||
}
|
||||
|
||||
if (!$this->tempUrlLocalStorageGenerator->validateSignature($signature, strtoupper($request->getMethod()), $objectName, $expiration)) {
|
||||
throw new AccessDeniedHttpException('Invalid signature');
|
||||
}
|
||||
|
||||
if (!$this->storedObjectManager->existsContent($objectName)) {
|
||||
throw new NotFoundHttpException('object does not exists on disk');
|
||||
throw new NotFoundHttpException('Object does not exists on disk');
|
||||
}
|
||||
|
||||
return match ($request->getMethod()) {
|
||||
'GET' => new Response($this->storedObjectManager->readContent($objectName)),
|
||||
'HEAD' => new Response(''),
|
||||
default => throw new BadRequestHttpException('method not supported'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user