diff --git a/.gitignore b/.gitignore index 055339d..c28b5b4 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ public public/* index.html composer.lock +.DS_Store # User-specific stuff: diff --git a/Classes/Controller/FolderController.php b/Classes/Controller/FolderController.php index 7060aab..1ea8e7d 100644 --- a/Classes/Controller/FolderController.php +++ b/Classes/Controller/FolderController.php @@ -6,6 +6,7 @@ use Fixpunkt\FpFileprotector\Domain\Repository\FolderRepository; use Fixpunkt\FpFileprotector\Resource\Folder; +use Fixpunkt\FpFileprotector\Service\AccessService; use Psr\Http\Message\ResponseInterface; use TYPO3\CMS\Backend\Attribute\AsController; use TYPO3\CMS\Backend\Template\Components\ButtonBar; @@ -29,6 +30,7 @@ public function __construct( protected readonly IconFactory $iconFactory, protected readonly FolderRepository $folderRepository, protected readonly StorageRepository $storageRepository, + protected readonly AccessService $accessService, ) {} /** @@ -56,7 +58,10 @@ public function showAction(string $id = "", bool $refreshFolderTree = false): Re $moduleTemplate = $this->moduleTemplateFactory->create($this->request); $this->initializeDocHeader($moduleTemplate, $folder); $this->statusCheck($folder); - $moduleTemplate->assign('folder', $folder); + $moduleTemplate->assignMultiple([ + 'folder' => $folder, + 'propertiesPartials' => $this->accessService->getPropertiesPartials(), + ]); return $moduleTemplate->renderResponse('Folder/Show'); } @@ -95,9 +100,9 @@ protected function initializeDocHeader(ModuleTemplate $moduleTemplate, Folder $f $moduleTemplate->getDocHeaderComponent()->setMetaInformationForResource($folder); $editStorageUri = $this->uriBuilder->reset() - ->uriFor('edit', ['fileStorageUid' => $folder->getStorage()->getUid()], 'FileStorage'); + ->uriFor('edit', ['fileStorageUid' => $folder->getStorage()->getUid(), 'id' => $folder->getCombinedIdentifier()], 'FileStorage'); $htaccessUri = $this->uriBuilder->reset() - ->uriFor('htaccess', ['fileStorageUid' => $folder->getStorage()->getUid()], 'FileStorage'); + ->uriFor('htaccess', ['fileStorageUid' => $folder->getStorage()->getUid(), 'id' => $folder->getCombinedIdentifier()], 'FileStorage'); // add buttons $buttonBar = $moduleTemplate->getDocHeaderComponent()->getButtonBar(); diff --git a/Classes/Controller/ProtectionController.php b/Classes/Controller/ProtectionController.php index e4ea5d7..7a4518f 100644 --- a/Classes/Controller/ProtectionController.php +++ b/Classes/Controller/ProtectionController.php @@ -9,6 +9,7 @@ use Fixpunkt\FpFileprotector\Domain\Repository\FrontendUserGroupRepository; use Fixpunkt\FpFileprotector\Domain\Repository\FrontendUserRepository; use Fixpunkt\FpFileprotector\Domain\Repository\ProtectionRepository; +use Fixpunkt\FpFileprotector\Service\AccessService; use Psr\Http\Message\ResponseInterface; use TYPO3\CMS\Backend\Attribute\AsController; use TYPO3\CMS\Backend\Template\ModuleTemplateFactory; @@ -28,6 +29,7 @@ public function __construct( protected readonly FrontendUserGroupRepository $userGroupRepository, protected readonly FrontendUserRepository $userRepository, protected readonly FolderRepository $folderRepository, + protected readonly AccessService $accessService, ) { $querySettings = GeneralUtility::makeInstance(Typo3QuerySettings::class); $querySettings->setRespectStoragePage(false); @@ -50,6 +52,7 @@ public function newAction(string $combinedIdentifier): ResponseInterface 'folder' => $this->folderRepository->findOneByCombinedIdentifier($combinedIdentifier), 'userGroups' => $this->userGroupRepository->findAll(), 'users' => $this->userRepository->findAll(), + 'accessPartials' => $this->accessService->getPartials(), ]); return $moduleTemplate->renderResponse('Protection/New'); @@ -87,6 +90,7 @@ public function editAction(Protection $protection): ResponseInterface 'folder' => $protection->getFolderObject(), 'userGroups' => $this->userGroupRepository->findAll(), 'users' => $this->userRepository->findAll(), + 'accessPartials' => $this->accessService->getPartials(), ]); return $moduleTemplate->renderResponse('Protection/Edit'); diff --git a/Classes/Domain/Model/Protection.php b/Classes/Domain/Model/Protection.php index 069073e..57b8818 100644 --- a/Classes/Domain/Model/Protection.php +++ b/Classes/Domain/Model/Protection.php @@ -6,8 +6,6 @@ use Fixpunkt\FpFileprotector\Domain\Repository\FolderRepository; use Fixpunkt\FpFileprotector\Resource\Folder; -use Fixpunkt\FpFileprotector\Utility\FrontendUserUtility; -use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; use TYPO3\CMS\Extbase\Persistence\ObjectStorage; @@ -177,50 +175,12 @@ public function removeUser(FrontendUser $user): void } /** - * Checks whether the current user is allowed to access the resource. - * - * @return bool - */ - public function isGranted(): bool - { - if ($this->isFeLogin()) { - $frontendUserUtility = GeneralUtility::makeInstance(FrontendUserUtility::class); - $feUser = $frontendUserUtility->getCurrentFrontendUser(); - if ($feUser && $feUser->isLoggedIn()) { - if ($this->getUserGroups()->count() === 0 && $this->getUsers()->count() === 0) { - return true; - } - - if (in_array($feUser->get('id'), $this->getUsersUids(), true)) { - return true; - } - - foreach ($feUser->get('groupIds') as $userGroupId) { - if (in_array($userGroupId, $this->getUserGroupsUids(), true)) { - return true; - } - } - } - } - - $context = GeneralUtility::makeInstance(Context::class); - if ( - $this->isBeLogin() - && (bool)$context->getPropertyFromAspect('backend.user', 'isLoggedIn') - ) { - return true; - } - - return false; - } - - /** - * Returns whether the folder protection applies any restrictions. + * Returns whether the folder protection applies any FE restrictions. * * @return bool */ public function isProtected(): bool { - return $this->isFeLogin() || $this->isBeLogin(); + return $this->isFeLogin(); } } diff --git a/Classes/Domain/Repository/FileStorageRepository.php b/Classes/Domain/Repository/FileStorageRepository.php index 17e6b3f..a2bce89 100644 --- a/Classes/Domain/Repository/FileStorageRepository.php +++ b/Classes/Domain/Repository/FileStorageRepository.php @@ -60,8 +60,8 @@ public function update(ResourceStorage $fileStorage): void $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_file_storage')->createQueryBuilder(); $queryBuilder ->update('sys_file_storage') - ->set('protected', $fileStorage->isProtected()) - ->set('protected_by_default', $fileStorage->isProtectedByDefault() ?: 0) + ->set('protected', (int)$fileStorage->isProtected()) + ->set('protected_by_default', (int)$fileStorage->isProtectedByDefault()) ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($fileStorage->getUid()))) ->executeStatement(); } diff --git a/Classes/Middleware/AccessMiddleware.php b/Classes/Middleware/AccessMiddleware.php index 5bfe457..d119a8e 100644 --- a/Classes/Middleware/AccessMiddleware.php +++ b/Classes/Middleware/AccessMiddleware.php @@ -5,6 +5,7 @@ namespace Fixpunkt\FpFileprotector\Middleware; use Fixpunkt\FpFileprotector\Domain\Repository\ProtectionRepository; +use Fixpunkt\FpFileprotector\Service\AccessService; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; @@ -16,10 +17,11 @@ use TYPO3\CMS\Core\Resource\ResourceStorage; use TYPO3\CMS\Core\Resource\StorageRepository; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Extbase\Utility\LocalizationUtility; class AccessMiddleware implements MiddlewareInterface { + public function __construct(private readonly AccessService $accessService) {} + public function process( ServerRequestInterface $request, RequestHandlerInterface $handler @@ -44,7 +46,7 @@ public function process( $storage = $this->getStorage($storageIdentifier); if (!$storage) { - return $this->createError(LocalizationUtility::translate('sys_file_storage.errors.storage_not_found', 'FpFileprotector')); + return $this->createError('The storage could not be found.'); } $protected = $storage->getStorageRecord()['protected']; $protectedByDefault = $storage->getStorageRecord()['protected_by_default']; @@ -65,7 +67,7 @@ public function process( ) ) ) { - return $this->createError(LocalizationUtility::translate('sys_file_storage.errors.file_not_found', 'FpFileprotector')); + return $this->createError('The file could not be found.'); } $originalFile = $file; if ($originalFile instanceof ProcessedFile) { @@ -78,14 +80,14 @@ public function process( $folder = $originalFile->getParentFolder(); if (!$folder) { - return $this->createError(LocalizationUtility::translate('sys_file_storage.errors.folder_not_found', 'FpFileprotector')); + return $this->createError('The storage location could not be found.'); } $protection = ProtectionRepository::getProtectionStatic($folder); - if ((!$protection && !$protectedByDefault) || ($protection && $protection->isGranted())) { + if ((!$protection && !$protectedByDefault) || ($protection && $this->accessService->isGranted($protection))) { return $this->releaseFile($storage, $filePath); } - return $this->createError(LocalizationUtility::translate('sys_file_storage.errors.access_denied', 'FpFileprotector'), 500); + return $this->createError('You do not have permission to access this file.', 500); } /** @@ -134,7 +136,7 @@ private function releaseFile(ResourceStorage $storage, string $fileIdentifier): { $file = $storage->getFile($fileIdentifier); if (!$file) { - return $this->createError(LocalizationUtility::translate('sys_file_storage.errors.file_release_not_found', 'FpFileprotector')); + return $this->createError('The requested file could not be found.'); } $body = new Stream('php://temp', 'rw'); diff --git a/Classes/Service/AccessService.php b/Classes/Service/AccessService.php new file mode 100644 index 0000000..6f49cc1 --- /dev/null +++ b/Classes/Service/AccessService.php @@ -0,0 +1,44 @@ + $accessUtilities */ + public function __construct(private readonly iterable $accessUtilities) {} + + public function isGranted(Protection $protection): bool + { + foreach ($this->accessUtilities as $utility) { + if ($utility->isGranted($protection)) { + return true; + } + } + return false; + } + + /** @return string[] */ + public function getPartials(): array + { + $partials = []; + foreach ($this->accessUtilities as $utility) { + $partials[] = $utility->getPartial(); + } + return $partials; + } + + /** @return string[] */ + public function getPropertiesPartials(): array + { + $partials = []; + foreach ($this->accessUtilities as $utility) { + $partials[] = $utility->getPropertiesPartial(); + } + return $partials; + } +} diff --git a/Classes/Utility/Access/AccessUtilityInterface.php b/Classes/Utility/Access/AccessUtilityInterface.php new file mode 100644 index 0000000..90df01b --- /dev/null +++ b/Classes/Utility/Access/AccessUtilityInterface.php @@ -0,0 +1,14 @@ +user['uid'])) { + return false; + } + + if ($beUser->isAdmin()) { + return true; + } + + foreach ($beUser->getFileStorages() as $storage) { + if ($storage->getUid() !== $protection->getStorage()) { + continue; + } + try { + $folder = $storage->getFolder($protection->getFolder()); + return $storage->isWithinFileMountBoundaries($folder, false); + } catch (\Exception) { + return false; + } + } + + return false; + } + + public function getPartial(): string + { + return 'Access/Be'; + } + + public function getPropertiesPartial(): string + { + return 'Properties/Be'; + } +} diff --git a/Classes/Utility/Access/FeLoginAccessUtility.php b/Classes/Utility/Access/FeLoginAccessUtility.php new file mode 100644 index 0000000..1455611 --- /dev/null +++ b/Classes/Utility/Access/FeLoginAccessUtility.php @@ -0,0 +1,51 @@ +isFeLogin()) { + return false; + } + + $feUser = $this->frontendUserUtility->getCurrentFrontendUser(); + if (!$feUser || !$feUser->isLoggedIn()) { + return false; + } + + if ($protection->getUserGroups()->count() === 0 && $protection->getUsers()->count() === 0) { + return true; + } + + if (in_array($feUser->get('id'), $protection->getUsersUids(), true)) { + return true; + } + + foreach ($feUser->get('groupIds') as $userGroupId) { + if (in_array($userGroupId, $protection->getUserGroupsUids(), true)) { + return true; + } + } + + return false; + } + + public function getPartial(): string + { + return 'Access/Fe'; + } + + public function getPropertiesPartial(): string + { + return 'Properties/Fe'; + } +} diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index de9f1ed..c614b37 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -5,6 +5,11 @@ services: autoconfigure: true public: true + _instanceof: + Fixpunkt\FpFileprotector\Utility\Access\AccessUtilityInterface: + tags: + - { name: 'fp_fileprotector.access' } + Fixpunkt\FpFileprotector\: resource: '../Classes/*' exclude: @@ -12,6 +17,17 @@ services: - '../Classes/Resource/*' - '../Classes/Utility/*' + Fixpunkt\FpFileprotector\Utility\Access\: + resource: '../Classes/Utility/Access/*' + + # Utilities needed for DI + Fixpunkt\FpFileprotector\Utility\FrontendUserUtility: ~ + + # Inject all tagged access utilities into AccessService + Fixpunkt\FpFileprotector\Service\AccessService: + arguments: + $accessUtilities: !tagged_iterator 'fp_fileprotector.access' + Fixpunkt\FpFileprotector\EventListener\ModifyIconForResourcePropertiesListener: tags: - name: event.listener diff --git a/Configuration/TCA/tx_fpfileprotector_domain_model_protection.php b/Configuration/TCA/tx_fpfileprotector_domain_model_protection.php index 83fc312..6831d2b 100644 --- a/Configuration/TCA/tx_fpfileprotector_domain_model_protection.php +++ b/Configuration/TCA/tx_fpfileprotector_domain_model_protection.php @@ -21,13 +21,9 @@ 'label' => 'LLL:EXT:fp_fileprotector/Resources/Private/Language/locallang.xlf:tx_fpfileprotector_domain_model_protection.palette.fe', 'showitem' => 'fe_login,--linebreak--,user_groups,--linebreak--,users', ], - 'be' => [ - 'label' => 'LLL:EXT:fp_fileprotector/Resources/Private/Language/locallang.xlf:tx_fpfileprotector_domain_model_protection.palette.be', - 'showitem' => 'be_login', - ], ], 'types' => [ - 0 => ['showitem' => '--palette--;;folder,--palette--;;fe,--palette--;;be'], + 0 => ['showitem' => '--palette--;;folder,--palette--;;fe'], ], 'columns' => [ 'storage' => [ diff --git a/Resources/Private/Partials/Access/Be.html b/Resources/Private/Partials/Access/Be.html new file mode 100644 index 0000000..3396fdf --- /dev/null +++ b/Resources/Private/Partials/Access/Be.html @@ -0,0 +1,3 @@ +