From 2dbd11ef28fa0f6efd7838fc9ce5a29e393856a9 Mon Sep 17 00:00:00 2001 From: Craig Williams Date: Tue, 15 Feb 2022 17:43:24 +0000 Subject: [PATCH] Refactored the organisation members controller - includes a permissions fix --- .../OrganisationMembersController.php | 255 +++++++++--------- 1 file changed, 133 insertions(+), 122 deletions(-) diff --git a/src/Controller/OrganisationMembersController.php b/src/Controller/OrganisationMembersController.php index ee07451..870060c 100644 --- a/src/Controller/OrganisationMembersController.php +++ b/src/Controller/OrganisationMembersController.php @@ -52,26 +52,33 @@ class OrganisationMembersController extends SimpleController */ public function join(Request $request, Response $response, $args) { - $organisation = $this->getOrganisationFromParams($args); - - // If the organisation doesn't exist, return 404 - if (!$organisation) { - throw new NotFoundException(); - } - /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ $authorizer = $this->ci->authorizer; + /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ + $classMapper = $this->ci->classMapper; + + /** @var \UserFrosting\Support\Repository\Repository $config */ + $config = $this->ci->config; + /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ $currentUser = $this->ci->currentUser; + /** @var \UserFrosting\Sprinkle\Core\Alert\AlertStream $ms */ + $ms = $this->ci->alerts; + + // Access-controlled page if (!$authorizer->checkAccess($currentUser, 'join_organisation')) { throw new ForbiddenException(); } - /** @var \UserFrosting\Sprinkle\Core\Alert\AlertStream $ms */ - $ms = $this->ci->alerts; + $organisation = $this->getOrganisationFromParams($args); + + // If the organisation doesn't exist, return 404 + if (!$organisation) { + throw new NotFoundException(); + } // Check if the user is a member of the organisation, pending or no relation at all $memberCheck = $organisation->members()->where('user_id', $currentUser->id)->withPivot('flag_approved')->first(); @@ -88,15 +95,9 @@ class OrganisationMembersController extends SimpleController return $response->withJson([], 400); } - /** @var \UserFrosting\Support\Repository\Repository $config */ - $config = $this->ci->config; - - /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ - $classMapper = $this->ci->classMapper; - // All checks passed! log events/activities and create organisation // Begin transaction - DB will be rolled back if an exception occurs - Capsule::transaction(function () use ($classMapper, $ms, $organisation, $currentUser, $config) { + Capsule::transaction(function () use ($organisation, $currentUser, $classMapper, $config) { $organisation->members()->attach($currentUser->id, [ 'flag_admin' => false, 'flag_approved' => !$config['organisation']['membership']['require_approval'], @@ -123,23 +124,25 @@ class OrganisationMembersController extends SimpleController $approval = $this->ci->repoOrganisationMembershipApproval->create($tokenOwner, $timeout); $this->sendApprovalEmail($currentUser, $organisation, $approval->getToken()); - - $ms->addMessageTranslated('success', 'ORGANISATION.JOIN_REQUEST.SUBMIT_SUCCESSFUL', [ - 'name' => $organisation->name - ]); } else { $this->ci->userActivityLogger->info("User {$currentUser->user_name} has joined organisation {$organisation->name}.", [ 'type' => 'organisation_join', 'user_id' => $currentUser->id, ]); - - $ms->addMessageTranslated('success', 'ORGANISATION.JOIN_SUCCESSFUL', [ - 'name' => $organisation->name - ]); } }); + if ($config['organisation']['membership']['require_approval']) { + $ms->addMessageTranslated('success', 'ORGANISATION.JOIN_REQUEST.SUBMIT_SUCCESSFUL', [ + 'name' => $organisation->name + ]); + } else { + $ms->addMessageTranslated('success', 'ORGANISATION.JOIN_SUCCESSFUL', [ + 'name' => $organisation->name + ]); + } + return $response->withJson([], 200); } @@ -163,13 +166,6 @@ class OrganisationMembersController extends SimpleController */ public function cancel(Request $request, Response $response, $args) { - $organisation = $this->getOrganisationFromParams($args); - - // If the organisation doesn't exist, return 404 - if (!$organisation) { - throw new NotFoundException(); - } - /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ $authorizer = $this->ci->authorizer; @@ -185,6 +181,13 @@ class OrganisationMembersController extends SimpleController /** @var \UserFrosting\Sprinkle\Core\Alert\AlertStream $ms */ $ms = $this->ci->alerts; + + $organisation = $this->getOrganisationFromParams($args); + + // If the organisation doesn't exist, return 404 + if (!$organisation) { + throw new NotFoundException(); + } // Check if the user is a member of the organisation, pending or no relation at all $memberCheck = $organisation->members()->where('user_id', $currentUser->id)->withPivot('flag_approved')->first(); if ($memberCheck) { @@ -202,7 +205,7 @@ class OrganisationMembersController extends SimpleController } // Begin transaction - DB will be rolled back if an exception occurs - Capsule::transaction(function () use ($organisation, $currentUser) { + Capsule::transaction(function () use ($organisation, $currentUser, $classMapper, $config) { $organisation->members()->detach($currentUser->id); if ($config['organisation']['membership']['require_approval']) { @@ -250,6 +253,16 @@ class OrganisationMembersController extends SimpleController */ public function leave(Request $request, Response $response, $args) { + /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ + $authorizer = $this->ci->authorizer; + + /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ + $currentUser = $this->ci->currentUser; + + /** @var \UserFrosting\Sprinkle\Core\Alert\AlertStream $ms */ + $ms = $this->ci->alerts; + + $organisation = $this->getOrganisationFromParams($args); // If the organisation doesn't exist, return 404 @@ -257,12 +270,6 @@ class OrganisationMembersController extends SimpleController throw new NotFoundException(); } - /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ - $authorizer = $this->ci->authorizer; - - /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ - $currentUser = $this->ci->currentUser; - // Access-controlled page if (!$authorizer->checkAccess($currentUser, 'leave_organisation', [ 'organisation' => $organisation, @@ -270,12 +277,6 @@ class OrganisationMembersController extends SimpleController throw new ForbiddenException(); } - /** @var \UserFrosting\Support\Repository\Repository $config */ - $config = $this->ci->config; - - /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ - $classMapper = $this->ci->classMapper; - // Check if the user is a member of the organisation, pending or no relation at all $memberCheck = $organisation->members()->where('user_id', $currentUser->id)->withPivot('flag_approved')->first(); if (!$memberCheck || !$memberCheck->pivot->flag_approved) { @@ -296,9 +297,6 @@ class OrganisationMembersController extends SimpleController ]); }); - /** @var \UserFrosting\Sprinkle\Core\Alert\AlertStream $ms */ - $ms = $this->ci->alerts; - $ms->addMessageTranslated('success', 'ORGANISATION.LEAVE_SUCCESSFUL', [ 'name' => $organisationName, ]); @@ -328,6 +326,16 @@ class OrganisationMembersController extends SimpleController */ public function accept(Request $request, Response $response, $args) { + /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ + $authorizer = $this->ci->authorizer; + + /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ + $classMapper = $this->ci->classMapper; + + /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ + $currentUser = $this->ci->currentUser; + + // Fetch the organisation from the params $organisation = $this->getOrganisationFromParams($args); @@ -339,8 +347,12 @@ class OrganisationMembersController extends SimpleController throw new NotFoundException(); } - /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ - $classMapper = $this->ci->classMapper; + // Access-controlled page + if (!$authorizer->checkAccess($currentUser, 'approve_organisation_membership', [ + 'organisation' => $organisation + ])) { + throw new ForbiddenException(); + } // Find the mapping $tokenOwner = $classMapper->getClassMapping('organisation_member')::query() @@ -381,13 +393,16 @@ class OrganisationMembersController extends SimpleController /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ $authorizer = $this->ci->authorizer; + /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ + $classMapper = $this->ci->classMapper; + /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ $currentUser = $this->ci->currentUser; - /** @var \UserFrosting\Sprinkle\Core\Alert\AlertStream $ms */ $ms = $this->ci->alerts; + // GET parameters $params = $request->getQueryParams(); @@ -407,12 +422,16 @@ class OrganisationMembersController extends SimpleController // Find the token owner if valid $owner_id = $this->ci->repoOrganisationMembershipApproval->findOwner($data['token']); - if (!$owner_id) { + // Fetch the mapping + $tokenOwner = $classMapper->getClassMapping('organisation_member')::query() + ->where('map_id', $owner_id) + ->first(); + if (!$tokenOwner) { $ms->addMessageTranslated('danger', 'ORGANISATION.JOIN_REQUEST.TOKEN_NOT_FOUND'); return $response->withRedirect($this->ci->router->pathFor('uri_organisations')); } - $organisation = $classMapper->getClassMapping('organisation')::findUnique($owner_id, 'id', false); + $organisation = $tokenOwner->organisation()->first(); // Access-controlled page if (!$authorizer->checkAccess($currentUser, 'approve_organisation_membership', [ @@ -421,19 +440,8 @@ class OrganisationMembersController extends SimpleController throw new ForbiddenException(); } - /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ - $classMapper = $this->ci->classMapper; - - // Fetch the mapping - $tokenOwner = $classMapper->getClassMapping('organisation_member')::query() - ->where('map_id', $owner_id) - ->first(); - - // Process the acceptance emails etc - if (!$this->processAcceptToken($tokenOwner)) { - return $response->withRedirect($this->ci->router->pathFor('uri_organisation', ['slug' => $organisation->slug])); - } + $this->processAcceptToken($tokenOwner); // Forward to organisation page return $response->withRedirect($this->ci->router->pathFor('uri_organisation', ['slug' => $organisation->slug])); @@ -460,6 +468,16 @@ class OrganisationMembersController extends SimpleController */ public function reject(Request $request, Response $response, $args) { + /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ + $authorizer = $this->ci->authorizer; + + /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ + $classMapper = $this->ci->classMapper; + + /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ + $currentUser = $this->ci->currentUser; + + // Fetch the organisation from the params $organisation = $this->getOrganisationFromParams($args); @@ -471,8 +489,12 @@ class OrganisationMembersController extends SimpleController throw new NotFoundException(); } - /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ - $classMapper = $this->ci->classMapper; + // Access-controlled page + if (!$authorizer->checkAccess($currentUser, 'approve_organisation_membership', [ + 'organisation' => $organisation + ])) { + throw new ForbiddenException(); + } // Find the mapping $tokenOwner = $classMapper->getClassMapping('organisation_member')::query() @@ -513,13 +535,16 @@ class OrganisationMembersController extends SimpleController /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ $authorizer = $this->ci->authorizer; + /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ + $classMapper = $this->ci->classMapper; + /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ $currentUser = $this->ci->currentUser; - /** @var \UserFrosting\Sprinkle\Core\Alert\AlertStream $ms */ $ms = $this->ci->alerts; + // GET parameters $params = $request->getQueryParams(); @@ -539,12 +564,16 @@ class OrganisationMembersController extends SimpleController // Find the token owner if valid $owner_id = $this->ci->repoOrganisationMembershipApproval->findOwner($data['token']); + // Fetch the mapping + $tokenOwner = $classMapper->getClassMapping('organisation_member')::query() + ->where('map_id', $owner_id) + ->first(); if (!$owner_id) { $ms->addMessageTranslated('danger', 'ORGANISATION.JOIN_REQUEST.TOKEN_NOT_FOUND'); return $response->withRedirect($this->ci->router->pathFor('uri_organisation', ['slug' => $organisation->slug])); } - $organisation = $classMapper->getClassMapping('organisation')::findUnique($owner_id, 'id', false); + $organisation = $tokenOwner->organisation()->first(); // Access-controlled page if (!$authorizer->checkAccess($currentUser, 'approve_organisation_membership', [ @@ -553,20 +582,8 @@ class OrganisationMembersController extends SimpleController throw new ForbiddenException(); } - - /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ - $classMapper = $this->ci->classMapper; - - // Fetch the mapping - $tokenOwner = $classMapper->getClassMapping('organisation_member')::query() - ->where('map_id', $owner_id) - ->first(); - - // Process the rejectance emails etc - if (!$this->processRejectToken($tokenOwner)) { - return $response->withRedirect($this->ci->router->pathFor('uri_organisation', ['slug' => $organisation->slug])); - } + $this->processRejectToken($tokenOwner); // Forward to organisation page return $response->withRedirect($this->ci->router->pathFor('uri_organisation', ['slug' => $organisation->slug])); @@ -589,6 +606,16 @@ class OrganisationMembersController extends SimpleController */ public function getList(Request $request, Response $response, $args) { + /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ + $authorizer = $this->ci->authorizer; + + /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ + $classMapper = $this->ci->classMapper; + + /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ + $currentUser = $this->ci->currentUser; + + $organisation = $this->getOrganisationFromParams($args); // If the organisation no longer exists, forward to main organisation listing page @@ -599,12 +626,6 @@ class OrganisationMembersController extends SimpleController // GET parameters $params = $request->getQueryParams(); - /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ - $authorizer = $this->ci->authorizer; - - /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ - $currentUser = $this->ci->currentUser; - // Access-controlled page if (!$authorizer->checkAccess($currentUser, 'view_organisation_field', [ 'organisation' => $organisation, @@ -613,9 +634,6 @@ class OrganisationMembersController extends SimpleController throw new ForbiddenException(); } - /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ - $classMapper = $this->ci->classMapper; - $sprunje = $classMapper->createInstance('user_sprunje', $classMapper, $params); $sprunje->extendQuery(function ($query) use ($classMapper, $organisation) { return $query->where('organisation_id', $organisation->id); @@ -639,6 +657,16 @@ class OrganisationMembersController extends SimpleController */ public function getModalConfirmLeave(Request $request, Response $response, $args) { + /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ + $authorizer = $this->ci->authorizer; + + /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ + $classMapper = $this->ci->classMapper; + + /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ + $currentUser = $this->ci->currentUser; + + $organisation = $this->getOrganisationFromParams($args); // If the organisation no longer exists, forward to main organisation listing page @@ -646,12 +674,6 @@ class OrganisationMembersController extends SimpleController throw new NotFoundException(); } - /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ - $authorizer = $this->ci->authorizer; - - /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ - $currentUser = $this->ci->currentUser; - // Access-controlled page if (!$authorizer->checkAccess($currentUser, 'leave_organisation', [ 'organisation' => $organisation, @@ -659,9 +681,6 @@ class OrganisationMembersController extends SimpleController throw new ForbiddenException(); } - /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ - $classMapper = $this->ci->classMapper; - return $this->ci->view->render($response, 'modals/confirm-leave-organisation.html.twig', [ 'organisation' => $organisation, 'form' => [ @@ -683,6 +702,13 @@ class OrganisationMembersController extends SimpleController */ public function getModalConfirmCancel(Request $request, Response $response, $args) { + /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ + $authorizer = $this->ci->authorizer; + + /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ + $currentUser = $this->ci->currentUser; + + // GET parameters $params = $request->getQueryParams(); @@ -693,12 +719,6 @@ class OrganisationMembersController extends SimpleController throw new NotFoundException(); } - /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ - $authorizer = $this->ci->authorizer; - - /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ - $currentUser = $this->ci->currentUser; - // Access-controlled page if (!$authorizer->checkAccess($currentUser, 'join_organisation', [ 'organisation' => $organisation, @@ -706,9 +726,6 @@ class OrganisationMembersController extends SimpleController throw new ForbiddenException(); } - /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ - $classMapper = $this->ci->classMapper; - return $this->ci->view->render($response, 'modals/confirm-cancel-organisation-join.html.twig', [ 'organisation' => $organisation, 'form' => [ @@ -809,25 +826,22 @@ class OrganisationMembersController extends SimpleController /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ $currentUser = $this->ci->currentUser; + /** @var \UserFrosting\Sprinkle\Core\Alert\AlertStream $ms */ + $ms = $this->ci->alerts; + + // Access-controlled page if (!$authorizer->checkAccess($currentUser, 'accept_organisation_membership')) { throw new ForbiddenException(); } - /** @var \UserFrosting\Sprinkle\Core\Alert\AlertStream $ms */ - $ms = $this->ci->alerts; - - // Try and complete the token, bail if not found $verification = $this->ci->repoOrganisationMembershipApproval->completeForOwner($tokenOwner, ['approved' => true, 'approver_id' => $currentUser->id]); if (!$verification) { $ms->addMessageTranslated('danger', 'ORGANISATION.JOIN_REQUEST.TOKEN_NOT_FOUND'); return false; } - - /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ - $classMapper = $this->ci->classMapper; $organisation = $tokenOwner->organisation()->first(); $requester = $tokenOwner->user()->first(); @@ -855,24 +869,21 @@ class OrganisationMembersController extends SimpleController /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ $currentUser = $this->ci->currentUser; + /** @var \UserFrosting\Sprinkle\Core\Alert\AlertStream $ms */ + $ms = $this->ci->alerts; + + // Access-controlled page if (!$authorizer->checkAccess($currentUser, 'accept_organisation_membership')) { throw new ForbiddenException(); } - - /** @var \UserFrosting\Sprinkle\Core\Alert\AlertStream $ms */ - $ms = $this->ci->alerts; - // Try and complete the token, bail if not found $verification = $this->ci->repoOrganisationMembershipApproval->completeForOwner($tokenOwner, ['approved' => false, 'approver_id' => $currentUser->id]); if (!$verification) { $ms->addMessageTranslated('danger', 'ORGANISATION.JOIN_REQUEST.TOKEN_NOT_FOUND'); return false; } - - /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ - $classMapper = $this->ci->classMapper; $organisation = $tokenOwner->organisation()->first(); $requester = $tokenOwner->user()->first();