diff --git a/core/Controller/OCJSController.php b/core/Controller/OCJSController.php index dbb203e827f34..4dc3a0a4b9cc1 100644 --- a/core/Controller/OCJSController.php +++ b/core/Controller/OCJSController.php @@ -29,6 +29,7 @@ namespace OC\Core\Controller; use bantu\IniGetWrapper\IniGetWrapper; +use OC\Authentication\Token\IProvider; use OC\CapabilitiesManager; use OC\Template\JSConfigHelper; use OCP\App\IAppManager; @@ -65,6 +66,7 @@ public function __construct( IURLGenerator $urlGenerator, CapabilitiesManager $capabilitiesManager, IInitialStateService $initialStateService, + IProvider $tokenProvider, ) { parent::__construct($appName, $request); @@ -79,7 +81,8 @@ public function __construct( $iniWrapper, $urlGenerator, $capabilitiesManager, - $initialStateService + $initialStateService, + $tokenProvider ); } diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php index 93365582864e1..2570a02e78b36 100644 --- a/lib/private/AppFramework/DependencyInjection/DIContainer.php +++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php @@ -276,7 +276,8 @@ public function __construct(string $appName, array $urlParams = [], ServerContai $c->get(IControllerMethodReflector::class), $c->get(ISession::class), $c->get(IUserSession::class), - $c->get(ITimeFactory::class) + $c->get(ITimeFactory::class), + $c->get(\OC\Authentication\Token\IProvider::class), ) ); $dispatcher->registerMiddleware( diff --git a/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php b/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php index 351f47ea92459..20b3761fef3a6 100644 --- a/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php @@ -25,6 +25,7 @@ use OC\AppFramework\Middleware\Security\Exceptions\NotConfirmedException; use OC\AppFramework\Utility\ControllerMethodReflector; +use OC\Authentication\Token\IProvider; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; use OCP\AppFramework\Middleware; @@ -45,6 +46,7 @@ class PasswordConfirmationMiddleware extends Middleware { private $timeFactory; /** @var array */ private $excludedUserBackEnds = ['user_saml' => true, 'user_globalsiteselector' => true]; + private IProvider $tokenProvider; /** * PasswordConfirmationMiddleware constructor. @@ -57,11 +59,14 @@ class PasswordConfirmationMiddleware extends Middleware { public function __construct(ControllerMethodReflector $reflector, ISession $session, IUserSession $userSession, - ITimeFactory $timeFactory) { + ITimeFactory $timeFactory, + IProvider $tokenProvider + ) { $this->reflector = $reflector; $this->session = $session; $this->userSession = $userSession; $this->timeFactory = $timeFactory; + $this->tokenProvider = $tokenProvider; } /** @@ -86,8 +91,16 @@ public function beforeController($controller, $methodName) { $backendClassName = $user->getBackendClassName(); } + $sessionId = $this->session->getId(); + $token = $this->tokenProvider->getToken($sessionId); + $scope = $token->getScopeAsArray(); + if (isset($scope['sso-based-login']) && $scope['sso-based-login'] === true) { + // Users logging in from SSO backends cannot confirm their password by design + return; + } + $lastConfirm = (int) $this->session->get('last-password-confirm'); - // we can't check the password against a SAML backend, so skip password confirmation in this case + // TODO: confirm excludedUserBackEnds can go away and remove it if (!isset($this->excludedUserBackEnds[$backendClassName]) && $lastConfirm < ($this->timeFactory->getTime() - (30 * 60 + 15))) { // allow 15 seconds delay throw new NotConfirmedException(); } diff --git a/lib/private/Authentication/Token/PublicKeyTokenProvider.php b/lib/private/Authentication/Token/PublicKeyTokenProvider.php index 6fd2bebc19502..b3f3c19360dec 100644 --- a/lib/private/Authentication/Token/PublicKeyTokenProvider.php +++ b/lib/private/Authentication/Token/PublicKeyTokenProvider.php @@ -265,6 +265,7 @@ public function renewSessionToken(string $oldSessionId, string $sessionId): OCPI OCPIToken::TEMPORARY_TOKEN, $token->getRemember() ); + $newToken->setScope($token->getScopeAsArray()); $this->cacheToken($newToken); $this->cacheInvalidHash($token->getToken()); diff --git a/lib/private/Template/JSConfigHelper.php b/lib/private/Template/JSConfigHelper.php index 8cba93f1f4e69..f0a48769d063c 100644 --- a/lib/private/Template/JSConfigHelper.php +++ b/lib/private/Template/JSConfigHelper.php @@ -34,6 +34,7 @@ namespace OC\Template; use bantu\IniGetWrapper\IniGetWrapper; +use OC\Authentication\Token\IProvider; use OC\CapabilitiesManager; use OC\Share\Share; use OCP\App\AppPathNotFoundException; @@ -53,43 +54,24 @@ use OCP\Util; class JSConfigHelper { - protected IL10N $l; - protected Defaults $defaults; - protected IAppManager $appManager; - protected ISession $session; - protected ?IUser $currentUser; - protected IConfig $config; - protected IGroupManager $groupManager; - protected IniGetWrapper $iniWrapper; - protected IURLGenerator $urlGenerator; - protected CapabilitiesManager $capabilitiesManager; - protected IInitialStateService $initialStateService; /** @var array user back-ends excluded from password verification */ private $excludedUserBackEnds = ['user_saml' => true, 'user_globalsiteselector' => true]; - public function __construct(IL10N $l, - Defaults $defaults, - IAppManager $appManager, - ISession $session, - ?IUser $currentUser, - IConfig $config, - IGroupManager $groupManager, - IniGetWrapper $iniWrapper, - IURLGenerator $urlGenerator, - CapabilitiesManager $capabilitiesManager, - IInitialStateService $initialStateService) { - $this->l = $l; - $this->defaults = $defaults; - $this->appManager = $appManager; - $this->session = $session; - $this->currentUser = $currentUser; - $this->config = $config; - $this->groupManager = $groupManager; - $this->iniWrapper = $iniWrapper; - $this->urlGenerator = $urlGenerator; - $this->capabilitiesManager = $capabilitiesManager; - $this->initialStateService = $initialStateService; + public function __construct( + protected IL10N $l, + protected Defaults $defaults, + protected IAppManager $appManager, + protected ISession $session, + protected ?IUser $currentUser, + protected IConfig $config, + protected IGroupManager $groupManager, + protected IniGetWrapper $iniWrapper, + protected IURLGenerator $urlGenerator, + protected CapabilitiesManager $capabilitiesManager, + protected IInitialStateService $initialStateService, + protected IProvider $tokenProvider, + ) { } public function getConfig(): string { @@ -155,9 +137,13 @@ public function getConfig(): string { } if ($this->currentUser instanceof IUser) { - $lastConfirmTimestamp = $this->session->get('last-password-confirm'); - if (!is_int($lastConfirmTimestamp)) { - $lastConfirmTimestamp = 0; + if ($this->isUserLoggedInViaSSO()) { + $lastConfirmTimestamp = PHP_INT_MAX; + } else { + $lastConfirmTimestamp = $this->session->get('last-password-confirm'); + if (!is_int($lastConfirmTimestamp)) { + $lastConfirmTimestamp = 0; + } } } else { $lastConfirmTimestamp = 0; @@ -311,4 +297,10 @@ public function getConfig(): string { return $result; } + + protected function isUserLoggedInViaSSO(): bool { + $token = $this->tokenProvider->getToken($this->session->getId()); + $scope = $token->getScopeAsArray(); + return isset($scope['sso-based-login']) && $scope['sso-based-login'] === true; + } } diff --git a/lib/private/TemplateLayout.php b/lib/private/TemplateLayout.php index b4490dfe101d0..f2b6c032d1d62 100644 --- a/lib/private/TemplateLayout.php +++ b/lib/private/TemplateLayout.php @@ -43,6 +43,7 @@ namespace OC; use bantu\IniGetWrapper\IniGetWrapper; +use OC\Authentication\Token\IProvider; use OC\Search\SearchQuery; use OC\Template\CSSResourceLocator; use OC\Template\JSConfigHelper; @@ -259,7 +260,8 @@ public function __construct($renderAs, $appId = '') { \OC::$server->get(IniGetWrapper::class), \OC::$server->getURLGenerator(), \OC::$server->getCapabilitiesManager(), - \OCP\Server::get(IInitialStateService::class) + \OCP\Server::get(IInitialStateService::class), + \OCP\Server::get(IProvider::class), ); $config = $jsConfigHelper->getConfig(); if (\OC::$server->getContentSecurityPolicyNonceManager()->browserSupportsCspV3()) { diff --git a/lib/private/legacy/OC_User.php b/lib/private/legacy/OC_User.php index 47890fd8ddaba..e3c678728a1a8 100644 --- a/lib/private/legacy/OC_User.php +++ b/lib/private/legacy/OC_User.php @@ -36,6 +36,7 @@ * */ +use OC\Authentication\Token\IProvider; use OC\User\LoginException; use OCP\EventDispatcher\IEventDispatcher; use OCP\IGroupManager; @@ -196,6 +197,14 @@ public static function loginWithApache(\OCP\Authentication\IApacheBackend $backe $userSession->createSessionToken($request, $uid, $uid, $password); $userSession->createRememberMeToken($userSession->getUser()); + + if (empty($password)) { + $tokenProvider = \OC::$server->get(IProvider::class); + $token = $tokenProvider->getToken($userSession->getSession()->getId()); + $token->setScope(['sso-based-login' => true]); + $tokenProvider->updateToken($token); + } + // setup the filesystem OC_Util::setupFS($uid); // first call the post_login hooks, the login-process needs to be