Skip to content

Commit 9696cf5

Browse files
dt-thomas-durandscheb
authored andcommitted
feat: added condition skipping 2fa event
1 parent 0e0515a commit 9696cf5

File tree

5 files changed

+45
-7
lines changed

5 files changed

+45
-7
lines changed

doc/events.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ Event class: ``\Scheb\TwoFactorBundle\Security\TwoFactor\Event\TwoFactorCodeReus
9090
Is dispatched when the code has already been used within the configured time frame.
9191
This requires a caching backend to be available
9292

93+
``scheb_two_factor.authentication.skipped``
94+
~~~~~~~~~~~~~~~~~~~~~~~~~~
95+
96+
Constant: ``Scheb\TwoFactorBundle\Security\TwoFactor\Event\TwoFactorAuthenticationEvents::SKIPPED``
97+
98+
Event class: ``\Scheb\TwoFactorBundle\Security\TwoFactor\Event\TwoFactorAuthenticationEvent``
99+
100+
Is dispatched when any ``\Scheb\TwoFactorBundle\Security\TwoFactor\Condition\TwoFactorConditionInterface`` prevent the two factor process to start by returning false.
101+
93102

94103
Backup Code Events
95104
------------------

src/bundle/Resources/config/two_factor.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
->lazy(true)
4040
->args([
4141
abstract_arg('Two-factor conditions'),
42+
service('event_dispatcher'),
4243
])
4344

4445
->set('scheb_two_factor.authenticated_token_condition', AuthenticatedTokenCondition::class)

src/bundle/Security/TwoFactor/Condition/TwoFactorConditionRegistry.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
namespace Scheb\TwoFactorBundle\Security\TwoFactor\Condition;
66

77
use Scheb\TwoFactorBundle\Security\TwoFactor\AuthenticationContextInterface;
8+
use Scheb\TwoFactorBundle\Security\TwoFactor\Event\TwoFactorAuthenticationEvent;
9+
use Scheb\TwoFactorBundle\Security\TwoFactor\Event\TwoFactorAuthenticationEvents;
10+
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
811

912
/**
1013
* @final
@@ -14,14 +17,19 @@ class TwoFactorConditionRegistry
1417
/**
1518
* @param TwoFactorConditionInterface[] $conditions
1619
*/
17-
public function __construct(private readonly iterable $conditions)
18-
{
20+
public function __construct(
21+
private readonly iterable $conditions,
22+
private readonly EventDispatcherInterface $eventDispatcher,
23+
) {
1924
}
2025

2126
public function shouldPerformTwoFactorAuthentication(AuthenticationContextInterface $context): bool
2227
{
2328
foreach ($this->conditions as $condition) {
2429
if (!$condition->shouldPerformTwoFactorAuthentication($context)) {
30+
$event = new TwoFactorAuthenticationEvent($context->getRequest(), $context->getToken());
31+
$this->eventDispatcher->dispatch($event, TwoFactorAuthenticationEvents::SKIPPED);
32+
2533
return false;
2634
}
2735
}

src/bundle/Security/TwoFactor/Event/TwoFactorAuthenticationEvents.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,9 @@ class TwoFactorAuthenticationEvents
5151
* When the two-factor code has been used already.
5252
*/
5353
public const string CODE_REUSED = 'scheb_two_factor.authentication.code_reused';
54+
55+
/**
56+
* When the two-factor process will not start because at least one 2fa condition is not met.
57+
*/
58+
public const string SKIPPED = 'scheb_two_factor.authentication.skipped';
5459
}

tests/Security/TwoFactor/Condition/TwoFactorConditionRegistryTest.php

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,16 @@
1010
use Scheb\TwoFactorBundle\Security\TwoFactor\AuthenticationContextInterface;
1111
use Scheb\TwoFactorBundle\Security\TwoFactor\Condition\TwoFactorConditionInterface;
1212
use Scheb\TwoFactorBundle\Security\TwoFactor\Condition\TwoFactorConditionRegistry;
13+
use Scheb\TwoFactorBundle\Security\TwoFactor\Event\TwoFactorAuthenticationEvent;
14+
use Scheb\TwoFactorBundle\Security\TwoFactor\Event\TwoFactorAuthenticationEvents;
15+
use Scheb\TwoFactorBundle\Tests\EventDispatcherTestHelper;
1316
use Scheb\TwoFactorBundle\Tests\TestCase;
17+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
1418

1519
class TwoFactorConditionRegistryTest extends TestCase
1620
{
21+
use EventDispatcherTestHelper;
22+
1723
private MockObject&AuthenticationContextInterface $context;
1824
private MockObject&TwoFactorConditionInterface $condition1;
1925
private MockObject&TwoFactorConditionInterface $condition2;
@@ -26,11 +32,15 @@ protected function setUp(): void
2632
$this->condition1 = $this->createMock(TwoFactorConditionInterface::class);
2733
$this->condition2 = $this->createMock(TwoFactorConditionInterface::class);
2834
$this->condition3 = $this->createMock(TwoFactorConditionInterface::class);
29-
$this->registry = new TwoFactorConditionRegistry(new ArrayIterator([
30-
$this->condition1,
31-
$this->condition2,
32-
$this->condition3,
33-
]));
35+
$this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
36+
$this->registry = new TwoFactorConditionRegistry(
37+
new ArrayIterator([
38+
$this->condition1,
39+
$this->condition2,
40+
$this->condition3,
41+
]),
42+
$this->eventDispatcher,
43+
);
3444
}
3545

3646
private function conditionReturns(MockObject $condition, bool $result): void
@@ -56,6 +66,7 @@ public function shouldPerformTwoFactorAuthentication_allConditionsFulfilled_chec
5666
$this->conditionReturns($this->condition2, true);
5767
$this->conditionReturns($this->condition3, true);
5868

69+
$this->expectNotDispatchEvent();
5970
$returnValue = $this->registry->shouldPerformTwoFactorAuthentication($this->context);
6071
$this->assertTrue($returnValue);
6172
}
@@ -67,6 +78,10 @@ public function shouldPerformTwoFactorAuthentication_conditionFails_skipFollowin
6778
$this->conditionReturns($this->condition2, false);
6879
$this->conditionNotCalled($this->condition3);
6980

81+
$this->expectDispatchOneEvent(
82+
$this->isInstanceOf(TwoFactorAuthenticationEvent::class),
83+
TwoFactorAuthenticationEvents::SKIPPED,
84+
);
7085
$returnValue = $this->registry->shouldPerformTwoFactorAuthentication($this->context);
7186
$this->assertFalse($returnValue);
7287
}

0 commit comments

Comments
 (0)