diff options
Diffstat (limited to 'lib/private/Authentication/TwoFactorAuth/Manager.php')
-rw-r--r-- | lib/private/Authentication/TwoFactorAuth/Manager.php | 98 |
1 files changed, 34 insertions, 64 deletions
diff --git a/lib/private/Authentication/TwoFactorAuth/Manager.php b/lib/private/Authentication/TwoFactorAuth/Manager.php index d62556465df..07aa98610ed 100644 --- a/lib/private/Authentication/TwoFactorAuth/Manager.php +++ b/lib/private/Authentication/TwoFactorAuth/Manager.php @@ -1,41 +1,25 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\TwoFactorAuth; use BadMethodCallException; use Exception; -use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Token\IProvider as TokenProvider; use OCP\Activity\IManager; +use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Authentication\Exceptions\InvalidTokenException; use OCP\Authentication\TwoFactorAuth\IActivatableAtLogin; use OCP\Authentication\TwoFactorAuth\IProvider; use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\Authentication\TwoFactorAuth\TwoFactorProviderChallengeFailed; +use OCP\Authentication\TwoFactorAuth\TwoFactorProviderChallengePassed; use OCP\Authentication\TwoFactorAuth\TwoFactorProviderForUserDisabled; use OCP\Authentication\TwoFactorAuth\TwoFactorProviderForUserEnabled; use OCP\EventDispatcher\IEventDispatcher; @@ -44,8 +28,6 @@ use OCP\ISession; use OCP\IUser; use OCP\Session\Exceptions\SessionNotAvailableException; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\GenericEvent; use function array_diff; use function array_filter; @@ -85,23 +67,19 @@ class Manager { /** @var IEventDispatcher */ private $dispatcher; - /** @var EventDispatcherInterface */ - private $legacyDispatcher; - /** @psalm-var array<string, bool> */ private $userIsTwoFactorAuthenticated = []; public function __construct(ProviderLoader $providerLoader, - IRegistry $providerRegistry, - MandatoryTwoFactor $mandatoryTwoFactor, - ISession $session, - IConfig $config, - IManager $activityManager, - LoggerInterface $logger, - TokenProvider $tokenProvider, - ITimeFactory $timeFactory, - IEventDispatcher $eventDispatcher, - EventDispatcherInterface $legacyDispatcher) { + IRegistry $providerRegistry, + MandatoryTwoFactor $mandatoryTwoFactor, + ISession $session, + IConfig $config, + IManager $activityManager, + LoggerInterface $logger, + TokenProvider $tokenProvider, + ITimeFactory $timeFactory, + IEventDispatcher $eventDispatcher) { $this->providerLoader = $providerLoader; $this->providerRegistry = $providerRegistry; $this->mandatoryTwoFactor = $mandatoryTwoFactor; @@ -112,14 +90,10 @@ class Manager { $this->tokenProvider = $tokenProvider; $this->timeFactory = $timeFactory; $this->dispatcher = $eventDispatcher; - $this->legacyDispatcher = $legacyDispatcher; } /** * Determine whether the user must provide a second factor challenge - * - * @param IUser $user - * @return boolean */ public function isTwoFactorAuthenticated(IUser $user): bool { if (isset($this->userIsTwoFactorAuthenticated[$user->getUID()])) { @@ -143,18 +117,13 @@ class Manager { /** * Get a 2FA provider by its ID - * - * @param IUser $user - * @param string $challengeProviderId - * @return IProvider|null */ - public function getProvider(IUser $user, string $challengeProviderId) { + public function getProvider(IUser $user, string $challengeProviderId): ?IProvider { $providers = $this->getProviderSet($user)->getProviders(); return $providers[$challengeProviderId] ?? null; } /** - * @param IUser $user * @return IActivatableAtLogin[] * @throws Exception */ @@ -224,7 +193,7 @@ class Manager { if (!empty($missing)) { // There was at least one provider missing - $this->logger->alert(count($missing) . " two-factor auth providers failed to load", ['app' => 'core']); + $this->logger->alert(count($missing) . ' two-factor auth providers failed to load', ['app' => 'core']); return true; } @@ -280,21 +249,17 @@ class Manager { $sessionId = $this->session->getId(); $token = $this->tokenProvider->getToken($sessionId); $tokenId = $token->getId(); - $this->config->deleteUserValue($user->getUID(), 'login_token_2fa', $tokenId); - - $dispatchEvent = new GenericEvent($user, ['provider' => $provider->getDisplayName()]); - $this->legacyDispatcher->dispatch(IProvider::EVENT_SUCCESS, $dispatchEvent); + $this->config->deleteUserValue($user->getUID(), 'login_token_2fa', (string)$tokenId); $this->dispatcher->dispatchTyped(new TwoFactorProviderForUserEnabled($user, $provider)); + $this->dispatcher->dispatchTyped(new TwoFactorProviderChallengePassed($user, $provider)); $this->publishEvent($user, 'twofactor_success', [ 'provider' => $provider->getDisplayName(), ]); } else { - $dispatchEvent = new GenericEvent($user, ['provider' => $provider->getDisplayName()]); - $this->legacyDispatcher->dispatch(IProvider::EVENT_FAILED, $dispatchEvent); - $this->dispatcher->dispatchTyped(new TwoFactorProviderForUserDisabled($user, $provider)); + $this->dispatcher->dispatchTyped(new TwoFactorProviderChallengeFailed($user, $provider)); $this->publishEvent($user, 'twofactor_failed', [ 'provider' => $provider->getDisplayName(), @@ -330,21 +295,21 @@ class Manager { * @param IUser $user the currently logged in user * @return boolean */ - public function needsSecondFactor(IUser $user = null): bool { + public function needsSecondFactor(?IUser $user = null): bool { if ($user === null) { return false; } - // If we are authenticated using an app password skip all this - if ($this->session->exists('app_password')) { + // If we are authenticated using an app password or AppAPI Auth, skip all this + if ($this->session->exists('app_password') || $this->session->get('app_api') === true) { return false; } // First check if the session tells us we should do 2FA (99% case) if (!$this->session->exists(self::SESSION_UID_KEY)) { // Check if the session tells us it is 2FA authenticated already - if ($this->session->exists(self::SESSION_UID_DONE) && - $this->session->get(self::SESSION_UID_DONE) === $user->getUID()) { + if ($this->session->exists(self::SESSION_UID_DONE) + && $this->session->get(self::SESSION_UID_DONE) === $user->getUID()) { return false; } @@ -358,7 +323,7 @@ class Manager { $tokenId = $token->getId(); $tokensNeeding2FA = $this->config->getUserKeys($user->getUID(), 'login_token_2fa'); - if (!\in_array((string) $tokenId, $tokensNeeding2FA, true)) { + if (!\in_array((string)$tokenId, $tokensNeeding2FA, true)) { $this->session->set(self::SESSION_UID_DONE, $user->getUID()); return false; } @@ -395,14 +360,19 @@ class Manager { $id = $this->session->getId(); $token = $this->tokenProvider->getToken($id); - $this->config->setUserValue($user->getUID(), 'login_token_2fa', (string) $token->getId(), $this->timeFactory->getTime()); + $this->config->setUserValue($user->getUID(), 'login_token_2fa', (string)$token->getId(), (string)$this->timeFactory->getTime()); } public function clearTwoFactorPending(string $userId) { $tokensNeeding2FA = $this->config->getUserKeys($userId, 'login_token_2fa'); foreach ($tokensNeeding2FA as $tokenId) { - $this->tokenProvider->invalidateTokenById($userId, (int)$tokenId); + $this->config->deleteUserValue($userId, 'login_token_2fa', $tokenId); + + try { + $this->tokenProvider->invalidateTokenById($userId, (int)$tokenId); + } catch (DoesNotExistException $e) { + } } } } |