Loading classes/datalayer/UserDAO.php +3 −1 Original line number Diff line number Diff line Loading @@ -83,4 +83,6 @@ interface UserDAO { function findJoinableUsersByEmail(string $email): array; function findJoinableUsersByUserId(string $userId): array; function insertRejectedJoin(string $userId1, string $userId2): void; } classes/datalayer/mysql/MySQLUserDAO.php +18 −4 Original line number Diff line number Diff line Loading @@ -351,4 +351,18 @@ class MySQLUserDAO extends BaseMySQLDAO implements UserDAO { return $results; } function insertRejectedJoin(string $userId1, string $userId2): void { $dbh = $this->getDBHandler(); $query = "INSERT INTO keep_separated (user_id1, user_id2) VALUES (:id1, :id2)\n" . "ON DUPLICATE KEY UPDATE user_id1 = user_id1, user_id2 = user_id2"; $stmt = $dbh->prepare($query); $stmt->bindParam(':id1', $userId1); $stmt->bindParam(':id2', $userId2); $stmt->execute(); } } classes/login/LoginHandler.php +27 −12 Original line number Diff line number Diff line Loading @@ -78,11 +78,6 @@ class LoginHandler { $this->joinUsers(); $autoJoinStep = $this->checkAutoJoin(); if ($autoJoinStep !== null) { return $autoJoinStep; } return $this->getAfterLoginRedirect(); } Loading @@ -105,6 +100,7 @@ class LoginHandler { return $this->showConfirmJoin($userToJoin); } $session->setAutojoin(false); return null; } Loading @@ -120,8 +116,9 @@ class LoginHandler { $session->setJoinRejected(true); if ($session->getUser()->id === null) { return $this->redirectToTOUCheck($session->getUser()->identities[0]); return $this->redirectToTOUCheck(); } else { $this->saveRejectedJoinIfPossible(); return $this->getAfterLoginRedirect(); } } Loading Loading @@ -158,12 +155,8 @@ class LoginHandler { $user = $session->getUser(); $userToJoin = $session->getUserToJoin(); if ($user === null) { $session->setUser($userToJoin); } else { $joinedUser = $this->locator->getUserHandler()->joinUsers($userToJoin, $user); $session->setUser($joinedUser); } if ($session->getAction() === 'join') { $session->setAction('account'); Loading @@ -174,7 +167,15 @@ class LoginHandler { private function getAfterLoginRedirect(): string { $autoJoinStep = $this->checkAutoJoin(); if ($autoJoinStep !== null) { return $autoJoinStep; } $session = $this->locator->getSession(); $this->saveRejectedJoinIfPossible(); $this->locator->getAuditLogger()->info("LOGIN," . $session->getLoginIdentityType() . "," . $session->getUser()->id); if ($session->getOAuth2RequestData() !== null) { Loading @@ -193,4 +194,18 @@ class LoginHandler { throw new \Exception("Unable to find a proper redirect"); } private function saveRejectedJoinIfPossible(): void { $session = $this->locator->getSession(); if ($session->isJoinRejected() && $session->getUserToJoin() !== null) { $id1 = $session->getUser()->id; $id2 = $session->getUserToJoin()->id; if ($id1 !== null && $id2 !== null) { $this->locator->getUserDAO()->insertRejectedJoin($id1, $id2); $session->setJoinRejected(false); } } } } tests/LoginFlowTest.php +150 −19 Original line number Diff line number Diff line Loading @@ -99,10 +99,16 @@ final class LoginFlowTest extends TestCase { 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')->willReturn($this->getFakeUser1()); $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()); Loading @@ -110,8 +116,9 @@ final class LoginFlowTest extends TestCase { $this->assertNotNull($this->sessionData->getUserToJoin()); $this->assertNull($this->sessionData->getUser()->id); $this->userDaoStub->method('findJoinableUsersByUserId')->willReturn(['2']); $this->userDaoStub->method('findJoinableUsersByUserId')->will($this->onConsecutiveCalls(['2'], [])); // First confirm join $redirect2 = $this->loginHandler->confirmJoin(); $this->assertEquals('1', $this->sessionData->getUser()->id); Loading @@ -122,6 +129,7 @@ final class LoginFlowTest extends TestCase { $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); Loading @@ -141,6 +149,129 @@ final class LoginFlowTest extends TestCase { $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(); Loading tests/LoginHandlerTest.php +17 −7 Original line number Diff line number Diff line Loading @@ -12,7 +12,6 @@ final class LoginHandlerTest extends TestCase { private $userDaoStub; private $sessionStub; private $userHandlerStub; private $oAuth2RequestHandler; private $auditLogger; private $loginHandler; Loading @@ -29,9 +28,6 @@ final class LoginHandlerTest extends TestCase { $this->userHandlerStub = $this->createMock(\RAP\UserHandler::class); $this->locatorStub->method('getUserHandler')->willReturn($this->userHandlerStub); $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); Loading @@ -44,14 +40,13 @@ final class LoginHandlerTest extends TestCase { $this->userDaoStub->method('findUserByIdentity')->willReturn($user); $this->oAuth2RequestHandler->method('getRedirectResponseUrl')->willReturn('http://redirect-url'); $this->sessionStub->method('getAction')->willReturn('account'); $this->sessionStub->method('getOAuth2RequestData')->willReturn(new \RAP\OAuth2RequestData()); $this->sessionStub->method('getUser')->willReturn($user); $redirect = $this->loginHandler->onIdentityDataReceived($this->getFakeIdentity1()); $this->assertEquals('http://redirect-url', $redirect); $this->assertEquals('http://rap-ia2/account', $redirect); } public function testShowConfirmJoinNewIdentity(): void { Loading Loading @@ -85,6 +80,21 @@ final class LoginHandlerTest extends TestCase { $this->assertEquals('http://rap-ia2/confirm-join', $redirect); } public function testRegisterFailWithoutSession(): void { $this->expectException(\RAP\BadRequestException::class); $this->loginHandler->register(); } public function testConfirmJoinFailWithoutSession(): void { $this->expectException(\RAP\BadRequestException::class); $this->loginHandler->confirmJoin(); } public function testRejectJoinFailWithoutSession(): void { $this->expectException(\RAP\BadRequestException::class); $this->loginHandler->rejectJoin(); } private function getFakeUser(): \RAP\User { $user = new \RAP\User(); Loading Loading
classes/datalayer/UserDAO.php +3 −1 Original line number Diff line number Diff line Loading @@ -83,4 +83,6 @@ interface UserDAO { function findJoinableUsersByEmail(string $email): array; function findJoinableUsersByUserId(string $userId): array; function insertRejectedJoin(string $userId1, string $userId2): void; }
classes/datalayer/mysql/MySQLUserDAO.php +18 −4 Original line number Diff line number Diff line Loading @@ -351,4 +351,18 @@ class MySQLUserDAO extends BaseMySQLDAO implements UserDAO { return $results; } function insertRejectedJoin(string $userId1, string $userId2): void { $dbh = $this->getDBHandler(); $query = "INSERT INTO keep_separated (user_id1, user_id2) VALUES (:id1, :id2)\n" . "ON DUPLICATE KEY UPDATE user_id1 = user_id1, user_id2 = user_id2"; $stmt = $dbh->prepare($query); $stmt->bindParam(':id1', $userId1); $stmt->bindParam(':id2', $userId2); $stmt->execute(); } }
classes/login/LoginHandler.php +27 −12 Original line number Diff line number Diff line Loading @@ -78,11 +78,6 @@ class LoginHandler { $this->joinUsers(); $autoJoinStep = $this->checkAutoJoin(); if ($autoJoinStep !== null) { return $autoJoinStep; } return $this->getAfterLoginRedirect(); } Loading @@ -105,6 +100,7 @@ class LoginHandler { return $this->showConfirmJoin($userToJoin); } $session->setAutojoin(false); return null; } Loading @@ -120,8 +116,9 @@ class LoginHandler { $session->setJoinRejected(true); if ($session->getUser()->id === null) { return $this->redirectToTOUCheck($session->getUser()->identities[0]); return $this->redirectToTOUCheck(); } else { $this->saveRejectedJoinIfPossible(); return $this->getAfterLoginRedirect(); } } Loading Loading @@ -158,12 +155,8 @@ class LoginHandler { $user = $session->getUser(); $userToJoin = $session->getUserToJoin(); if ($user === null) { $session->setUser($userToJoin); } else { $joinedUser = $this->locator->getUserHandler()->joinUsers($userToJoin, $user); $session->setUser($joinedUser); } if ($session->getAction() === 'join') { $session->setAction('account'); Loading @@ -174,7 +167,15 @@ class LoginHandler { private function getAfterLoginRedirect(): string { $autoJoinStep = $this->checkAutoJoin(); if ($autoJoinStep !== null) { return $autoJoinStep; } $session = $this->locator->getSession(); $this->saveRejectedJoinIfPossible(); $this->locator->getAuditLogger()->info("LOGIN," . $session->getLoginIdentityType() . "," . $session->getUser()->id); if ($session->getOAuth2RequestData() !== null) { Loading @@ -193,4 +194,18 @@ class LoginHandler { throw new \Exception("Unable to find a proper redirect"); } private function saveRejectedJoinIfPossible(): void { $session = $this->locator->getSession(); if ($session->isJoinRejected() && $session->getUserToJoin() !== null) { $id1 = $session->getUser()->id; $id2 = $session->getUserToJoin()->id; if ($id1 !== null && $id2 !== null) { $this->locator->getUserDAO()->insertRejectedJoin($id1, $id2); $session->setJoinRejected(false); } } } }
tests/LoginFlowTest.php +150 −19 Original line number Diff line number Diff line Loading @@ -99,10 +99,16 @@ final class LoginFlowTest extends TestCase { 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')->willReturn($this->getFakeUser1()); $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()); Loading @@ -110,8 +116,9 @@ final class LoginFlowTest extends TestCase { $this->assertNotNull($this->sessionData->getUserToJoin()); $this->assertNull($this->sessionData->getUser()->id); $this->userDaoStub->method('findJoinableUsersByUserId')->willReturn(['2']); $this->userDaoStub->method('findJoinableUsersByUserId')->will($this->onConsecutiveCalls(['2'], [])); // First confirm join $redirect2 = $this->loginHandler->confirmJoin(); $this->assertEquals('1', $this->sessionData->getUser()->id); Loading @@ -122,6 +129,7 @@ final class LoginFlowTest extends TestCase { $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); Loading @@ -141,6 +149,129 @@ final class LoginFlowTest extends TestCase { $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(); Loading
tests/LoginHandlerTest.php +17 −7 Original line number Diff line number Diff line Loading @@ -12,7 +12,6 @@ final class LoginHandlerTest extends TestCase { private $userDaoStub; private $sessionStub; private $userHandlerStub; private $oAuth2RequestHandler; private $auditLogger; private $loginHandler; Loading @@ -29,9 +28,6 @@ final class LoginHandlerTest extends TestCase { $this->userHandlerStub = $this->createMock(\RAP\UserHandler::class); $this->locatorStub->method('getUserHandler')->willReturn($this->userHandlerStub); $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); Loading @@ -44,14 +40,13 @@ final class LoginHandlerTest extends TestCase { $this->userDaoStub->method('findUserByIdentity')->willReturn($user); $this->oAuth2RequestHandler->method('getRedirectResponseUrl')->willReturn('http://redirect-url'); $this->sessionStub->method('getAction')->willReturn('account'); $this->sessionStub->method('getOAuth2RequestData')->willReturn(new \RAP\OAuth2RequestData()); $this->sessionStub->method('getUser')->willReturn($user); $redirect = $this->loginHandler->onIdentityDataReceived($this->getFakeIdentity1()); $this->assertEquals('http://redirect-url', $redirect); $this->assertEquals('http://rap-ia2/account', $redirect); } public function testShowConfirmJoinNewIdentity(): void { Loading Loading @@ -85,6 +80,21 @@ final class LoginHandlerTest extends TestCase { $this->assertEquals('http://rap-ia2/confirm-join', $redirect); } public function testRegisterFailWithoutSession(): void { $this->expectException(\RAP\BadRequestException::class); $this->loginHandler->register(); } public function testConfirmJoinFailWithoutSession(): void { $this->expectException(\RAP\BadRequestException::class); $this->loginHandler->confirmJoin(); } public function testRejectJoinFailWithoutSession(): void { $this->expectException(\RAP\BadRequestException::class); $this->loginHandler->rejectJoin(); } private function getFakeUser(): \RAP\User { $user = new \RAP\User(); Loading