<?php use PHPUnit\Framework\TestCase; if (session_status() === PHP_SESSION_NONE) { session_start(); } /** * This test has a setup similar to the LoginHandlerTest one, however SessionData and * UserHandler are not mocked, so interaction between session and login handler can be * tested and complete login and join flows can be simulated step by step. */ final class LoginFlowTest extends TestCase { private $locatorStub; private $userDaoStub; private $oAuth2RequestHandler; private $auditLogger; private $gmsClientStub; private $sessionData; private $userHandler; private $loginHandler; public function setUp(): void { $this->locatorStub = $this->createMock(\RAP\Locator::class); $this->locatorStub->method('getBasePath')->willReturn('http://rap-ia2'); $this->userDaoStub = $this->createMock(\RAP\UserDAO::class); $this->locatorStub->method('getUserDAO')->willReturn($this->userDaoStub); $this->oAuth2RequestHandler = $this->createMock(\RAP\OAuth2RequestHandler::class); $this->locatorStub->method('getOAuth2RequestHandler')->willReturn($this->oAuth2RequestHandler); $this->auditLogger = $this->createMock(\Monolog\Logger::class); $this->locatorStub->method('getAuditLogger')->willReturn($this->auditLogger); $this->gmsClientStub = $this->createMock(\RAP\GmsClient::class); $this->locatorStub->method('getGmsClient')->willReturn($this->gmsClientStub); $this->sessionData = new \RAP\SessionData(); $this->locatorStub->method('getSession')->willReturn($this->sessionData); $this->userHandler = new \RAP\UserHandler($this->locatorStub); $this->locatorStub->method('getUserHandler')->willReturn($this->userHandler); $this->loginHandler = new \RAP\LoginHandler($this->locatorStub); } public function testNewIdentityLogin(): void { $this->sessionData->setAction('account'); $identity = $this->getFakeIdentity1(); // verify DAO is called for checking user existence $this->userDaoStub->expects($this->once()) ->method('findUserByIdentity')->with($identity->type, $identity->typedId); $redirect1 = $this->loginHandler->onIdentityDataReceived($identity); // verify ids are not set $this->assertNull($this->sessionData->getUser()->id); $this->assertNull($this->sessionData->getUser()->identities[0]->id); $this->assertEquals("http://rap-ia2/tou-check", $redirect1); $this->userDaoStub->method('createUser')->willReturn('2'); $this->userDaoStub->method('insertIdentity')->willReturn('3'); $redirect2 = $this->loginHandler->register(); $this->assertEquals('2', $this->sessionData->getUser()->id); $this->assertEquals('3', $this->sessionData->getUser()->identities[0]->id); $this->assertEquals("http://rap-ia2/account", $redirect2); } public function testExistingUserLogin(): void { $this->sessionData->setAction('account'); $user = $this->getFakeUser1(); $this->assertEquals('test@example.com', $user->identities[0]->email); $this->userDaoStub->method('findUserByIdentity')->willReturn($user); $this->userDaoStub->expects($this->once()) ->method('updateIdentity')->with($this->anything()); $redirect = $this->loginHandler->onIdentityDataReceived($this->getFakeIdentity1NewEmail()); // verify email update $this->assertEquals('test2@example.com', $user->identities[0]->email); $this->assertEquals('http://rap-ia2/account', $redirect); } public function testNewIdentityAutojoin(): void { $this->oAuth2RequestHandler->method('getRedirectResponseUrl')->willReturn('http://redirect-url'); $this->sessionData->setOAuth2RequestData(new \RAP\OAuth2RequestData()); $this->userDaoStub->method('findJoinableUsersByEmail')->willReturn(['1', '2']); $this->userDaoStub->method('findUserById')->will($this->returnValueMap([ ['1', $this->getFakeUser1()], ['2', $this->getFakeUser2()] ])); // Login: two joinable users detected $redirect1 = $this->loginHandler->onIdentityDataReceived($this->getFakeIdentity3()); $this->assertTrue($this->sessionData->isAutojoin()); $this->assertEquals('http://rap-ia2/confirm-join', $redirect1); $this->assertNotNull($this->sessionData->getUserToJoin()); $this->assertNull($this->sessionData->getUser()->id); $this->userDaoStub->method('findJoinableUsersByUserId')->will($this->onConsecutiveCalls(['2'], [])); // First confirm join $redirect2 = $this->loginHandler->confirmJoin(); $this->assertEquals('1', $this->sessionData->getUser()->id); $this->assertEquals('http://rap-ia2/confirm-join', $redirect2); $this->gmsClientStub->method('joinGroups')->willReturn('2'); $this->gmsClientStub->expects($this->once()) ->method('joinGroups')->with($this->anything()); // Second confirm join, then redirect to caller application $redirect3 = $this->loginHandler->confirmJoin(); $this->assertEquals('2', $this->sessionData->getUser()->id); $this->assertEquals('http://redirect-url', $redirect3); } public function testJoinAlreadyJoinedUser(): void { // go to account page without joining $user = $this->getFakeUser1(); $this->sessionData->setAction('join'); $this->sessionData->setUser($user); $this->userDaoStub->method('findUserByIdentity')->willReturn($user); $redirect = $this->loginHandler->onIdentityDataReceived($this->getFakeIdentity1()); $this->assertEquals('http://rap-ia2/account', $redirect); } public function testRejectJoinExistingUser(): void { $this->sessionData->setAction('account'); $this->userDaoStub->method('findUserByIdentity')->willReturn($this->getFakeUser1()); $this->userDaoStub->method('findUserById')->willReturn($this->getFakeUser2()); $this->userDaoStub->method('findJoinableUsersByUserId')->will($this->onConsecutiveCalls(['2'], [])); $redirect1 = $this->loginHandler->onIdentityDataReceived($this->getFakeIdentity1()); $this->assertTrue($this->sessionData->isAutojoin()); $this->assertEquals('http://rap-ia2/confirm-join', $redirect1); $this->userDaoStub->expects($this->once())->method('insertRejectedJoin'); // User rejects join, redirect to account page $redirect2 = $this->loginHandler->rejectJoin(); $this->assertEquals('http://rap-ia2/account', $redirect2); } public function testRejectJoinNewUser(): void { $this->sessionData->setAction('account'); $this->userDaoStub->method('findJoinableUsersByEmail')->willReturn(['1']); $this->userDaoStub->method('findUserById')->willReturn($this->getFakeUser1()); // Login: one joinable user detected $redirect1 = $this->loginHandler->onIdentityDataReceived($this->getFakeIdentity3()); $this->assertTrue($this->sessionData->isAutojoin()); $this->assertEquals('http://rap-ia2/confirm-join', $redirect1); // User rejects join, redirect to TOU check $redirect2 = $this->loginHandler->rejectJoin(); $this->assertTrue($this->sessionData->isJoinRejected()); $this->assertEquals('http://rap-ia2/tou-check', $redirect2); $this->userDaoStub->method('createUser')->willReturn('5'); $this->userDaoStub->expects($this->once())->method('insertRejectedJoin'); // User accepts TOU $redirect3 = $this->loginHandler->register(); $this->assertEquals('http://rap-ia2/account', $redirect3); } public function testExplicitJoin(): void { // First login result $this->sessionData->setUser($this->getFakeUser2()); $this->sessionData->setAction('join'); $this->userDaoStub->method('findUserById')->willReturn($this->getFakeUser1()); // Second login $redirect1 = $this->loginHandler->onIdentityDataReceived($this->getFakeIdentity1()); $this->assertFalse($this->sessionData->isAutojoin()); $this->assertEquals('http://rap-ia2/confirm-join', $redirect1); // User confirms the join $redirect2 = $this->loginHandler->confirmJoin(); $this->assertEquals('http://rap-ia2/account', $redirect2); } public function testExplicitJoinAndAutojoin(): void { // First login result $this->sessionData->setUser($this->getFakeUser2()); $this->sessionData->setAction('join'); $this->userDaoStub->method('findUserById')->willReturn($this->getFakeUser1()); $this->userDaoStub->method('findJoinableUsersByUserId')->will($this->onConsecutiveCalls(['2'], [])); // Second login $redirect1 = $this->loginHandler->onIdentityDataReceived($this->getFakeIdentity1()); $this->assertFalse($this->sessionData->isAutojoin()); $this->assertEquals('http://rap-ia2/confirm-join', $redirect1); // User confirms the join, another join is detected $redirect2 = $this->loginHandler->confirmJoin(); $this->assertTrue($this->sessionData->isAutojoin()); $this->assertEquals('http://rap-ia2/confirm-join', $redirect2); // User confirm the second join too $redirect3 = $this->loginHandler->confirmJoin(); $this->assertEquals('http://rap-ia2/account', $redirect3); } public function testExplicitJoinAndRejectedAutojoin(): void { // First login result $this->sessionData->setUser($this->getFakeUser2()); $this->sessionData->setAction('join'); $this->userDaoStub->method('findUserById')->willReturn($this->getFakeUser1()); $this->userDaoStub->method('findJoinableUsersByUserId')->will($this->onConsecutiveCalls(['2'], [])); // Second login $redirect1 = $this->loginHandler->onIdentityDataReceived($this->getFakeIdentity1()); $this->assertFalse($this->sessionData->isAutojoin()); $this->assertEquals('http://rap-ia2/confirm-join', $redirect1); // User confirms the join, another join is detected $redirect2 = $this->loginHandler->confirmJoin(); $this->assertTrue($this->sessionData->isAutojoin()); $this->assertEquals('http://rap-ia2/confirm-join', $redirect2); $this->userDaoStub->expects($this->once())->method('insertRejectedJoin'); // User reject the second join $redirect3 = $this->loginHandler->rejectJoin(); $this->assertEquals('http://rap-ia2/account', $redirect3); } private function getFakeUser1(): \RAP\User { $user = new \RAP\User(); $user->id = '1'; $identity = new \RAP\Identity('eduGAIN'); $identity->email = 'test@example.com'; $identity->id = '4'; $identity->typedId = '123'; $user->addIdentity($identity); return $user; } private function getFakeUser2(): \RAP\User { $user = new \RAP\User(); $user->id = '2'; $identity = new \RAP\Identity('eduGAIN'); $identity->email = 'test@example.com'; $identity->id = '6'; $identity->typedId = '999'; $user->addIdentity($identity); return $user; } private function getFakeIdentity1(): \RAP\Identity { $identity = new \RAP\Identity(\RAP\Identity::EDU_GAIN); $identity->typedId = '123'; $identity->email = 'test@example.com'; return $identity; } private function getFakeIdentity1NewEmail(): \RAP\Identity { $identity = new \RAP\Identity(\RAP\Identity::EDU_GAIN); $identity->typedId = '123'; $identity->email = 'test2@example.com'; return $identity; } private function getFakeIdentity2(): \RAP\Identity { $identity = new \RAP\Identity(\RAP\Identity::EDU_GAIN); $identity->typedId = '456'; $identity->email = 'test3@example.com'; return $identity; } private function getFakeIdentity3(): \RAP\Identity { $identity = new \RAP\Identity(\RAP\Identity::GOOGLE); $identity->typedId = '789'; $identity->email = 'test@example.com'; return $identity; } }