aboutsummaryrefslogtreecommitdiffstats
path: root/tests/Core/Controller/TwoFactorChallengeControllerTest.php
diff options
context:
space:
mode:
Diffstat (limited to 'tests/Core/Controller/TwoFactorChallengeControllerTest.php')
-rw-r--r--tests/Core/Controller/TwoFactorChallengeControllerTest.php446
1 files changed, 446 insertions, 0 deletions
diff --git a/tests/Core/Controller/TwoFactorChallengeControllerTest.php b/tests/Core/Controller/TwoFactorChallengeControllerTest.php
new file mode 100644
index 00000000000..d9ea1ca263f
--- /dev/null
+++ b/tests/Core/Controller/TwoFactorChallengeControllerTest.php
@@ -0,0 +1,446 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+namespace Test\Core\Controller;
+
+use OC\Authentication\TwoFactorAuth\Manager;
+use OC\Authentication\TwoFactorAuth\ProviderSet;
+use OC\Core\Controller\TwoFactorChallengeController;
+use OCP\AppFramework\Http\RedirectResponse;
+use OCP\AppFramework\Http\StandaloneTemplateResponse;
+use OCP\Authentication\TwoFactorAuth\IActivatableAtLogin;
+use OCP\Authentication\TwoFactorAuth\ILoginSetupProvider;
+use OCP\Authentication\TwoFactorAuth\IProvider;
+use OCP\Authentication\TwoFactorAuth\TwoFactorException;
+use OCP\IRequest;
+use OCP\ISession;
+use OCP\IURLGenerator;
+use OCP\IUser;
+use OCP\IUserSession;
+use OCP\Template\ITemplate;
+use Psr\Log\LoggerInterface;
+use Test\TestCase;
+
+class TwoFactorChallengeControllerTest extends TestCase {
+ /** @var IRequest|\PHPUnit\Framework\MockObject\MockObject */
+ private $request;
+
+ /** @var Manager|\PHPUnit\Framework\MockObject\MockObject */
+ private $twoFactorManager;
+
+ /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
+ private $userSession;
+
+ /** @var ISession|\PHPUnit\Framework\MockObject\MockObject */
+ private $session;
+
+ /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
+ private $urlGenerator;
+
+ /** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */
+ private $logger;
+
+ /** @var TwoFactorChallengeController|\PHPUnit\Framework\MockObject\MockObject */
+ private $controller;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->request = $this->createMock(IRequest::class);
+ $this->twoFactorManager = $this->createMock(Manager::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->session = $this->createMock(ISession::class);
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+
+ $this->controller = $this->getMockBuilder(TwoFactorChallengeController::class)
+ ->setConstructorArgs([
+ 'core',
+ $this->request,
+ $this->twoFactorManager,
+ $this->userSession,
+ $this->session,
+ $this->urlGenerator,
+ $this->logger,
+ ])
+ ->onlyMethods(['getLogoutUrl'])
+ ->getMock();
+ $this->controller->expects($this->any())
+ ->method('getLogoutUrl')
+ ->willReturn('logoutAttribute');
+ }
+
+ public function testSelectChallenge(): void {
+ $user = $this->getMockBuilder(IUser::class)->getMock();
+ $p1 = $this->createMock(IActivatableAtLogin::class);
+ $p1->method('getId')->willReturn('p1');
+ $backupProvider = $this->createMock(IProvider::class);
+ $backupProvider->method('getId')->willReturn('backup_codes');
+ $providerSet = new ProviderSet([$p1, $backupProvider], true);
+ $this->twoFactorManager->expects($this->once())
+ ->method('getLoginSetupProviders')
+ ->with($user)
+ ->willReturn([$p1]);
+
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($user);
+ $this->twoFactorManager->expects($this->once())
+ ->method('getProviderSet')
+ ->with($user)
+ ->willReturn($providerSet);
+
+ $expected = new StandaloneTemplateResponse('core', 'twofactorselectchallenge', [
+ 'providers' => [
+ $p1,
+ ],
+ 'providerMissing' => true,
+ 'backupProvider' => $backupProvider,
+ 'redirect_url' => '/some/url',
+ 'logout_url' => 'logoutAttribute',
+ 'hasSetupProviders' => true,
+ ], 'guest');
+
+ $this->assertEquals($expected, $this->controller->selectChallenge('/some/url'));
+ }
+
+ public function testShowChallenge(): void {
+ $user = $this->createMock(IUser::class);
+ $provider = $this->createMock(IProvider::class);
+ $provider->method('getId')->willReturn('myprovider');
+ $backupProvider = $this->createMock(IProvider::class);
+ $backupProvider->method('getId')->willReturn('backup_codes');
+ $tmpl = $this->createMock(ITemplate::class);
+ $providerSet = new ProviderSet([$provider, $backupProvider], true);
+
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($user);
+ $this->twoFactorManager->expects($this->once())
+ ->method('getProviderSet')
+ ->with($user)
+ ->willReturn($providerSet);
+ $provider->expects($this->once())
+ ->method('getId')
+ ->willReturn('u2f');
+ $backupProvider->expects($this->once())
+ ->method('getId')
+ ->willReturn('backup_codes');
+
+ $this->session->expects($this->once())
+ ->method('exists')
+ ->with('two_factor_auth_error')
+ ->willReturn(true);
+ $this->session->expects($this->exactly(2))
+ ->method('remove')
+ ->with($this->logicalOr($this->equalTo('two_factor_auth_error'), $this->equalTo('two_factor_auth_error_message')));
+ $provider->expects($this->once())
+ ->method('getTemplate')
+ ->with($user)
+ ->willReturn($tmpl);
+ $tmpl->expects($this->once())
+ ->method('fetchPage')
+ ->willReturn('<html/>');
+
+ $expected = new StandaloneTemplateResponse('core', 'twofactorshowchallenge', [
+ 'error' => true,
+ 'provider' => $provider,
+ 'backupProvider' => $backupProvider,
+ 'logout_url' => 'logoutAttribute',
+ 'template' => '<html/>',
+ 'redirect_url' => '/re/dir/ect/url',
+ 'error_message' => null,
+ ], 'guest');
+
+ $this->assertEquals($expected, $this->controller->showChallenge('myprovider', '/re/dir/ect/url'));
+ }
+
+ public function testShowInvalidChallenge(): void {
+ $user = $this->createMock(IUser::class);
+ $providerSet = new ProviderSet([], false);
+
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($user);
+ $this->twoFactorManager->expects($this->once())
+ ->method('getProviderSet')
+ ->with($user)
+ ->willReturn($providerSet);
+ $this->urlGenerator->expects($this->once())
+ ->method('linkToRoute')
+ ->with('core.TwoFactorChallenge.selectChallenge')
+ ->willReturn('select/challenge/url');
+
+ $expected = new RedirectResponse('select/challenge/url');
+
+ $this->assertEquals($expected, $this->controller->showChallenge('myprovider', 'redirect/url'));
+ }
+
+ public function testSolveChallenge(): void {
+ $user = $this->createMock(IUser::class);
+ $provider = $this->createMock(IProvider::class);
+
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($user);
+ $this->twoFactorManager->expects($this->once())
+ ->method('getProvider')
+ ->with($user, 'myprovider')
+ ->willReturn($provider);
+
+ $this->twoFactorManager->expects($this->once())
+ ->method('verifyChallenge')
+ ->with('myprovider', $user, 'token')
+ ->willReturn(true);
+ $this->urlGenerator
+ ->expects($this->once())
+ ->method('linkToDefaultPageUrl')
+ ->willReturn('/default/foo');
+
+ $expected = new RedirectResponse('/default/foo');
+ $this->assertEquals($expected, $this->controller->solveChallenge('myprovider', 'token'));
+ }
+
+ public function testSolveValidChallengeAndRedirect(): void {
+ $user = $this->createMock(IUser::class);
+ $provider = $this->createMock(IProvider::class);
+
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($user);
+ $this->twoFactorManager->expects($this->once())
+ ->method('getProvider')
+ ->with($user, 'myprovider')
+ ->willReturn($provider);
+
+ $this->twoFactorManager->expects($this->once())
+ ->method('verifyChallenge')
+ ->with('myprovider', $user, 'token')
+ ->willReturn(true);
+ $this->urlGenerator->expects($this->once())
+ ->method('getAbsoluteURL')
+ ->with('redirect url')
+ ->willReturn('redirect/url');
+
+ $expected = new RedirectResponse('redirect/url');
+ $this->assertEquals($expected, $this->controller->solveChallenge('myprovider', 'token', 'redirect%20url'));
+ }
+
+ public function testSolveChallengeInvalidProvider(): void {
+ $user = $this->getMockBuilder(IUser::class)->getMock();
+
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($user);
+ $this->twoFactorManager->expects($this->once())
+ ->method('getProvider')
+ ->with($user, 'myprovider')
+ ->willReturn(null);
+ $this->urlGenerator->expects($this->once())
+ ->method('linkToRoute')
+ ->with('core.TwoFactorChallenge.selectChallenge')
+ ->willReturn('select/challenge/url');
+
+ $expected = new RedirectResponse('select/challenge/url');
+
+ $this->assertEquals($expected, $this->controller->solveChallenge('myprovider', 'token'));
+ }
+
+ public function testSolveInvalidChallenge(): void {
+ $user = $this->createMock(IUser::class);
+ $provider = $this->createMock(IProvider::class);
+
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($user);
+ $this->twoFactorManager->expects($this->once())
+ ->method('getProvider')
+ ->with($user, 'myprovider')
+ ->willReturn($provider);
+
+ $this->twoFactorManager->expects($this->once())
+ ->method('verifyChallenge')
+ ->with('myprovider', $user, 'token')
+ ->willReturn(false);
+ $this->session->expects($this->once())
+ ->method('set')
+ ->with('two_factor_auth_error', true);
+ $this->urlGenerator->expects($this->once())
+ ->method('linkToRoute')
+ ->with('core.TwoFactorChallenge.showChallenge', [
+ 'challengeProviderId' => 'myprovider',
+ 'redirect_url' => '/url',
+ ])
+ ->willReturn('files/index/url');
+ $provider->expects($this->once())
+ ->method('getId')
+ ->willReturn('myprovider');
+
+ $expected = new RedirectResponse('files/index/url');
+ $this->assertEquals($expected, $this->controller->solveChallenge('myprovider', 'token', '/url'));
+ }
+
+ public function testSolveChallengeTwoFactorException(): void {
+ $user = $this->createMock(IUser::class);
+ $provider = $this->createMock(IProvider::class);
+ $exception = new TwoFactorException('2FA failed');
+
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($user);
+ $this->twoFactorManager->expects($this->once())
+ ->method('getProvider')
+ ->with($user, 'myprovider')
+ ->willReturn($provider);
+
+ $this->twoFactorManager->expects($this->once())
+ ->method('verifyChallenge')
+ ->with('myprovider', $user, 'token')
+ ->willThrowException($exception);
+ $calls = [
+ ['two_factor_auth_error_message', '2FA failed'],
+ ['two_factor_auth_error', true],
+ ];
+ $this->session->expects($this->exactly(2))
+ ->method('set')
+ ->willReturnCallback(function () use (&$calls): void {
+ $expected = array_shift($calls);
+ $this->assertEquals($expected, func_get_args());
+ });
+ $this->urlGenerator->expects($this->once())
+ ->method('linkToRoute')
+ ->with('core.TwoFactorChallenge.showChallenge', [
+ 'challengeProviderId' => 'myprovider',
+ 'redirect_url' => '/url',
+ ])
+ ->willReturn('files/index/url');
+ $provider->expects($this->once())
+ ->method('getId')
+ ->willReturn('myprovider');
+
+ $expected = new RedirectResponse('files/index/url');
+ $this->assertEquals($expected, $this->controller->solveChallenge('myprovider', 'token', '/url'));
+ }
+
+ public function testSetUpProviders(): void {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($user);
+ $provider = $this->createMock(IActivatableAtLogin::class);
+ $this->twoFactorManager->expects($this->once())
+ ->method('getLoginSetupProviders')
+ ->with($user)
+ ->willReturn([
+ $provider,
+ ]);
+ $expected = new StandaloneTemplateResponse(
+ 'core',
+ 'twofactorsetupselection',
+ [
+ 'providers' => [
+ $provider,
+ ],
+ 'logout_url' => 'logoutAttribute',
+ 'redirect_url' => null,
+ ],
+ 'guest'
+ );
+
+ $response = $this->controller->setupProviders();
+
+ $this->assertEquals($expected, $response);
+ }
+
+ public function testSetUpInvalidProvider(): void {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($user);
+ $provider = $this->createMock(IActivatableAtLogin::class);
+ $provider->expects($this->any())
+ ->method('getId')
+ ->willReturn('prov1');
+ $this->twoFactorManager->expects($this->once())
+ ->method('getLoginSetupProviders')
+ ->with($user)
+ ->willReturn([
+ $provider,
+ ]);
+ $this->urlGenerator->expects($this->once())
+ ->method('linkToRoute')
+ ->with('core.TwoFactorChallenge.selectChallenge')
+ ->willReturn('2fa/select/page');
+ $expected = new RedirectResponse('2fa/select/page');
+
+ $response = $this->controller->setupProvider('prov2');
+
+ $this->assertEquals($expected, $response);
+ }
+
+ public function testSetUpProvider(): void {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($user);
+ $provider = $this->createMock(IActivatableAtLogin::class);
+ $provider->expects($this->any())
+ ->method('getId')
+ ->willReturn('prov1');
+ $this->twoFactorManager->expects($this->once())
+ ->method('getLoginSetupProviders')
+ ->with($user)
+ ->willReturn([
+ $provider,
+ ]);
+ $loginSetup = $this->createMock(ILoginSetupProvider::class);
+ $provider->expects($this->any())
+ ->method('getLoginSetup')
+ ->with($user)
+ ->willReturn($loginSetup);
+ $tmpl = $this->createMock(ITemplate::class);
+ $loginSetup->expects($this->once())
+ ->method('getBody')
+ ->willReturn($tmpl);
+ $tmpl->expects($this->once())
+ ->method('fetchPage')
+ ->willReturn('tmpl');
+ $expected = new StandaloneTemplateResponse(
+ 'core',
+ 'twofactorsetupchallenge',
+ [
+ 'provider' => $provider,
+ 'logout_url' => 'logoutAttribute',
+ 'template' => 'tmpl',
+ 'redirect_url' => null,
+ ],
+ 'guest'
+ );
+
+ $response = $this->controller->setupProvider('prov1');
+
+ $this->assertEquals($expected, $response);
+ }
+
+ public function testConfirmProviderSetup(): void {
+ $this->urlGenerator->expects($this->once())
+ ->method('linkToRoute')
+ ->with(
+ 'core.TwoFactorChallenge.showChallenge',
+ [
+ 'challengeProviderId' => 'totp',
+ 'redirect_url' => null,
+ ])
+ ->willReturn('2fa/select/page');
+ $expected = new RedirectResponse('2fa/select/page');
+
+ $response = $this->controller->confirmProviderSetup('totp');
+
+ $this->assertEquals($expected, $response);
+ }
+}