Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dcca8459 authored by Akhil's avatar Akhil 🙂
Browse files

Merge branch 'dev/recovery-email-token-fix' into 'main'

(fix) extend verification token service to remove disabled check

See merge request !135
parents 6b6fe41b a68aa101
Loading
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@
	<name>Email Recovery</name>
	<summary>Email Recovery App</summary>
	<description><![CDATA[Email Recovery App]]></description>
	<version>12.3.4</version>
	<version>12.3.5</version>
	<licence>agpl</licence>
	<author mail="dev@murena.com" homepage="https://murena.com/">MURENA SAS</author>
	<namespace>EmailRecovery</namespace>
+6 −0
Original line number Diff line number Diff line
@@ -103,6 +103,12 @@ class EmailRecoveryController extends Controller {

			$this->recoveryEmailService->deleteVerificationToken($token, $user, $verificationKey);
			$this->recoveryEmailService->deleteRecoveryEmailReminderStartDate($userId);
			
			// If the user has been disabled due to recovery email setting we need to enable
			if (!empty($this->recoveryEmailService->getUnverifiedUserDisableAt($userId))) {
				$user->setEnabled(true);
			}

			$this->recoveryEmailService->deleteUnverifiedUserDisableAt($userId);
			$responseParams = [
				'title' => $this->l->t('You verified recovery email successfully.'),
+6 −7
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@ use OCP\IUserManager;
use OCP\L10N\IFactory;
use OCP\Mail\IEMailTemplate;
use OCP\Mail\IMailer;
use OCP\Security\VerificationToken\IVerificationToken;
use OCP\Util;

class RecoveryEmailService {
@@ -38,7 +37,7 @@ class RecoveryEmailService {
	private IFactory $l10nFactory;
	private IURLGenerator $urlGenerator;
	private Defaults $themingDefaults;
	private IVerificationToken $verificationToken;
	private VerificationTokenService $verificationTokenService;
	private ICacheFactory $cacheFactory;
	private CurlService $curl;
	private IClientService $httpClientService;
@@ -66,7 +65,7 @@ class RecoveryEmailService {
	private ISession $session;
	private ShopAccountService $shopAccountService;

	public function __construct(string $appName, LoggerInterface $logger, IConfig $config, ISession $session, IUserManager $userManager, IMailer $mailer, IFactory $l10nFactory, IURLGenerator $urlGenerator, Defaults $themingDefaults, IVerificationToken $verificationToken, CurlService $curlService, DomainService $domainService, IL10N $l, ICacheFactory $cacheFactory, IClientService $httpClientService, ConfigMapper $configMapper, ShopAccountService $shopAccountService) {
	public function __construct(string $appName, LoggerInterface $logger, IConfig $config, ISession $session, IUserManager $userManager, IMailer $mailer, IFactory $l10nFactory, IURLGenerator $urlGenerator, Defaults $themingDefaults, VerificationTokenService $verificationTokenService, CurlService $curlService, DomainService $domainService, IL10N $l, ICacheFactory $cacheFactory, IClientService $httpClientService, ConfigMapper $configMapper, ShopAccountService $shopAccountService) {
		$this->logger = $logger;
		$this->config = $config;
		$this->appName = $appName;
@@ -76,7 +75,7 @@ class RecoveryEmailService {
		$this->l10nFactory = $l10nFactory;
		$this->urlGenerator = $urlGenerator;
		$this->themingDefaults = $themingDefaults;
		$this->verificationToken = $verificationToken;
		$this->verificationTokenService = $verificationTokenService;
		$this->curl = $curlService;
		$this->domainService = $domainService;
		$this->httpClientService = $httpClientService;
@@ -557,13 +556,13 @@ class RecoveryEmailService {
	}
	private function createToken(IUser $user, string $recoveryEmail = ''): string {
		$ref = \substr(hash('sha256', $recoveryEmail), 0, 8);
		return $this->verificationToken->create($user, 'verifyRecoveryMail' . $ref, $recoveryEmail, self::TOKEN_LIFETIME);
		return $this->verificationTokenService->create($user, 'verifyRecoveryMail' . $ref, $recoveryEmail, self::TOKEN_LIFETIME);
	}
	public function verifyToken(string $token, IUser $user, string $verificationKey, string $email): void {
		$this->verificationToken->check($token, $user, $verificationKey, $email);
		$this->verificationTokenService->check($token, $user, $verificationKey, $email);
	}
	public function deleteVerificationToken(string $token, IUser $user, string $verificationKey): void {
		$this->verificationToken->delete($token, $user, $verificationKey);
		$this->verificationTokenService->delete($token, $user, $verificationKey);
	}
	public function makeRecoveryEmailVerified(string $userId): void {
		$newRecoveryEmailAddress = $this->getUnverifiedRecoveryEmail($userId);
+69 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace OCA\EmailRecovery\Service;

use OC\Security\VerificationToken\VerificationToken;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IConfig;
use OCP\IUser;
use OCP\Security\ICrypto;
use OCP\Security\VerificationToken\InvalidTokenException;
use OCP\Security\VerificationToken\IVerificationToken;
use OCP\BackgroundJob\IJobList;
use OCP\Security\ISecureRandom;

class VerificationTokenService extends VerificationToken implements IVerificationToken {
	public function __construct(
		private IConfig $config,
		private ICrypto $crypto,
		private ITimeFactory $timeFactory,
		private ISecureRandom $secureRandom,
		private IJobList $jobList,
	) {
		parent::__construct($config, $crypto, $timeFactory, $secureRandom, $jobList);
	}

	public function check(
		string $token,
		?IUser $user,
		string $subject,
		string $passwordPrefix = '',
		bool $expiresWithLogin = false,
	): void {
		if ($user === null) {
			$this->throwInvalidTokenException(InvalidTokenException::USER_UNKNOWN);
		}

		$encryptedToken = $this->config->getUserValue($user->getUID(), 'core', $subject, null);
		if ($encryptedToken === null) {
			$this->throwInvalidTokenException(InvalidTokenException::TOKEN_NOT_FOUND);
		}

		try {
			$decryptedToken = $this->crypto->decrypt($encryptedToken, $passwordPrefix . $this->config->getSystemValueString('secret'));
		} catch (\Exception $e) {
			// Retry with empty secret as a fallback for instances where the secret might not have been set by accident
			try {
				$decryptedToken = $this->crypto->decrypt($encryptedToken, $passwordPrefix);
			} catch (\Exception $e2) {
				$this->throwInvalidTokenException(InvalidTokenException::TOKEN_DECRYPTION_ERROR);
			}
		}

		$splitToken = explode(':', $decryptedToken);
		if (count($splitToken) !== 2) {
			$this->throwInvalidTokenException(InvalidTokenException::TOKEN_INVALID_FORMAT);
		}

		if ($splitToken[0] < ($this->timeFactory->getTime() - self::TOKEN_LIFETIME)
			|| ($expiresWithLogin && $user->getLastLogin() > $splitToken[0])) {
			$this->throwInvalidTokenException(InvalidTokenException::TOKEN_EXPIRED);
		}

		if (!hash_equals($splitToken[1], $token)) {
			$this->throwInvalidTokenException(InvalidTokenException::TOKEN_MISMATCH);
		}
	}
}