From 3921134aebd0273a64e8e68b5faa8ddfaa7d8d87 Mon Sep 17 00:00:00 2001 From: Vlad Ghita Date: Thu, 17 Mar 2022 20:39:26 +0200 Subject: [PATCH] Identify user's role from hierarchy instead of just assuming it's first one from the list. --- src/Controller/AccessAwareController.php | 22 ++++++++- .../FrontendUsersProfileController.php | 45 ++++++++++++------- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/Controller/AccessAwareController.php b/src/Controller/AccessAwareController.php index 47778f8..61243a4 100644 --- a/src/Controller/AccessAwareController.php +++ b/src/Controller/AccessAwareController.php @@ -44,8 +44,28 @@ public function applyIsAuthenticatedGuard(): void public function applyEditProfileGuard(): void { - if (! $this->getExtension()->getExtConfig('allow_profile_edit', $this->getUser()->getRoles()[0], false)) { + if (! $this->getExtension()->getExtConfig('allow_profile_edit', $this->findGrantedUserRole(), false)) { throw new AccessDeniedHttpException(); } } + + /** + * Walks through defined groups and returns first one matching users' roles. + * Supports role hierarchies. + * + * Eg: if ROLE_ADMIN inherits from ROLE_MEMBER, groups contains ROLE_MEMBER and user's role is ROLE_ADMIN, then ROLE_MEMBER is returned. + */ + public function findGrantedUserRole(): string + { + $groups = $this->getExtension()->getConfig()->get('groups', []); + + foreach ($groups as $role => $roleConfig) + if ($this->isGranted(new Expression(sprintf("'%s' in role_names", $role)))) { + return $role; + } + + // no user role was found in groups + throw new AccessDeniedHttpException(); + } + } diff --git a/src/Controller/FrontendUsersProfileController.php b/src/Controller/FrontendUsersProfileController.php index 16659fe..fc1b42f 100644 --- a/src/Controller/FrontendUsersProfileController.php +++ b/src/Controller/FrontendUsersProfileController.php @@ -17,6 +17,7 @@ use Bolt\UsersExtension\ExtensionConfigTrait; use Bolt\UsersExtension\Utils\ExtensionTemplateChooser; use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; @@ -69,10 +70,10 @@ public function view(): Response if ($user instanceof User && $this->isGranted('ROLE_ADMIN')) { return $this->redirectToRoute('bolt_user_edit', ['id' => $user->getId()]); } - + if ($user !== null /* Ensure there is an active user logged on*/ ) { - $contentTypeSlug = $this->getExtension()->getExtConfig('contenttype', $user->getRoles()[0]); - + $contentTypeSlug = $this->findContentTypeSlug(); + /** @var ContentType $contentType */ $contentType = $this->getBoltConfig()->getContentType($contentTypeSlug); $this->applyAllowForGroupsGuard($contentType); @@ -93,12 +94,12 @@ public function view(): Response public function edit(ContentType $contentType, Request $request): Response { $user = $this->getUser(); - + if ($user !== null /* Ensure there is an active user logged on*/ ) { - + $this->applyIsAuthenticatedGuard(); - $contentTypeSlug = $this->getExtension()->getExtConfig('contenttype', $this->getUser()->getRoles()[0]); + $contentTypeSlug = $this->findContentTypeSlug(); /** @var ContentType $contentType */ $contentType = $this->getBoltConfig()->getContentType($contentTypeSlug); @@ -112,7 +113,7 @@ public function edit(ContentType $contentType, Request $request): Response return $this->redirectToRoute('extension_frontend_user_profile'); } - $templates = $this->templateChooser->forProfileEdit($this->getUser()->getRoles()[0]); + $templates = $this->templateChooser->forProfileEdit($this->findGrantedUserRole()); $parameters = [ 'record' => $content, @@ -133,24 +134,24 @@ private function getUserRecord(ContentType $contentType): Content { /** @var User $user */ $user = $this->getUser(); - + // Access user record, if available - $contentTypeSlug = $this->getExtension()->getExtConfig('contenttype', $user->getRoles()[0]); + $contentTypeSlug = $this->findContentTypeSlug(); $contentType = $this->getBoltConfig()->getContentType($contentTypeSlug); $content = $this->contentRepository->findBy([ 'author' => $user, 'contentType' => $contentType->getSlug(), ]); - + // If user record unavailable, create it if (empty($content)) { return $this->new($contentType); - } + } elseif (is_iterable($content)) { $content = end($content); } - + return $content; } @@ -159,9 +160,9 @@ private function new(ContentType $contentType): Content { /** @var User $user */ $user = $this->getUser(); - $contentTypeSlug = $this->getExtension()->getExtConfig('contenttype', $user->getRoles()[0]); + $contentTypeSlug = $this->findContentTypeSlug(); $contentType = $this->getBoltConfig()->getContentType($contentTypeSlug); - + $content = new Content($contentType); $content->setAuthor($user); $content->setCreatedAt(new \DateTime()); @@ -172,7 +173,7 @@ private function new(ContentType $contentType): Content $content->setFieldValue('displayName', $user->getDisplayName()); // Hidden field for record title $content->setFieldValue('username', $user->getUsername()); // Hidden field with copy of username $content->setFieldValue('slug', $user->getUsername()); // Make slugs unique to users - + // Initialise ALL extra fields as defined in the contenttype with empty strings. // This ensures they are displayed on the /profile/edit route without backend intervention foreach($contentType->get('fields') as $name => $field){ @@ -181,7 +182,7 @@ private function new(ContentType $contentType): Content $content->setFieldValue($name, ''); } } - + $this->contentFillListener->fillContent($content); // Persist in DB @@ -196,4 +197,16 @@ private function saveContent(Content $content): void $this->em->flush(); } + private function findContentTypeSlug(): ?string + { + $role = $this->findGrantedUserRole(); + + $contentTypeSlug = $this->getExtension()->getExtConfig('contenttype', $role); + + if ($contentTypeSlug === null) + throw new \Exception("Must define a contenttype."); + + return $contentTypeSlug; + } + }