Skip to content

Commit

Permalink
change voter and file_type mapping #2222
Browse files Browse the repository at this point in the history
  • Loading branch information
hmeneuvrier committed Feb 16, 2024
1 parent 17a5986 commit b0bf8b2
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 61 deletions.
13 changes: 9 additions & 4 deletions src/Controller/Back/SignalementFileController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use App\Entity\Suivi;
use App\Entity\User;
use App\Factory\SuiviFactory;
use App\Manager\FileManager;
use App\Messenger\Message\PdfExportMessage;
use App\Repository\FileRepository;
use App\Service\Signalement\SignalementFileProcessor;
Expand Down Expand Up @@ -121,10 +122,14 @@ public function deleteFileSignalement(
UploadHandlerService $uploadHandlerService,
EntityManagerInterface $entityManager,
SuiviFactory $suiviFactory,
FileManager $fileManager,
): JsonResponse {
$this->denyAccessUnlessGranted('FILE_DELETE', $signalement);
if ($this->isCsrfTokenValid('signalement_delete_file_'.$signalement->getId(), $request->get('_token'))) {
if ($uploadHandlerService->deleteSignalementFile($signalement, $type, $filename, $fileRepository)) {
$file = $fileManager->getFileFromSignalement($signalement, $type, $filename);
$this->denyAccessUnlessGranted('FILE_DELETE', $file);
if (null !== $file
&& $this->isCsrfTokenValid('signalement_delete_file_'.$signalement->getId(), $request->get('_token'))
) {
if ($uploadHandlerService->deleteSignalementFile($file, $fileRepository)) {
$suivi = $suiviFactory->createInstanceFrom($this->getUser(), $signalement);
/** @var User $user */
$user = $this->getUser();
Expand Down Expand Up @@ -157,7 +162,6 @@ public function editFileSignalement(
FileRepository $fileRepository,
EntityManagerInterface $entityManager,
): RedirectResponse {
$this->denyAccessUnlessGranted('FILE_EDIT', $signalement);
if ($this->isCsrfTokenValid('signalement_edit_file_'.$signalement->getId(), $request->get('_token'))) {
$file = $fileRepository->findOneBy(
[
Expand All @@ -166,6 +170,7 @@ public function editFileSignalement(
]
);
if (null !== $file) {
$this->denyAccessUnlessGranted('FILE_EDIT', $file);
$documentType = DocumentType::tryFrom($request->get('documentType'));
if (null !== $documentType) {
$file->setDocumentType($documentType);
Expand Down
5 changes: 3 additions & 2 deletions src/Entity/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,11 @@ public function isUsagerFile(): ?bool

public function isPartnerFile(): ?bool
{
return $this->uploadedBy->isSuperAdmin()
return null !== $this->uploadedBy
&& ($this->uploadedBy->isSuperAdmin()
|| $this->uploadedBy->isTerritoryAdmin()
|| $this->uploadedBy->isPartnerAdmin()
|| $this->uploadedBy->isUserPartner();
|| $this->uploadedBy->isUserPartner());
}

public function getSignalement(): ?Signalement
Expand Down
19 changes: 16 additions & 3 deletions src/Factory/FileFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\Entity\Intervention;
use App\Entity\Signalement;
use App\Entity\User;
use App\Service\ImageManipulationHandler;
use App\Service\Signalement\SignalementDocumentTypeMapper;

class FileFactory
Expand Down Expand Up @@ -75,9 +76,7 @@ public function createFromFileArray(
return $this->createInstanceFrom(
filename: $file['file'],
title: $file['titre'],
type: 'pdf' === pathinfo($file['file'], \PATHINFO_EXTENSION)
? File::FILE_TYPE_DOCUMENT
: File::FILE_TYPE_PHOTO,
type: $this->getFileType($file),
signalement: $signalement,
user: $user,
intervention: $intervention,
Expand All @@ -86,4 +85,18 @@ public function createFromFileArray(
description: $fileDescription,
);
}

private function getFileType(array $file)
{
if ('photos' === $file['key']
&& \in_array(
strtolower(pathinfo($file['file'], \PATHINFO_EXTENSION)),
ImageManipulationHandler::IMAGE_EXTENSION
)
) {
return File::FILE_TYPE_PHOTO;
}

return File::FILE_TYPE_DOCUMENT;
}
}
19 changes: 19 additions & 0 deletions src/Manager/FileManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,23 @@ public function updateSignalementFilesUser(
}
}
}

public function getFileFromSignalement(
Signalement $signalement,
string $type,
string $filename
): ?File {
$fileType = 'documents' === $type ? File::FILE_TYPE_DOCUMENT : File::FILE_TYPE_PHOTO;
$fileCollection = $signalement->getFiles()->filter(
function (File $file) use ($fileType, $filename) {
return $fileType === $file->getFileType()
&& $filename === $file->getFilename();
}
);
if (!$fileCollection->isEmpty()) {
return $fileCollection->current();
}

return null;
}
}
81 changes: 62 additions & 19 deletions src/Security/Voter/FileVoter.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
namespace App\Security\Voter;

use App\Entity\Affectation;
use App\Entity\File;
use App\Entity\Signalement;
use App\Entity\User;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\User\UserInterface;
Expand All @@ -16,25 +18,27 @@ class FileVoter extends Voter
public const CREATE = 'FILE_CREATE';
public const EDIT = 'FILE_EDIT';

public function __construct(private ParameterBagInterface $parameterBag)
{
}

protected function supports(string $attribute, $subject): bool
{
return \in_array($attribute, [self::DELETE, self::VIEW, self::CREATE, self::EDIT])
&& ($subject instanceof Signalement || 'boolean' === \gettype($subject));
&& ($subject instanceof Signalement || $subject instanceof File || 'boolean' === \gettype($subject));
}

protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
/** @var User $user */
$user = $token->getUser();
if ($user instanceof UserInterface) {
if ($user->isSuperAdmin() || (
$user->isTerritoryAdmin() &&
$subject instanceof Signalement &&
$subject->getTerritory() === $user->getTerritory()
)
) {
return true;
}
if (!$user instanceof UserInterface || (!$subject instanceof Signalement && !$subject instanceof File)) {
return false;
}
if (self::DELETE !== $attribute &&
($user->isSuperAdmin() || ($user->isTerritoryAdmin() && $this->isOnUserTerritory($subject, $user)))
) {
return true;
}

return match ($attribute) {
Expand All @@ -46,24 +50,43 @@ protected function voteOnAttribute(string $attribute, $subject, TokenInterface $
};
}

private function canEdit(Signalement $signalement, User $user): bool
private function canEdit(File $file, User $user): bool
{
return $this->canCreate($file->getSignalement(), $user)
&& $this->parameterBag->get('feature_new_form')
&& $this->parameterBag->get('feature_documents_enable')
&& $this->isFileUploadedByUser($file, $user);
}

private function canDelete(File $file, User $user): bool
{
return $this->canCreate($file->getSignalement(), $user)
&& (
$this->isFileUploadedByUser($file, $user)
||
$this->isPartnerFileDeletableByAdmin($file, $user)
);
}

private function isPartnerFileDeletableByAdmin(File $file, User $user): bool
{
return $this->canCreate($signalement, $user);
return $file->isPartnerFile() &&
(
$user->isSuperAdmin() ||
($user->isTerritoryAdmin() && $this->isOnUserTerritory($file, $user))
)
;
}

private function canDelete(Signalement $signalement, User $user): bool
private function isFileUploadedByUser(File $file, User $user): bool
{
return $this->canCreate($signalement, $user);
return null !== $file->getUploadedBy() && $file->getUploadedBy() === $user;
}

private function canCreate(Signalement $signalement, User $user): bool
{
return Signalement::STATUS_ACTIVE === $signalement->getStatut()
&& $signalement->getAffectations()->filter(
function (Affectation $affectation) use ($user) {
return $affectation->getPartner()->getId() === $user->getPartner()->getId();
}
)->count() > 0;
&& $this->checkSignalementPermission($signalement, $user);
}

private function canView(bool|Signalement $subject, ?User $user = null): bool
Expand All @@ -76,11 +99,31 @@ private function checkSignalementPermission(Signalement $signalement, ?User $use
if (null === $user) {
return false;
}
if ($user->isSuperAdmin() || ($user->isTerritoryAdmin() && $this->isOnUserTerritory($signalement, $user))) {
return true;
}

return $signalement->getAffectations()->filter(
function (Affectation $affectation) use ($user) {
return $affectation->getPartner()->getId() === $user->getPartner()->getId();
}
)->count() > 0;
}

private function isOnUserTerritory(File|Signalement $subject, User $user): bool
{
if (
(
$subject instanceof Signalement && $subject->getTerritory() === $user->getTerritory()
)
||
(
$subject instanceof File && $subject->getSignalement()->getTerritory() === $user->getTerritory()
)
) {
return true;
}

return false;
}
}
8 changes: 7 additions & 1 deletion src/Service/ImageManipulationHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ class ImageManipulationHandler
'image/png',
'image/gif',
];
public const IMAGE_EXTENSION = [
'jpeg',
'jpg',
'png',
'gif',
];

private const DEFAULT_SIZE_RESIZE = 1000;
private const DEFAULT_SIZE_THUMB = 400;
Expand All @@ -25,7 +31,7 @@ public function __construct(
private readonly ParameterBagInterface $parameterBag,
private readonly FilesystemOperator $fileStorage,
private readonly ImageManager $imageManager
) {
) {
}

public function setUseTmpDir(bool $tmp): self
Expand Down
23 changes: 4 additions & 19 deletions src/Service/UploadHandlerService.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace App\Service;

use App\Entity\File;
use App\Entity\Signalement;
use App\Exception\File\MaxUploadSizeExceededException;
use App\Exception\File\UnsupportedFileFormatException;
use App\Repository\FileRepository;
Expand Down Expand Up @@ -120,26 +119,12 @@ private function movePhotoVariants(string $filename): void
$this->moveFilePath($distantFolder.$variantNames[ImageManipulationHandler::SUFFIX_THUMB]);
}

public function deleteSignalementFile(Signalement $signalement, string $type, string $filename, FileRepository $fileRepository): bool
public function deleteSignalementFile(File $file, FileRepository $fileRepository): bool
{
$fileType = 'documents' === $type ? File::FILE_TYPE_DOCUMENT : File::FILE_TYPE_PHOTO;
$this->deleteFileInBucket($file);
$fileRepository->remove($file, true);

$fileCollection = $signalement->getFiles()->filter(
function (File $file) use ($fileType, $filename) {
return $fileType === $file->getFileType()
&& $filename === $file->getFilename();
}
);

if (!$fileCollection->isEmpty()) {
$file = $fileCollection->current();
$this->deleteFileInBucket($file);
$fileRepository->remove($file, true);

return true;
}

return false;
return true;
}

public function deleteFileInBucket(File $file): void
Expand Down
18 changes: 5 additions & 13 deletions templates/back/signalement/view/photos-documents.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,7 @@
data-mail="{{ photo.createdAt is defined ? photo.createdAt|date('d.m.Y') : 'N/R' }}"
style="background: url('{{ asset('_up/'~photo.filename~'/'~signalement.uuid~'?variant=thumb') }}')no-repeat center center/cover">
<button class="fr-btn fr-btn--sm fr-icon-eye-line open-photo-album" data-id={{ photo.id }} title="Voir la photo"></button>
{% if canEditSignalement
and isNewFormEnabled and isDocumentsEnabled
and (is_granted('ROLE_ADMIN_TERRITORY') or is_granted('ROLE_ADMIN') or photo.uploadedBy.id is same as(app.user.id)) %}
{% if is_granted('FILE_EDIT', photo) %}
{% set DocumentType = enum('\\App\\Entity\\Enum\\DocumentType') %}
{% include 'back/signalement/view/edit-modals/edit-file.html.twig' %}
<a href="#" class="fr-btn fr-btn--sm fr-btn--secondary fr-background--white fr-fi-edit-line btn-signalement-file-edit"
Expand All @@ -90,9 +88,7 @@
data-signalement-desordres="{{ criteres | json_encode }}"
></a>
{% endif %}
{% if (importedBy is not same as 'user' and (photo.uploadedBy is not null and photo.isPartnerFile))
and (canEditSignalement)
and (is_granted('ROLE_ADMIN_TERRITORY') or is_granted('ROLE_ADMIN') or photo.uploadedBy.id is same as(app.user.id)) %}
{% if is_granted('FILE_DELETE', photo) %}
<button title="Supprimer la photo"
data-delete="{{ path('back_signalement_delete_file',{uuid:signalement.uuid,type:'photos',filename:photo.filename}) }}"
data-token="{{ csrf_token('signalement_delete_file_'~signalement.id) }}"
Expand Down Expand Up @@ -133,9 +129,7 @@
<a href="{{ asset('_up/'~doc.filename~'/'~signalement.uuid) }}"
class="fr-btn fr-btn--sm fr-icon-eye-fill img-box" title="Afficher le document"
target="_blank" rel="noopener"></a>
{% if canEditSignalement
and isNewFormEnabled and isDocumentsEnabled
and (is_granted('ROLE_ADMIN_TERRITORY') or is_granted('ROLE_ADMIN') or doc.uploadedBy.id is same as(app.user.id)) %}
{% if is_granted('FILE_EDIT', doc) %}
{% set DocumentType = enum('\\App\\Entity\\Enum\\DocumentType') %}
{% include 'back/signalement/view/edit-modals/edit-file.html.twig' %}
<a href="#" class="fr-btn fr-btn--sm fr-btn--secondary fr-background--white fr-fi-edit-line btn-signalement-file-edit"
Expand All @@ -151,10 +145,8 @@
data-partner-name="{{ doc.uploadedBy and doc.uploadedBy.partner ? doc.uploadedBy.partner.nom ~ ' - ' : '' }}"
data-user-name="{{ doc.uploadedBy.nomComplet ?? 'N/R' }}"
></a>
{% endif %}
{% if (importedBy is not same as 'user' and (doc.uploadedBy is not null and doc.isPartnerFile))
and (canEditSignalement)
and (is_granted('ROLE_ADMIN_TERRITORY') or is_granted('ROLE_ADMIN') or doc.uploadedBy.id is same as(app.user.id)) %}
{% endif %}
{% if is_granted('FILE_DELETE', doc) %}
<button title="Supprimer le document"
data-delete="{{ path('back_signalement_delete_file',{uuid:signalement.uuid,type:'documents',filename:doc.filename}) }}"
data-token="{{ csrf_token('signalement_delete_file_'~signalement.id) }}"
Expand Down
Loading

0 comments on commit b0bf8b2

Please sign in to comment.