diff --git a/Classes/Domain/Model/MenuInterface.php b/Classes/Domain/Model/MenuInterface.php index bb1117f0a368570d4e604f337a0169cd06d5650d..395193a1df14402114258d80c358899a9e19ad49 100644 --- a/Classes/Domain/Model/MenuInterface.php +++ b/Classes/Domain/Model/MenuInterface.php @@ -12,6 +12,10 @@ interface MenuInterface public function getItems(): ?ObjectStorage; + public function addItem(MenuItem $item): void; + + public function removeItem(MenuItem $itemToRemove): void; + public function getItemsByType(int $type): array; public function getPageItems(): array; diff --git a/Classes/Domain/Model/Page.php b/Classes/Domain/Model/Page.php index f557b2bc9b23856f42d8a46761b23581c5fdbf4a..d86e6712e7c507f978bc564e6e3dd51138eddd09 100644 --- a/Classes/Domain/Model/Page.php +++ b/Classes/Domain/Model/Page.php @@ -11,7 +11,7 @@ namespace NL\NlMenubuilder\Domain\Model; * For the full copyright and license information, please read the * LICENSE.txt file that was distributed with this source code. * - * (c) 2021 + * (c) 2021 * ***/ /** @@ -22,28 +22,33 @@ class Page extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity /** * title - * + * * @var string */ protected $title = ''; /** * navTitle - * + * * @var string */ protected $navTitle = ''; /** * subtitle - * + * * @var string */ protected $subtitle = ''; + /** + * @var string + */ + protected $feGroup = ''; + /** * Returns the title - * + * * @return string $title */ public function getTitle() @@ -53,7 +58,7 @@ class Page extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity /** * Sets the title - * + * * @param string $title * @return void */ @@ -64,7 +69,7 @@ class Page extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity /** * Returns the navTitle - * + * * @return string $navTitle */ public function getNavTitle() @@ -74,7 +79,7 @@ class Page extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity /** * Sets the navTitle - * + * * @param string $navTitle * @return void */ @@ -85,7 +90,7 @@ class Page extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity /** * Returns the subtitle - * + * * @return string $subtitle */ public function getSubtitle() @@ -95,7 +100,7 @@ class Page extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity /** * Sets the subtitle - * + * * @param string $subtitle * @return void */ @@ -103,4 +108,20 @@ class Page extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity { $this->subtitle = $subtitle; } + + /** + * @return string + */ + public function getFeGroup(): string + { + return $this->feGroup; + } + + /** + * @param string $feGroup + */ + public function setFeGroup(string $feGroup): void + { + $this->feGroup = $feGroup; + } } diff --git a/Classes/Service/AuthService.php b/Classes/Service/AuthService.php new file mode 100644 index 0000000000000000000000000000000000000000..e1e37d47fc8f8cdbb5d7f9c563d743f63deca531 --- /dev/null +++ b/Classes/Service/AuthService.php @@ -0,0 +1,65 @@ +<?php + + +namespace NL\NlMenubuilder\Service; + + +use TYPO3\CMS\Core\Context\AspectInterface; +use TYPO3\CMS\Core\Context\Context; +use TYPO3\CMS\Core\SingletonInterface; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Domain\Model\FrontendUser; +use TYPO3\CMS\Extbase\Domain\Repository\FrontendUserRepository; + +class AuthService implements SingletonInterface +{ + /** + * @var FrontendUserRepository + */ + protected $userRepository = null; + + /** + * @param FrontendUserRepository $repository + */ + public function injectUserRepository(FrontendUserRepository $repository): void + { + $this->userRepository = $repository; + } + + /** + * @throws \TYPO3\CMS\Core\Context\Exception\AspectPropertyNotFoundException + */ + public function getUser(): ?FrontendUser + { + $id = $this->getPropertyFromAspect('id'); + + return $id ? $this->userRepository->findByUid($id) : null; + } + + /** + * @return bool + * @throws \TYPO3\CMS\Core\Context\Exception\AspectPropertyNotFoundException + */ + public function isLoggedIn(): bool + { + return (bool) $this->getPropertyFromAspect('isLoggedIn'); + } + + /** + * @param string $property + * @return mixed + * @throws \TYPO3\CMS\Core\Context\Exception\AspectPropertyNotFoundException + */ + protected function getPropertyFromAspect(string $property) + { + return $this->getAspect()->get($property); + } + + /** + * @return object|\Psr\Log\LoggerAwareInterface|SingletonInterface + */ + protected function getAspect(): AspectInterface + { + return GeneralUtility::makeInstance(Context::class)->getAspect('frontend.user'); + } +} diff --git a/Classes/Service/MenuSecurityService.php b/Classes/Service/MenuSecurityService.php new file mode 100644 index 0000000000000000000000000000000000000000..3658d5d3c3bc71ce6eed955c51829326cd5f3a6f --- /dev/null +++ b/Classes/Service/MenuSecurityService.php @@ -0,0 +1,103 @@ +<?php + + +namespace NL\NlMenubuilder\Service; + + +use NL\NlMenubuilder\Domain\Model\MenuInterface; +use NL\NlMenubuilder\Domain\Model\MenuItem; +use NL\NlMenubuilder\Domain\Model\MenuItemPage; +use TYPO3\CMS\Core\SingletonInterface; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Reflection\ObjectAccess; + +class MenuSecurityService implements SingletonInterface +{ + /** + * @var AuthService + */ + protected $authService; + + /** + * @param AuthService $service + */ + public function injectAuthService(AuthService $service): void + { + $this->authService = $service; + } + + /** + * @param MenuInterface $menu + * @throws \TYPO3\CMS\Core\Context\Exception\AspectPropertyNotFoundException + */ + public function applySecurityCheck(MenuInterface $menu): void + { + $this->removeInaccessible($menu); + + $this->removeEmpty($menu); + } + + /** + * @param MenuInterface $menu + * @throws \TYPO3\CMS\Core\Context\Exception\AspectPropertyNotFoundException + */ + protected function removeInaccessible(MenuInterface $menu): void + { + foreach ($menu->getPageItems() as $pageItem) { + if (false === $this->isAccessible($pageItem)) { + $menu->removeItem($pageItem); + } + } + + foreach ($menu->getSubmenuItems() as $submenuItem) { + $this->removeInaccessible($submenuItem); + } + } + + /** + * @param MenuInterface $menu + */ + protected function removeEmpty(MenuInterface $menu): void + { + /** @var MenuItem $submenuItem */ + foreach ($menu->getSubmenuItems() as $submenuItem) { + $this->removeEmpty($submenuItem); + + if (empty($submenuItem->getItems())) { + $menu->removeItem($submenuItem); + } + } + } + + /** + * @param MenuItemPage $itemPage + * @return bool + * @throws \TYPO3\CMS\Core\Context\Exception\AspectPropertyNotFoundException + */ + protected function isAccessible(MenuItemPage $itemPage): bool + { + $feGroups = array_filter(GeneralUtility::intExplode( + ',', + ObjectAccess::getPropertyPath($itemPage, 'page.feGroup'), + true + )); + + if (empty($feGroups) || (in_array(-2, $feGroups) && $this->authService->isLoggedIn())) { + return true; + } + + if (in_array(-1, $feGroups) && $this->authService->isLoggedIn()) { + return false; + } + + if ($user = $this->authService->getUser()) { + foreach ($user->getUsergroup() as $group) { + if (in_array($group->getUid(), $feGroups)) { + return true; + } + } + } + + return false; + } +} diff --git a/Classes/ViewHelpers/Menu/Controller/RenderController.php b/Classes/ViewHelpers/Menu/Controller/RenderController.php index b23855cc9fc747514ec689422b794bc5184679c9..26305e36299e13d0d090d42351ed0096d9810ba3 100644 --- a/Classes/ViewHelpers/Menu/Controller/RenderController.php +++ b/Classes/ViewHelpers/Menu/Controller/RenderController.php @@ -4,6 +4,7 @@ namespace NL\NlMenubuilder\ViewHelpers\Menu\Controller; use NL\NlMenubuilder\Domain\Model\Menu; use NL\NlMenubuilder\Domain\Repository\MenuRepository; +use NL\NlMenubuilder\Service\MenuSecurityService; use TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetController; class RenderController extends AbstractWidgetController @@ -13,16 +14,29 @@ class RenderController extends AbstractWidgetController */ protected $menuRepository; + /** + * @var MenuSecurityService + */ + protected $menuSecurityService; + /** * Inject menu repository * * @param MenuRepository $repository */ - public function injectMenuRepository(MenuRepository $repository) + public function injectMenuRepository(MenuRepository $repository): void { $this->menuRepository = $repository; } + /** + * @param MenuSecurityService $service + */ + public function injectMenuSecurityService(MenuSecurityService $service): void + { + $this->menuSecurityService = $service; + } + /** * @return string */ @@ -31,6 +45,10 @@ class RenderController extends AbstractWidgetController /** @var Menu $menu */ $menu = $this->menuRepository->findByIdentifier($this->widgetConfiguration['uid']); + if (!$this->widgetConfiguration['disableSecurityCheck']) { + $this->menuSecurityService->applySecurityCheck($menu); + } + $this->view->assign('menu', $menu); return $this->view->render($menu ? $menu->getTemplate() : Menu::DEFAULT_TEMPLATE); diff --git a/Classes/ViewHelpers/Menu/GetViewHelper.php b/Classes/ViewHelpers/Menu/GetViewHelper.php index a2339f9387656c54e1021f3aba365b7ee749e5c2..07524a74b3698594d1f97d7f31e526e0770196fb 100644 --- a/Classes/ViewHelpers/Menu/GetViewHelper.php +++ b/Classes/ViewHelpers/Menu/GetViewHelper.php @@ -3,7 +3,9 @@ namespace NL\NlMenubuilder\ViewHelpers\Menu; use FluidTYPO3\Vhs\Traits\TemplateVariableViewHelperTrait; +use NL\NlMenubuilder\Domain\Model\Menu; use NL\NlMenubuilder\Domain\Repository\MenuRepository; +use NL\NlMenubuilder\Service\MenuSecurityService; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Object\ObjectManager; use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; @@ -32,6 +34,7 @@ class GetViewHelper extends AbstractViewHelper { $this->registerAsArgument(); $this->registerArgument('uid', 'int', 'UID of the menu', true, 0); + $this->registerArgument('disableSecurityCheck', 'bool', 'Disable security check', false, false); } /** @@ -49,8 +52,18 @@ class GetViewHelper extends AbstractViewHelper /** @var MenuRepository $menuRepository */ $menuRepository = $objectManager->get(MenuRepository::class); + /** @var Menu $menu */ + $menu = $menuRepository->findByUid($arguments['uid'] ?? 0); + + if (!$arguments['disableSecurityCheck']) { + /** @var MenuSecurityService $menuSecurityService */ + $menuSecurityService = $objectManager->get(MenuSecurityService::class); + + $menuSecurityService->applySecurityCheck($menu); + } + return static::renderChildrenWithVariableOrReturnInputStatic( - $menuRepository->findByUid($arguments['uid'] ?? 0), + $menu, $arguments['as'], $renderingContext, $renderChildrenClosure diff --git a/Classes/ViewHelpers/Menu/RenderViewHelper.php b/Classes/ViewHelpers/Menu/RenderViewHelper.php index 58bedd502de8985d8bf7d6d283fb21b26a9ccfed..c9cf00eb5990615841788bd48671b59a129740a3 100644 --- a/Classes/ViewHelpers/Menu/RenderViewHelper.php +++ b/Classes/ViewHelpers/Menu/RenderViewHelper.php @@ -30,6 +30,7 @@ class RenderViewHelper extends AbstractWidgetViewHelper public function initializeArguments(): void { $this->registerArgument('uid', 'int', 'UID of the menu', true); + $this->registerArgument('disableSecurityCheck', 'bool', 'Disable security check', false, false); } /**