From c42bc0cf095168f32cdb4b9d84bc70d5f00a5a78 Mon Sep 17 00:00:00 2001 From: "michal.roszak@put.poznan.pl" Date: Tue, 5 May 2026 10:39:25 +0200 Subject: [PATCH 1/2] feat (2fa): Add IStatelessProvider interface Signed-off-by: michal.roszak@put.poznan.pl --- AUTHORS | 1 + lib/composer/composer/autoload_classmap.php | 1 + lib/composer/composer/autoload_static.php | 1 + .../TwoFactorAuth/ProviderManager.php | 9 +++++-- .../Authentication/TwoFactorAuth/Registry.php | 9 +++++++ .../TwoFactorAuth/IStatelessProvider.php | 22 ++++++++++++++++ .../TwoFactorAuth/RegistryTest.php | 25 +++++++++++++++++++ 7 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 lib/public/Authentication/TwoFactorAuth/IStatelessProvider.php diff --git a/AUTHORS b/AUTHORS index fe478401fdd..6af579f94a9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -629,6 +629,7 @@ - zorn-v - zulan - Łukasz Buśko + - Michał Roszak - Nextcloud GmbH - ownCloud GmbH - ownCloud, Inc. diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 28373a5af85..4ce0e141f14 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -183,6 +183,7 @@ return array( 'OCP\\Authentication\\TwoFactorAuth\\IProvidesIcons' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/IProvidesIcons.php', 'OCP\\Authentication\\TwoFactorAuth\\IProvidesPersonalSettings' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/IProvidesPersonalSettings.php', 'OCP\\Authentication\\TwoFactorAuth\\IRegistry' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/IRegistry.php', + 'OCP\\Authentication\\TwoFactorAuth\\IStatelessProvider' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/IStatelessProvider.php', 'OCP\\Authentication\\TwoFactorAuth\\RegistryEvent' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/RegistryEvent.php', 'OCP\\Authentication\\TwoFactorAuth\\TwoFactorException' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/TwoFactorException.php', 'OCP\\Authentication\\TwoFactorAuth\\TwoFactorProviderChallengeFailed' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/TwoFactorProviderChallengeFailed.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index e28dc907636..f5d97b9dbdc 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -224,6 +224,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Authentication\\TwoFactorAuth\\IProvidesIcons' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/IProvidesIcons.php', 'OCP\\Authentication\\TwoFactorAuth\\IProvidesPersonalSettings' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/IProvidesPersonalSettings.php', 'OCP\\Authentication\\TwoFactorAuth\\IRegistry' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/IRegistry.php', + 'OCP\\Authentication\\TwoFactorAuth\\IStatelessProvider' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/IStatelessProvider.php', 'OCP\\Authentication\\TwoFactorAuth\\RegistryEvent' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/RegistryEvent.php', 'OCP\\Authentication\\TwoFactorAuth\\TwoFactorException' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/TwoFactorException.php', 'OCP\\Authentication\\TwoFactorAuth\\TwoFactorProviderChallengeFailed' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/TwoFactorProviderChallengeFailed.php', diff --git a/lib/private/Authentication/TwoFactorAuth/ProviderManager.php b/lib/private/Authentication/TwoFactorAuth/ProviderManager.php index f82f666ac9d..6ed9c715666 100644 --- a/lib/private/Authentication/TwoFactorAuth/ProviderManager.php +++ b/lib/private/Authentication/TwoFactorAuth/ProviderManager.php @@ -13,6 +13,7 @@ use OCP\Authentication\TwoFactorAuth\IActivatableByAdmin; use OCP\Authentication\TwoFactorAuth\IDeactivatableByAdmin; use OCP\Authentication\TwoFactorAuth\IProvider; use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\Authentication\TwoFactorAuth\IStatelessProvider; use OCP\IUser; class ProviderManager { @@ -42,7 +43,9 @@ class ProviderManager { public function tryEnableProviderFor(string $providerId, IUser $user): bool { $provider = $this->getProvider($providerId, $user); - if ($provider instanceof IActivatableByAdmin) { + if ($provider instanceof IActivatableByAdmin + && !($provider instanceof IStatelessProvider) + ) { $provider->enableFor($user); $this->providerRegistry->enableProviderFor($provider, $user); return true; @@ -61,7 +64,9 @@ class ProviderManager { public function tryDisableProviderFor(string $providerId, IUser $user): bool { $provider = $this->getProvider($providerId, $user); - if ($provider instanceof IDeactivatableByAdmin) { + if ($provider instanceof IDeactivatableByAdmin + && !($provider instanceof IStatelessProvider) + ) { $provider->disableFor($user); $this->providerRegistry->disableProviderFor($provider, $user); return true; diff --git a/lib/private/Authentication/TwoFactorAuth/Registry.php b/lib/private/Authentication/TwoFactorAuth/Registry.php index 1224a6caaf0..7306479ea87 100644 --- a/lib/private/Authentication/TwoFactorAuth/Registry.php +++ b/lib/private/Authentication/TwoFactorAuth/Registry.php @@ -11,6 +11,7 @@ namespace OC\Authentication\TwoFactorAuth; use OC\Authentication\TwoFactorAuth\Db\ProviderUserAssignmentDao; use OCP\Authentication\TwoFactorAuth\IProvider; use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\Authentication\TwoFactorAuth\IStatelessProvider; use OCP\Authentication\TwoFactorAuth\RegistryEvent; use OCP\Authentication\TwoFactorAuth\TwoFactorProviderDisabled; use OCP\Authentication\TwoFactorAuth\TwoFactorProviderForUserRegistered; @@ -33,6 +34,10 @@ class Registry implements IRegistry { #[\Override] public function enableProviderFor(IProvider $provider, IUser $user) { + if ($provider instanceof IStatelessProvider) { + return; + } + $this->assignmentDao->persist($provider->getId(), $user->getUID(), 1); $event = new RegistryEvent($provider, $user); @@ -42,6 +47,10 @@ class Registry implements IRegistry { #[\Override] public function disableProviderFor(IProvider $provider, IUser $user) { + if ($provider instanceof IStatelessProvider) { + return; + } + $this->assignmentDao->persist($provider->getId(), $user->getUID(), 0); $event = new RegistryEvent($provider, $user); diff --git a/lib/public/Authentication/TwoFactorAuth/IStatelessProvider.php b/lib/public/Authentication/TwoFactorAuth/IStatelessProvider.php new file mode 100644 index 00000000000..79109dace19 --- /dev/null +++ b/lib/public/Authentication/TwoFactorAuth/IStatelessProvider.php @@ -0,0 +1,22 @@ +registry->enableProviderFor($provider, $user); } + public function testEnableStatelessProvider(): void { + $user = $this->createMock(IUser::class); + $provider = $this->createMock(IStatelessProvider::class); + + $this->dao->expects($this->never())->method('persist'); + + $this->dispatcher->expects($this->never())->method('dispatch'); + $this->dispatcher->expects($this->never())->method('dispatchTyped'); + + $this->registry->enableProviderFor($provider, $user); + } + public function testDisableProvider(): void { $user = $this->createMock(IUser::class); $provider = $this->createMock(IProvider::class); @@ -104,6 +117,18 @@ class RegistryTest extends TestCase { $this->registry->disableProviderFor($provider, $user); } + public function testDisableStatelessProvider(): void { + $user = $this->createMock(IUser::class); + $provider = $this->createMock(IStatelessProvider::class); + + $this->dao->expects($this->never())->method('persist'); + + $this->dispatcher->expects($this->never())->method('dispatch'); + $this->dispatcher->expects($this->never())->method('dispatchTyped'); + + $this->registry->disableProviderFor($provider, $user); + } + public function testDeleteUserData(): void { $user = $this->createMock(IUser::class); $user->expects($this->once())->method('getUID')->willReturn('user123'); From 87e907592a43ba1570a7ce3b1faea3ddfc139dc9 Mon Sep 17 00:00:00 2001 From: Salvatore Martire <4652631+salmart-dev@users.noreply.github.com> Date: Thu, 11 Jun 2026 10:53:49 +0200 Subject: [PATCH 2/2] chore: update since from 34.0.0 to 35.0.0 Signed-off-by: Salvatore Martire <4652631+salmart-dev@users.noreply.github.com> --- .../Authentication/TwoFactorAuth/IStatelessProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/public/Authentication/TwoFactorAuth/IStatelessProvider.php b/lib/public/Authentication/TwoFactorAuth/IStatelessProvider.php index 79109dace19..0af28021331 100644 --- a/lib/public/Authentication/TwoFactorAuth/IStatelessProvider.php +++ b/lib/public/Authentication/TwoFactorAuth/IStatelessProvider.php @@ -15,8 +15,8 @@ use OCP\AppFramework\Attribute\Implementable; * Marks the 2FA provider stateless. That means the state of 2FA activation * for user will be checked dynamically and not stored in the database. * - * @since 34.0.0 + * @since 35.0.0 */ -#[Implementable(since: '34.0.0')] +#[Implementable(since: '35.0.0')] interface IStatelessProvider extends IProvider { }