Skip to content

Commit d0355a1

Browse files
committed
Create tests for MFA pages
Signed-off-by: Tim Goudriaan <tim@codedmonkey.com>
1 parent 4536039 commit d0355a1

3 files changed

Lines changed: 207 additions & 5 deletions

File tree

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
<?php
2+
3+
namespace CodedMonkey\Dirigent\Tests\FunctionalTests\Controller\Dashboard;
4+
5+
use CodedMonkey\Dirigent\Controller\Dashboard\DashboardAccountController;
6+
use CodedMonkey\Dirigent\Doctrine\Entity\User;
7+
use CodedMonkey\Dirigent\Tests\Helper\MockEntityFactoryTrait;
8+
use CodedMonkey\Dirigent\Tests\Helper\WebTestCaseTrait;
9+
use Doctrine\ORM\EntityManagerInterface;
10+
use PHPUnit\Framework\Attributes\CoversClass;
11+
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Totp\TotpFactory;
12+
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
13+
use Symfony\Component\HttpFoundation\Response;
14+
15+
#[CoversClass(DashboardAccountController::class)]
16+
class DashboardAccountControllerTest extends WebTestCase
17+
{
18+
use MockEntityFactoryTrait;
19+
use WebTestCaseTrait;
20+
21+
public function testMfaUnauthenticated(): void
22+
{
23+
$client = static::createClient();
24+
25+
$client->request('GET', '/account/mfa');
26+
27+
$this->assertResponseRedirects('/login', Response::HTTP_FOUND);
28+
}
29+
30+
public function testMfaSetup(): void
31+
{
32+
$client = static::createClient();
33+
$totpFactory = $this->getService(TotpFactory::class, 'scheb_two_factor.security.totp_factory');
34+
35+
$user = $this->createMockUser();
36+
$this->persistEntities($user);
37+
38+
$client->loginUser($user);
39+
40+
$client->request('GET', '/account/mfa');
41+
42+
$client->submitForm('Enable MFA authentication', [
43+
'mfa_setup_form[currentPassword]' => 'PlainPassword99',
44+
'mfa_setup_form[totpCode]' => $totpFactory->createTotpForUser($user)->now(),
45+
]);
46+
47+
$this->assertResponseRedirects('/account', Response::HTTP_FOUND);
48+
49+
$this->clearEntities();
50+
$user = $this->getService(EntityManagerInterface::class)->find(User::class, $user->getId());
51+
52+
$this->assertNotNull($user->getTotpSecret());
53+
}
54+
55+
public function testMfaSetupWrongPassword(): void
56+
{
57+
$client = static::createClient();
58+
$totpFactory = $this->getService(TotpFactory::class, 'scheb_two_factor.security.totp_factory');
59+
60+
$user = $this->createMockUser();
61+
$this->persistEntities($user);
62+
63+
$client->loginUser($user);
64+
65+
$client->request('GET', '/account/mfa');
66+
67+
$client->submitForm('Enable MFA authentication', [
68+
'mfa_setup_form[currentPassword]' => 'OddPassword11',
69+
'mfa_setup_form[totpCode]' => $totpFactory->createTotpForUser($user)->now(),
70+
]);
71+
72+
$this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY);
73+
74+
$this->clearEntities();
75+
$user = $this->getService(EntityManagerInterface::class)->find(User::class, $user->getId());
76+
77+
$this->assertNull($user->getTotpSecret());
78+
}
79+
80+
public function testMfaSetupWrongTotpCode(): void
81+
{
82+
$client = static::createClient();
83+
84+
$user = $this->createMockUser();
85+
$this->persistEntities($user);
86+
87+
$client->loginUser($user);
88+
89+
$client->request('GET', '/account/mfa');
90+
91+
$client->submitForm('Enable MFA authentication', [
92+
'mfa_setup_form[currentPassword]' => 'PlainPassword99',
93+
'mfa_setup_form[totpCode]' => 'abcdef',
94+
]);
95+
96+
$this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY);
97+
98+
$this->clearEntities();
99+
$user = $this->getService(EntityManagerInterface::class)->find(User::class, $user->getId());
100+
101+
$this->assertNull($user->getTotpSecret());
102+
}
103+
104+
public function testMfaClear(): void
105+
{
106+
$client = static::createClient();
107+
108+
$user = $this->createMockUser(mfaEnabled: true);
109+
$this->persistEntities($user);
110+
111+
$client->loginUser($user);
112+
113+
$client->request('GET', '/account/mfa');
114+
115+
$client->submitForm('Disable MFA authentication', [
116+
'mfa_clear_form[currentPassword]' => 'PlainPassword99',
117+
]);
118+
119+
$this->assertResponseRedirects('/account', Response::HTTP_FOUND);
120+
121+
$this->clearEntities();
122+
$user = $this->getService(EntityManagerInterface::class)->find(User::class, $user->getId());
123+
124+
$this->assertNull($user->getTotpSecret());
125+
}
126+
127+
public function testMfaClearWrongPassword(): void
128+
{
129+
$client = static::createClient();
130+
131+
$user = $this->createMockUser(mfaEnabled: true);
132+
$this->persistEntities($user);
133+
134+
$client->loginUser($user);
135+
136+
$client->request('GET', '/account/mfa');
137+
138+
$client->submitForm('Disable MFA authentication', [
139+
'mfa_clear_form[currentPassword]' => 'OddPassword11',
140+
]);
141+
142+
$this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY);
143+
144+
$this->clearEntities();
145+
$user = $this->getService(EntityManagerInterface::class)->find(User::class, $user->getId());
146+
147+
$this->assertNotNull($user->getTotpSecret());
148+
}
149+
150+
public function testMfaQrCode(): void
151+
{
152+
$client = static::createClient();
153+
$this->loginUser();
154+
155+
// Set TOTP secret to session
156+
$client->request('GET', '/account/mfa');
157+
158+
$client->request('GET', '/account/mfa/qr-code');
159+
160+
$this->assertResponseStatusCodeSame(Response::HTTP_OK);
161+
}
162+
163+
public function testMfaQrCodeUnauthenticated(): void
164+
{
165+
$client = static::createClient();
166+
167+
$client->request('GET', '/account/mfa/qr-code');
168+
169+
$this->assertResponseRedirects('/login', Response::HTTP_FOUND);
170+
}
171+
172+
public function testMfaQrCodeNoSetup(): void
173+
{
174+
$client = static::createClient();
175+
$this->loginUser();
176+
177+
$client->request('GET', '/account/mfa/qr-code');
178+
179+
$this->assertResponseStatusCodeSame(Response::HTTP_FORBIDDEN);
180+
}
181+
}

tests/FunctionalTests/Controller/Dashboard/DashboardSecurityControllerTest.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@ public function testLoginRedirect(): void
1818
$client = static::createClient();
1919
$client->request('GET', '/');
2020

21-
$this->assertResponseStatusCodeSame(Response::HTTP_FOUND);
22-
23-
$client->followRedirect();
24-
25-
self::assertSame('/login', $client->getRequest()->getRequestUri());
21+
$this->assertResponseRedirects('/login', Response::HTTP_FOUND);
2622
}
2723
}

tests/Helper/MockEntityFactoryTrait.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
namespace CodedMonkey\Dirigent\Tests\Helper;
44

55
use CodedMonkey\Dirigent\Doctrine\Entity\Package;
6+
use CodedMonkey\Dirigent\Doctrine\Entity\User;
67
use CodedMonkey\Dirigent\Doctrine\Entity\Version;
78
use Composer\Semver\VersionParser;
89
use Doctrine\ORM\EntityManagerInterface;
10+
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Totp\TotpAuthenticator;
911

1012
trait MockEntityFactoryTrait
1113
{
@@ -17,6 +19,22 @@ protected function createMockPackage(): Package
1719
return $package;
1820
}
1921

22+
protected function createMockUser(bool $mfaEnabled = false): User
23+
{
24+
$user = new User();
25+
26+
$user->setUsername(uniqid());
27+
$user->setPlainPassword('PlainPassword99');
28+
29+
if ($mfaEnabled) {
30+
$totpAuthenticator = $this->getService(TotpAuthenticator::class);
31+
32+
$user->setTotpSecret($totpAuthenticator->generateSecret());
33+
}
34+
35+
return $user;
36+
}
37+
2038
protected function createMockVersion(Package $package, string $versionName = '1.0.0'): Version
2139
{
2240
$version = new Version();
@@ -46,4 +64,11 @@ protected function persistEntities(...$entities): void
4664

4765
$entityManager->flush();
4866
}
67+
68+
protected function clearEntities(): void
69+
{
70+
$entityManager = $this->getService(EntityManagerInterface::class);
71+
72+
$entityManager->clear();
73+
}
4974
}

0 commit comments

Comments
 (0)