Skip to content
Snippets Groups Projects
Commit 5c457edf authored by Eirik Morland's avatar Eirik Morland
Browse files

Issue #3431313 by eiriksm: Add a route where we can accept an invitation

parent a447bbe1
No related branches found
No related tags found
1 merge request!41First start
Pipeline #122456 passed with warnings
<?php
declare(strict_types=1);
namespace Drupal\violinist_teams\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\user\UserInterface;
use Drupal\violinist_teams\TeamManager;
use Drupal\violinist_teams\TeamNode;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* Returns responses for Violinist teams routes.
*/
final class InviteController extends ControllerBase {
const INVITE_DATA = 'violinist_teams_invite_data';
public function __construct(
private TeamManager $teamManager,
EntityTypeManagerInterface $entityTypeManager,
AccountProxyInterface $currentUser,
ModuleHandlerInterface $moduleHandler,
private SessionInterface $session
) {
$this->entityTypeManager = $entityTypeManager;
$this->currentUser = $currentUser;
$this->moduleHandler = $moduleHandler;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('violinist_teams.team_manager'),
$container->get('entity_type.manager'),
$container->get('current_user'),
$container->get('module_handler'),
$container->get('session')
);
}
/**
* Builds the response.
*/
public function __invoke($team_id, $membership_type, $timestamp, $hash) {
// First check that this is in fact an actual team.
$team = $this->entityTypeManager->getStorage('node')->load($team_id);
if (!$team instanceof TeamNode) {
throw new AccessDeniedHttpException('No team loaded from id ' . $team_id);
}
// Then validate the hash.
$calculated_hash = $this->teamManager->getInviteHash($team, $membership_type, (int) $timestamp);
if ($calculated_hash != $hash) {
throw new AccessDeniedHttpException('Invalid hash');
}
// Now have a look if the user is logged in.
if ($this->currentUser->isAuthenticated()) {
$user = $this->entityTypeManager->getStorage('user')->load($this->currentUser->id());
if (!$user instanceof UserInterface) {
throw new AccessDeniedHttpException('No user loaded from id ' . $this->currentUser->id());
}
// Add them to the team. Based on what role we have in the parameter.
if ($membership_type === 'admin') {
$team->appendAdmin($user)->save();
}
else {
$team->appendMember($user)->save();
}
// Then let's just redirect to the team, shall we?
$redirect_response = $this->redirect('entity.node.canonical', ['node' => $team->id()]);
$this->moduleHandler->alter('violinist_teams_invite_redirect', $redirect_response, $team, $user);
return $redirect_response;
}
// OK so the user is not authenticated. Let's redirect them to the login
// page. But first, let's set a session cookie so we can actually do the
// assigning when the user returns.
$this->session->set(self::INVITE_DATA, [
'team_id' => $team_id,
'membership_type' => $membership_type,
]);
$redirect_response = $this->redirect('user.login');
$this->moduleHandler->alter('violinist_teams_invite_anonymous_redirect', $redirect_response);
return $redirect_response;
}
}
<?php
namespace Drupal\Tests\violinist_teams\Kernel;
use Drupal\node\Entity\Node;
use Drupal\user\Entity\User;
use Drupal\user\UserInterface;
use Drupal\violinist_teams\Controller\InviteController;
use Drupal\violinist_teams\TeamNode;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* Tests for the invite controller.
*
* @group violinist_teams
*/
class InviteControllerTest extends KernelTestBase {
/**
* Invite controller.
*
* @var \Drupal\violinist_teams\Controller\InviteController
*/
private InviteController $controller;
/**
* Team node.
*
* @var \Drupal\violinist_teams\TeamNode
*/
private TeamNode $team;
/**
* User.
*
* @var \Drupal\user\UserInterface
*/
private UserInterface $user;
/**
* {@inheritdoc}
*/
public function setUp() : void {
parent::setUp();
$this->controller = InviteController::create($this->container);
$this->team = Node::create([
'type' => 'team',
'title' => 'test',
]);
$this->team->save();
$user = User::create([
'name' => 'test',
'mail' => 'test@test.com',
]);
$user->save();
$this->user = $user;
}
/**
* Test with a non-existing team.
*/
public function testNonExistingTeam() {
$controller = $this->controller;
$this->expectException(AccessDeniedHttpException::class);
$this->expectExceptionMessage('No team loaded from id 1234');
$controller(1234, 'member', time(), 'hash');
}
/**
* Test with an invalid hash.
*/
public function testInvalidRandomHashCall() {
$controller = $this->controller;
$this->expectExceptionMessage('Invalid hash');
$this->expectException(AccessDeniedHttpException::class);
$controller($this->team->id(), 'member', time(), 'hash');
}
/**
* Test with a valid hash and a logged in user.
*/
public function testAssignLoggedinUser() {
/** @var \Drupal\Core\Session\AccountProxy $current_user */
$current_user = $this->container->get('current_user');
$current_user->setAccount($this->user);
$controller = $this->controller;
$response = $controller($this->team->id(), 'member', time(), $this->container->get('violinist_teams.team_manager')->getInviteHash($this->team, 'member', time()));
self::assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response);
// Reload object since it was saved.
/** @var \Drupal\violinist_teams\TeamNode $team */
$team = $this->container->get('entity_type.manager')->getStorage('node')->load($this->team->id());
self::assertEquals(TRUE, in_array($this->user->id(), $team->getMemberIds()));
}
/**
* Test with a valid hash and not a logged in user.
*/
public function testSetSessionNotLoggedIn() {
/** @var \Symfony\Component\HttpFoundation\Session\SessionInterface $session */
$session = $this->container->get('session');
$session->remove(InviteController::INVITE_DATA);
self::assertEmpty($session->get(InviteController::INVITE_DATA));
$controller = $this->controller;
$controller($this->team->id(), 'admin', time(), $this->container->get('violinist_teams.team_manager')->getInviteHash($this->team, 'admin', time()));
$session_data = $session->get(InviteController::INVITE_DATA);
self::assertEquals($this->team->id(), $session_data['team_id']);
self::assertEquals('admin', $session_data['membership_type']);
}
}
......@@ -33,3 +33,11 @@ violinist_teams.team_settings:
type: entity:node
requirements:
_custom_access: '\Drupal\violinist_teams\Controller\TeamSettingsController::accessTeamSettings'
violinist_teams.invite_accept:
path: '/violinist-teams/invite/{team_id}/{membership_type}/{timestamp}/{hash}'
defaults:
_title: 'Invite'
_controller: '\Drupal\violinist_teams\Controller\InviteController'
requirements:
_permission: 'access content'
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment