VerificationController.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OCA\Provisioning_API\Controller;
  8. use InvalidArgumentException;
  9. use OC\Security\Crypto;
  10. use OCP\Accounts\IAccountManager;
  11. use OCP\AppFramework\Controller;
  12. use OCP\AppFramework\Http\Attribute\BruteForceProtection;
  13. use OCP\AppFramework\Http\Attribute\NoAdminRequired;
  14. use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
  15. use OCP\AppFramework\Http\Attribute\OpenAPI;
  16. use OCP\AppFramework\Http\TemplateResponse;
  17. use OCP\IL10N;
  18. use OCP\IRequest;
  19. use OCP\IUserManager;
  20. use OCP\IUserSession;
  21. use OCP\Security\VerificationToken\InvalidTokenException;
  22. use OCP\Security\VerificationToken\IVerificationToken;
  23. #[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
  24. class VerificationController extends Controller {
  25. /** @var Crypto */
  26. private $crypto;
  27. public function __construct(
  28. string $appName,
  29. IRequest $request,
  30. private IVerificationToken $verificationToken,
  31. private IUserManager $userManager,
  32. private IL10N $l10n,
  33. private IUserSession $userSession,
  34. private IAccountManager $accountManager,
  35. Crypto $crypto,
  36. ) {
  37. parent::__construct($appName, $request);
  38. $this->crypto = $crypto;
  39. }
  40. /**
  41. * @NoSubAdminRequired
  42. */
  43. #[NoAdminRequired]
  44. #[NoCSRFRequired]
  45. public function showVerifyMail(string $token, string $userId, string $key): TemplateResponse {
  46. if ($this->userSession->getUser()->getUID() !== $userId) {
  47. // not a public page, hence getUser() must return an IUser
  48. throw new InvalidArgumentException('Logged in account is not mail address owner');
  49. }
  50. $email = $this->crypto->decrypt($key);
  51. return new TemplateResponse(
  52. 'core', 'confirmation', [
  53. 'title' => $this->l10n->t('Email confirmation'),
  54. 'message' => $this->l10n->t('To enable the email address %s please click the button below.', [$email]),
  55. 'action' => $this->l10n->t('Confirm'),
  56. ], TemplateResponse::RENDER_AS_GUEST);
  57. }
  58. /**
  59. * @NoSubAdminRequired
  60. */
  61. #[NoAdminRequired]
  62. #[BruteForceProtection(action: 'emailVerification')]
  63. public function verifyMail(string $token, string $userId, string $key): TemplateResponse {
  64. $throttle = false;
  65. try {
  66. if ($this->userSession->getUser()->getUID() !== $userId) {
  67. throw new InvalidArgumentException('Logged in account is not mail address owner');
  68. }
  69. $email = $this->crypto->decrypt($key);
  70. $ref = \substr(hash('sha256', $email), 0, 8);
  71. $user = $this->userManager->get($userId);
  72. $this->verificationToken->check($token, $user, 'verifyMail' . $ref, $email);
  73. $userAccount = $this->accountManager->getAccount($user);
  74. $emailProperty = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL)
  75. ->getPropertyByValue($email);
  76. if ($emailProperty === null) {
  77. throw new InvalidArgumentException($this->l10n->t('Email was already removed from account and cannot be confirmed anymore.'));
  78. }
  79. $emailProperty->setLocallyVerified(IAccountManager::VERIFIED);
  80. $this->accountManager->updateAccount($userAccount);
  81. $this->verificationToken->delete($token, $user, 'verifyMail' . $ref);
  82. } catch (InvalidTokenException $e) {
  83. if ($e->getCode() === InvalidTokenException::TOKEN_EXPIRED) {
  84. $error = $this->l10n->t('Could not verify mail because the token is expired.');
  85. } else {
  86. $throttle = true;
  87. $error = $this->l10n->t('Could not verify mail because the token is invalid.');
  88. }
  89. } catch (InvalidArgumentException $e) {
  90. $error = $e->getMessage();
  91. } catch (\Exception $e) {
  92. $error = $this->l10n->t('An unexpected error occurred. Please contact your admin.');
  93. }
  94. if (isset($error)) {
  95. $response = new TemplateResponse(
  96. 'core', 'error', [
  97. 'errors' => [['error' => $error]]
  98. ], TemplateResponse::RENDER_AS_GUEST);
  99. if ($throttle) {
  100. $response->throttle();
  101. }
  102. return $response;
  103. }
  104. return new TemplateResponse(
  105. 'core', 'success', [
  106. 'title' => $this->l10n->t('Email confirmation successful'),
  107. 'message' => $this->l10n->t('Email confirmation successful'),
  108. ], TemplateResponse::RENDER_AS_GUEST);
  109. }
  110. }