Skip to content

Commit c42bc0c

Browse files
michal.roszak@put.poznan.plmiaulalala
authored andcommitted
feat (2fa): Add IStatelessProvider interface
Signed-off-by: michal.roszak@put.poznan.pl <michal.roszak@put.poznan.pl>
1 parent f2ea807 commit c42bc0c

7 files changed

Lines changed: 66 additions & 2 deletions

File tree

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,7 @@
629629
- zorn-v <zorn7@yandex.ru>
630630
- zulan <git@zulan.net>
631631
- Łukasz Buśko <busko.lukasz@pm.me>
632+
- Michał Roszak <m.roszakos@gmail.com>
632633
- Nextcloud GmbH
633634
- ownCloud GmbH
634635
- ownCloud, Inc.

lib/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@
183183
'OCP\\Authentication\\TwoFactorAuth\\IProvidesIcons' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/IProvidesIcons.php',
184184
'OCP\\Authentication\\TwoFactorAuth\\IProvidesPersonalSettings' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/IProvidesPersonalSettings.php',
185185
'OCP\\Authentication\\TwoFactorAuth\\IRegistry' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/IRegistry.php',
186+
'OCP\\Authentication\\TwoFactorAuth\\IStatelessProvider' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/IStatelessProvider.php',
186187
'OCP\\Authentication\\TwoFactorAuth\\RegistryEvent' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/RegistryEvent.php',
187188
'OCP\\Authentication\\TwoFactorAuth\\TwoFactorException' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/TwoFactorException.php',
188189
'OCP\\Authentication\\TwoFactorAuth\\TwoFactorProviderChallengeFailed' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/TwoFactorProviderChallengeFailed.php',

lib/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
224224
'OCP\\Authentication\\TwoFactorAuth\\IProvidesIcons' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/IProvidesIcons.php',
225225
'OCP\\Authentication\\TwoFactorAuth\\IProvidesPersonalSettings' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/IProvidesPersonalSettings.php',
226226
'OCP\\Authentication\\TwoFactorAuth\\IRegistry' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/IRegistry.php',
227+
'OCP\\Authentication\\TwoFactorAuth\\IStatelessProvider' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/IStatelessProvider.php',
227228
'OCP\\Authentication\\TwoFactorAuth\\RegistryEvent' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/RegistryEvent.php',
228229
'OCP\\Authentication\\TwoFactorAuth\\TwoFactorException' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/TwoFactorException.php',
229230
'OCP\\Authentication\\TwoFactorAuth\\TwoFactorProviderChallengeFailed' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/TwoFactorProviderChallengeFailed.php',

lib/private/Authentication/TwoFactorAuth/ProviderManager.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use OCP\Authentication\TwoFactorAuth\IDeactivatableByAdmin;
1414
use OCP\Authentication\TwoFactorAuth\IProvider;
1515
use OCP\Authentication\TwoFactorAuth\IRegistry;
16+
use OCP\Authentication\TwoFactorAuth\IStatelessProvider;
1617
use OCP\IUser;
1718

1819
class ProviderManager {
@@ -42,7 +43,9 @@ private function getProvider(string $providerId, IUser $user): IProvider {
4243
public function tryEnableProviderFor(string $providerId, IUser $user): bool {
4344
$provider = $this->getProvider($providerId, $user);
4445

45-
if ($provider instanceof IActivatableByAdmin) {
46+
if ($provider instanceof IActivatableByAdmin
47+
&& !($provider instanceof IStatelessProvider)
48+
) {
4649
$provider->enableFor($user);
4750
$this->providerRegistry->enableProviderFor($provider, $user);
4851
return true;
@@ -61,7 +64,9 @@ public function tryEnableProviderFor(string $providerId, IUser $user): bool {
6164
public function tryDisableProviderFor(string $providerId, IUser $user): bool {
6265
$provider = $this->getProvider($providerId, $user);
6366

64-
if ($provider instanceof IDeactivatableByAdmin) {
67+
if ($provider instanceof IDeactivatableByAdmin
68+
&& !($provider instanceof IStatelessProvider)
69+
) {
6570
$provider->disableFor($user);
6671
$this->providerRegistry->disableProviderFor($provider, $user);
6772
return true;

lib/private/Authentication/TwoFactorAuth/Registry.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use OC\Authentication\TwoFactorAuth\Db\ProviderUserAssignmentDao;
1212
use OCP\Authentication\TwoFactorAuth\IProvider;
1313
use OCP\Authentication\TwoFactorAuth\IRegistry;
14+
use OCP\Authentication\TwoFactorAuth\IStatelessProvider;
1415
use OCP\Authentication\TwoFactorAuth\RegistryEvent;
1516
use OCP\Authentication\TwoFactorAuth\TwoFactorProviderDisabled;
1617
use OCP\Authentication\TwoFactorAuth\TwoFactorProviderForUserRegistered;
@@ -33,6 +34,10 @@ public function getProviderStates(IUser $user): array {
3334

3435
#[\Override]
3536
public function enableProviderFor(IProvider $provider, IUser $user) {
37+
if ($provider instanceof IStatelessProvider) {
38+
return;
39+
}
40+
3641
$this->assignmentDao->persist($provider->getId(), $user->getUID(), 1);
3742

3843
$event = new RegistryEvent($provider, $user);
@@ -42,6 +47,10 @@ public function enableProviderFor(IProvider $provider, IUser $user) {
4247

4348
#[\Override]
4449
public function disableProviderFor(IProvider $provider, IUser $user) {
50+
if ($provider instanceof IStatelessProvider) {
51+
return;
52+
}
53+
4554
$this->assignmentDao->persist($provider->getId(), $user->getUID(), 0);
4655

4756
$event = new RegistryEvent($provider, $user);
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCP\Authentication\TwoFactorAuth;
11+
12+
use OCP\AppFramework\Attribute\Implementable;
13+
14+
/**
15+
* Marks the 2FA provider stateless. That means the state of 2FA activation
16+
* for user will be checked dynamically and not stored in the database.
17+
*
18+
* @since 34.0.0
19+
*/
20+
#[Implementable(since: '34.0.0')]
21+
interface IStatelessProvider extends IProvider {
22+
}

tests/lib/Authentication/TwoFactorAuth/RegistryTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use OC\Authentication\TwoFactorAuth\Registry;
1414
use OCP\Authentication\TwoFactorAuth\IProvider;
1515
use OCP\Authentication\TwoFactorAuth\IRegistry;
16+
use OCP\Authentication\TwoFactorAuth\IStatelessProvider;
1617
use OCP\Authentication\TwoFactorAuth\RegistryEvent;
1718
use OCP\Authentication\TwoFactorAuth\TwoFactorProviderDisabled;
1819
use OCP\Authentication\TwoFactorAuth\TwoFactorProviderForUserRegistered;
@@ -77,6 +78,18 @@ public function testEnableProvider(): void {
7778
$this->registry->enableProviderFor($provider, $user);
7879
}
7980

81+
public function testEnableStatelessProvider(): void {
82+
$user = $this->createMock(IUser::class);
83+
$provider = $this->createMock(IStatelessProvider::class);
84+
85+
$this->dao->expects($this->never())->method('persist');
86+
87+
$this->dispatcher->expects($this->never())->method('dispatch');
88+
$this->dispatcher->expects($this->never())->method('dispatchTyped');
89+
90+
$this->registry->enableProviderFor($provider, $user);
91+
}
92+
8093
public function testDisableProvider(): void {
8194
$user = $this->createMock(IUser::class);
8295
$provider = $this->createMock(IProvider::class);
@@ -104,6 +117,18 @@ public function testDisableProvider(): void {
104117
$this->registry->disableProviderFor($provider, $user);
105118
}
106119

120+
public function testDisableStatelessProvider(): void {
121+
$user = $this->createMock(IUser::class);
122+
$provider = $this->createMock(IStatelessProvider::class);
123+
124+
$this->dao->expects($this->never())->method('persist');
125+
126+
$this->dispatcher->expects($this->never())->method('dispatch');
127+
$this->dispatcher->expects($this->never())->method('dispatchTyped');
128+
129+
$this->registry->disableProviderFor($provider, $user);
130+
}
131+
107132
public function testDeleteUserData(): void {
108133
$user = $this->createMock(IUser::class);
109134
$user->expects($this->once())->method('getUID')->willReturn('user123');

0 commit comments

Comments
 (0)