diff options
Diffstat (limited to 'core/Controller')
-rw-r--r-- | core/Controller/LostController.php | 145 | ||||
-rw-r--r-- | core/Controller/SvgController.php | 5 | ||||
-rw-r--r-- | core/Controller/TwoFactorChallengeController.php | 11 | ||||
-rw-r--r-- | core/Controller/UnifiedSearchController.php | 18 |
4 files changed, 77 insertions, 102 deletions
diff --git a/core/Controller/LostController.php b/core/Controller/LostController.php index bed96eeec83..87a629b9ee8 100644 --- a/core/Controller/LostController.php +++ b/core/Controller/LostController.php @@ -40,7 +40,6 @@ use OC\Core\Exception\ResetPasswordException; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\TemplateResponse; -use OCP\AppFramework\Utility\ITimeFactory; use OCP\Defaults; use OCP\Encryption\IEncryptionModule; use OCP\Encryption\IManager; @@ -54,8 +53,8 @@ use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; use OCP\Mail\IMailer; -use OCP\Security\ICrypto; -use OCP\Security\ISecureRandom; +use OCP\Security\VerificationToken\InvalidTokenException; +use OCP\Security\VerificationToken\IVerificationToken; use function array_filter; use function count; use function reset; @@ -82,67 +81,46 @@ class LostController extends Controller { protected $encryptionManager; /** @var IConfig */ protected $config; - /** @var ISecureRandom */ - protected $secureRandom; /** @var IMailer */ protected $mailer; - /** @var ITimeFactory */ - protected $timeFactory; - /** @var ICrypto */ - protected $crypto; /** @var ILogger */ private $logger; /** @var Manager */ private $twoFactorManager; /** @var IInitialStateService */ private $initialStateService; - - /** - * @param string $appName - * @param IRequest $request - * @param IURLGenerator $urlGenerator - * @param IUserManager $userManager - * @param Defaults $defaults - * @param IL10N $l10n - * @param IConfig $config - * @param ISecureRandom $secureRandom - * @param string $defaultMailAddress - * @param IManager $encryptionManager - * @param IMailer $mailer - * @param ITimeFactory $timeFactory - * @param ICrypto $crypto - */ - public function __construct($appName, - IRequest $request, - IURLGenerator $urlGenerator, - IUserManager $userManager, - Defaults $defaults, - IL10N $l10n, - IConfig $config, - ISecureRandom $secureRandom, - $defaultMailAddress, - IManager $encryptionManager, - IMailer $mailer, - ITimeFactory $timeFactory, - ICrypto $crypto, - ILogger $logger, - Manager $twoFactorManager, - IInitialStateService $initialStateService) { + /** @var IVerificationToken */ + private $verificationToken; + + public function __construct( + $appName, + IRequest $request, + IURLGenerator $urlGenerator, + IUserManager $userManager, + Defaults $defaults, + IL10N $l10n, + IConfig $config, + $defaultMailAddress, + IManager $encryptionManager, + IMailer $mailer, + ILogger $logger, + Manager $twoFactorManager, + IInitialStateService $initialStateService, + IVerificationToken $verificationToken + ) { parent::__construct($appName, $request); $this->urlGenerator = $urlGenerator; $this->userManager = $userManager; $this->defaults = $defaults; $this->l10n = $l10n; - $this->secureRandom = $secureRandom; $this->from = $defaultMailAddress; $this->encryptionManager = $encryptionManager; $this->config = $config; $this->mailer = $mailer; - $this->timeFactory = $timeFactory; - $this->crypto = $crypto; $this->logger = $logger; $this->twoFactorManager = $twoFactorManager; $this->initialStateService = $initialStateService; + $this->verificationToken = $verificationToken; } /** @@ -156,22 +134,24 @@ class LostController extends Controller { * @return TemplateResponse */ public function resetform($token, $userId) { - if ($this->config->getSystemValue('lost_password_link', '') !== '') { - return new TemplateResponse('core', 'error', [ - 'errors' => [['error' => $this->l10n->t('Password reset is disabled')]] - ], - 'guest' - ); - } - try { $this->checkPasswordResetToken($token, $userId); } catch (\Exception $e) { - return new TemplateResponse( - 'core', 'error', [ - "errors" => [["error" => $e->getMessage()]] - ], - 'guest' + if ($this->config->getSystemValue('lost_password_link', '') !== 'disabled' + || ($e instanceof InvalidTokenException + && !in_array($e->getCode(), [InvalidTokenException::TOKEN_NOT_FOUND, InvalidTokenException::USER_UNKNOWN])) + ) { + return new TemplateResponse( + 'core', 'error', [ + "errors" => [["error" => $e->getMessage()]] + ], + TemplateResponse::RENDER_AS_GUEST + ); + } + return new TemplateResponse('core', 'error', [ + 'errors' => [['error' => $this->l10n->t('Password reset is disabled')]] + ], + TemplateResponse::RENDER_AS_GUEST ); } $this->initialStateService->provideInitialState('core', 'resetPasswordUser', $userId); @@ -192,36 +172,15 @@ class LostController extends Controller { * @param string $userId * @throws \Exception */ - protected function checkPasswordResetToken($token, $userId) { - $user = $this->userManager->get($userId); - if ($user === null || !$user->isEnabled()) { - throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid')); - } - - $encryptedToken = $this->config->getUserValue($userId, 'core', 'lostpassword', null); - if ($encryptedToken === null) { - throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid')); - } - + protected function checkPasswordResetToken(string $token, string $userId): void { try { - $mailAddress = !is_null($user->getEMailAddress()) ? $user->getEMailAddress() : ''; - $decryptedToken = $this->crypto->decrypt($encryptedToken, $mailAddress.$this->config->getSystemValue('secret')); - } catch (\Exception $e) { - throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid')); - } - - $splittedToken = explode(':', $decryptedToken); - if (count($splittedToken) !== 2) { - throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid')); - } - - if ($splittedToken[0] < ($this->timeFactory->getTime() - 60 * 60 * 24 * 7) || - $user->getLastLogin() > $splittedToken[0]) { - throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is expired')); - } - - if (!hash_equals($splittedToken[1], $token)) { - throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid')); + $user = $this->userManager->get($userId); + $this->verificationToken->check($token, $user, 'lostpassword', $user ? $user->getEMailAddress() : '', true); + } catch (InvalidTokenException $e) { + $error = $e->getCode() === InvalidTokenException::TOKEN_EXPIRED + ? $this->l10n->t('Could not reset password because the token is expired') + : $this->l10n->t('Could not reset password because the token is invalid'); + throw new \Exception($error, (int)$e->getCode(), $e); } } @@ -285,10 +244,6 @@ class LostController extends Controller { * @return array */ public function setPassword($token, $userId, $password, $proceed) { - if ($this->config->getSystemValue('lost_password_link', '') !== '') { - return $this->error($this->l10n->t('Password reset is disabled')); - } - if ($this->encryptionManager->isEnabled() && !$proceed) { $encryptionModules = $this->encryptionManager->getEncryptionModules(); foreach ($encryptionModules as $module) { @@ -343,15 +298,7 @@ class LostController extends Controller { // secret being the users' email address appended with the system secret. // This makes the token automatically invalidate once the user changes // their email address. - $token = $this->secureRandom->generate( - 21, - ISecureRandom::CHAR_DIGITS. - ISecureRandom::CHAR_LOWER. - ISecureRandom::CHAR_UPPER - ); - $tokenValue = $this->timeFactory->getTime() .':'. $token; - $encryptedValue = $this->crypto->encrypt($tokenValue, $email . $this->config->getSystemValue('secret')); - $this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue); + $token = $this->verificationToken->create($user, 'lostpassword', $email); $link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', ['userId' => $user->getUID(), 'token' => $token]); diff --git a/core/Controller/SvgController.php b/core/Controller/SvgController.php index ea73ba118d9..17f16dd48e6 100644 --- a/core/Controller/SvgController.php +++ b/core/Controller/SvgController.php @@ -31,6 +31,7 @@ declare(strict_types=1); */ namespace OC\Core\Controller; +use OC\Files\Filesystem; use OC\Template\IconsCacher; use OCP\App\AppPathNotFoundException; use OCP\App\IAppManager; @@ -117,6 +118,10 @@ class SvgController extends Controller { * @return DataDisplayResponse|NotFoundResponse */ private function getSvg(string $path, string $color, string $fileName) { + if (!Filesystem::isValidPath($path)) { + return new NotFoundResponse(); + } + if (!file_exists($path)) { return new NotFoundResponse(); } diff --git a/core/Controller/TwoFactorChallengeController.php b/core/Controller/TwoFactorChallengeController.php index f23df5aea28..8f82346c050 100644 --- a/core/Controller/TwoFactorChallengeController.php +++ b/core/Controller/TwoFactorChallengeController.php @@ -34,6 +34,7 @@ use OCP\Authentication\TwoFactorAuth\IActivatableAtLogin; use OCP\Authentication\TwoFactorAuth\IProvider; use OCP\Authentication\TwoFactorAuth\IProvidesCustomCSP; use OCP\Authentication\TwoFactorAuth\TwoFactorException; +use OCP\ILogger; use OCP\IRequest; use OCP\ISession; use OCP\IURLGenerator; @@ -50,6 +51,9 @@ class TwoFactorChallengeController extends Controller { /** @var ISession */ private $session; + /** @var ILogger */ + private $logger; + /** @var IURLGenerator */ private $urlGenerator; @@ -60,14 +64,16 @@ class TwoFactorChallengeController extends Controller { * @param IUserSession $userSession * @param ISession $session * @param IURLGenerator $urlGenerator + * @param ILogger $logger */ public function __construct($appName, IRequest $request, Manager $twoFactorManager, IUserSession $userSession, - ISession $session, IURLGenerator $urlGenerator) { + ISession $session, IURLGenerator $urlGenerator, ILogger $logger) { parent::__construct($appName, $request); $this->twoFactorManager = $twoFactorManager; $this->userSession = $userSession; $this->session = $session; $this->urlGenerator = $urlGenerator; + $this->logger = $logger; } /** @@ -207,6 +213,9 @@ class TwoFactorChallengeController extends Controller { $this->session->set('two_factor_auth_error_message', $e->getMessage()); } + $ip = $this->request->getRemoteAddress(); + $uid = $user->getUID(); + $this->logger->warning("Two-factor challenge failed: $uid (Remote IP: $ip)"); $this->session->set('two_factor_auth_error', true); return new RedirectResponse($this->urlGenerator->linkToRoute('core.TwoFactorChallenge.showChallenge', [ 'challengeProviderId' => $provider->getId(), diff --git a/core/Controller/UnifiedSearchController.php b/core/Controller/UnifiedSearchController.php index 93fbb323ee5..bfed6d606ae 100644 --- a/core/Controller/UnifiedSearchController.php +++ b/core/Controller/UnifiedSearchController.php @@ -33,6 +33,7 @@ use OCP\AppFramework\OCSController; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\IRequest; +use OCP\IURLGenerator; use OCP\IUserSession; use OCP\Route\IRouter; use OCP\Search\ISearchQuery; @@ -49,15 +50,20 @@ class UnifiedSearchController extends OCSController { /** @var IRouter */ private $router; + /** @var IURLGenerator */ + private $urlGenerator; + public function __construct(IRequest $request, IUserSession $userSession, SearchComposer $composer, - IRouter $router) { + IRouter $router, + IURLGenerator $urlGenerator) { parent::__construct('core', $request); $this->composer = $composer; $this->userSession = $userSession; $this->router = $router; + $this->urlGenerator = $urlGenerator; } /** @@ -123,9 +129,17 @@ class UnifiedSearchController extends OCSController { if ($url !== '') { $urlParts = parse_url($url); + $urlPath = $urlParts['path']; + + // Optionally strip webroot from URL. Required for route matching on setups + // with Nextcloud in a webserver subfolder (webroot). + $webroot = $this->urlGenerator->getWebroot(); + if ($webroot !== '' && substr($urlPath, 0, strlen($webroot)) === $webroot) { + $urlPath = substr($urlPath, strlen($webroot)); + } try { - $parameters = $this->router->findMatchingRoute($urlParts['path']); + $parameters = $this->router->findMatchingRoute($urlPath); // contacts.PageController.index => contacts.Page.index $route = $parameters['caller']; |