diff options
author | Arthur Schiwon <blizzz@arthur-schiwon.de> | 2021-08-20 19:59:08 +0200 |
---|---|---|
committer | Arthur Schiwon <blizzz@arthur-schiwon.de> | 2021-09-09 14:03:35 +0200 |
commit | aacaad2a3f56893c6be463ec7a21c868322654ee (patch) | |
tree | 0e995c930501cae44ba49ff707ddc896c6048093 /apps/provisioning_api | |
parent | 19cc757531959a14df40a79d550c82b39e4bc5a2 (diff) | |
download | nextcloud-server-aacaad2a3f56893c6be463ec7a21c868322654ee.tar.gz nextcloud-server-aacaad2a3f56893c6be463ec7a21c868322654ee.zip |
implement verification for additional mails
- mails added by (sub)admins are automatically verified
- provisioning_api controller as verification endpoint
- IAccountProperty gets a locallyVerified property
- IPropertyCollection gets a method to fetch an IAccountProperty by value
- an remove equivalent was already present
- AccountManager always initiates mail verification on update if necessary
- add core success template for arbitrary title and message
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
Diffstat (limited to 'apps/provisioning_api')
6 files changed, 139 insertions, 6 deletions
diff --git a/apps/provisioning_api/appinfo/routes.php b/apps/provisioning_api/appinfo/routes.php index 2f981e0c924..81a5bb94e02 100644 --- a/apps/provisioning_api/appinfo/routes.php +++ b/apps/provisioning_api/appinfo/routes.php @@ -74,4 +74,8 @@ return [ ['name' => 'AppConfig#setValue', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'POST'], ['name' => 'AppConfig#deleteKey', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'DELETE'], ], + 'routes' => [ + // Verification + ['name' => 'Verification#verifyMail', 'url' => '/mailVerification/{key}/{token}/{userId}', 'verb' => 'GET'], + ] ]; diff --git a/apps/provisioning_api/composer/composer/autoload_classmap.php b/apps/provisioning_api/composer/composer/autoload_classmap.php index 22927806e65..447f92afc8d 100644 --- a/apps/provisioning_api/composer/composer/autoload_classmap.php +++ b/apps/provisioning_api/composer/composer/autoload_classmap.php @@ -14,6 +14,7 @@ return array( 'OCA\\Provisioning_API\\Controller\\AppsController' => $baseDir . '/../lib/Controller/AppsController.php', 'OCA\\Provisioning_API\\Controller\\GroupsController' => $baseDir . '/../lib/Controller/GroupsController.php', 'OCA\\Provisioning_API\\Controller\\UsersController' => $baseDir . '/../lib/Controller/UsersController.php', + 'OCA\\Provisioning_API\\Controller\\VerificationController' => $baseDir . '/../lib/Controller/VerificationController.php', 'OCA\\Provisioning_API\\FederatedShareProviderFactory' => $baseDir . '/../lib/FederatedShareProviderFactory.php', 'OCA\\Provisioning_API\\Listener\\UserDeletedListener' => $baseDir . '/../lib/Listener/UserDeletedListener.php', 'OCA\\Provisioning_API\\Middleware\\Exceptions\\NotSubAdminException' => $baseDir . '/../lib/Middleware/Exceptions/NotSubAdminException.php', diff --git a/apps/provisioning_api/composer/composer/autoload_static.php b/apps/provisioning_api/composer/composer/autoload_static.php index f5a4b73f4f8..6dbf6b45c79 100644 --- a/apps/provisioning_api/composer/composer/autoload_static.php +++ b/apps/provisioning_api/composer/composer/autoload_static.php @@ -29,6 +29,7 @@ class ComposerStaticInitProvisioning_API 'OCA\\Provisioning_API\\Controller\\AppsController' => __DIR__ . '/..' . '/../lib/Controller/AppsController.php', 'OCA\\Provisioning_API\\Controller\\GroupsController' => __DIR__ . '/..' . '/../lib/Controller/GroupsController.php', 'OCA\\Provisioning_API\\Controller\\UsersController' => __DIR__ . '/..' . '/../lib/Controller/UsersController.php', + 'OCA\\Provisioning_API\\Controller\\VerificationController' => __DIR__ . '/..' . '/../lib/Controller/VerificationController.php', 'OCA\\Provisioning_API\\FederatedShareProviderFactory' => __DIR__ . '/..' . '/../lib/FederatedShareProviderFactory.php', 'OCA\\Provisioning_API\\Listener\\UserDeletedListener' => __DIR__ . '/..' . '/../lib/Listener/UserDeletedListener.php', 'OCA\\Provisioning_API\\Middleware\\Exceptions\\NotSubAdminException' => __DIR__ . '/..' . '/../lib/Middleware/Exceptions/NotSubAdminException.php', diff --git a/apps/provisioning_api/composer/composer/installed.php b/apps/provisioning_api/composer/composer/installed.php index b99ca67ef3a..561b3105cde 100644 --- a/apps/provisioning_api/composer/composer/installed.php +++ b/apps/provisioning_api/composer/composer/installed.php @@ -5,7 +5,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../', 'aliases' => array(), - 'reference' => 'fa56c13484afa1baf908b93ed5b6990c6a0e9ad6', + 'reference' => '2e49000abb5acb09de041369a2239db23fa63ec7', 'name' => '__root__', 'dev' => false, ), @@ -16,7 +16,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../', 'aliases' => array(), - 'reference' => 'fa56c13484afa1baf908b93ed5b6990c6a0e9ad6', + 'reference' => '2e49000abb5acb09de041369a2239db23fa63ec7', 'dev_requirement' => false, ), ), diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php index a0eda5848ec..aae34975c25 100644 --- a/apps/provisioning_api/lib/Controller/UsersController.php +++ b/apps/provisioning_api/lib/Controller/UsersController.php @@ -621,6 +621,10 @@ class UsersController extends AUserData { throw new OCSException('', OCSController::RESPOND_NOT_FOUND); } + $subAdminManager = $this->groupManager->getSubAdmin(); + $isAdminOrSubadmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID()) + || $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser); + $permittedFields = []; if ($targetUser->getUID() === $currentLoggedInUser->getUID()) { // Editing self (display, email) @@ -628,11 +632,8 @@ class UsersController extends AUserData { $permittedFields[] = IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX; } else { // Check if admin / subadmin - $subAdminManager = $this->groupManager->getSubAdmin(); - if ($this->groupManager->isAdmin($currentLoggedInUser->getUID()) - || $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) { + if ($isAdminOrSubadmin) { // They have permissions over the user - $permittedFields[] = IAccountManager::COLLECTION_EMAIL; } else { // No rights @@ -652,6 +653,11 @@ class UsersController extends AUserData { $mailCollection->removePropertyByValue($key); if ($value !== '') { $mailCollection->addPropertyWithDefaults($value); + $property = $mailCollection->getPropertyByValue($key); + if ($isAdminOrSubadmin && $property) { + // admin set mails are auto-verified + $property->setLocallyVerified(IAccountManager::VERIFIED); + } } $this->accountManager->updateAccount($userAccount); break; diff --git a/apps/provisioning_api/lib/Controller/VerificationController.php b/apps/provisioning_api/lib/Controller/VerificationController.php new file mode 100644 index 00000000000..b248d3e8285 --- /dev/null +++ b/apps/provisioning_api/lib/Controller/VerificationController.php @@ -0,0 +1,121 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2021 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Provisioning_API\Controller; + +use InvalidArgumentException; +use OC\Security\Crypto; +use OCP\Accounts\IAccountManager; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\IL10N; +use OCP\IRequest; +use OCP\IUserManager; +use OCP\IUserSession; +use OCP\Security\VerificationToken\InvalidTokenException; +use OCP\Security\VerificationToken\IVerificationToken; + +class VerificationController extends Controller { + + /** @var IVerificationToken */ + private $verificationToken; + /** @var IUserManager */ + private $userManager; + /** @var IL10N */ + private $l10n; + /** @var IUserSession */ + private $userSession; + /** @var IAccountManager */ + private $accountManager; + /** @var Crypto */ + private $crypto; + + public function __construct( + string $appName, + IRequest $request, + IVerificationToken $verificationToken, + IUserManager $userManager, + IL10N $l10n, + IUserSession $userSession, + IAccountManager $accountManager, + Crypto $crypto + ) { + parent::__construct($appName, $request); + $this->verificationToken = $verificationToken; + $this->userManager = $userManager; + $this->l10n = $l10n; + $this->userSession = $userSession; + $this->accountManager = $accountManager; + $this->crypto = $crypto; + } + + /** + * @NoCSRFRequired + */ + public function verifyMail(string $token, string $userId, string $key) { + try { + if ($this->userSession->getUser()->getUID() !== $userId) { + throw new InvalidArgumentException('Logged in user is not mail address owner'); + } + $email = $this->crypto->decrypt($key); + $ref = \substr(hash('sha256', $email), 0, 8); + + $user = $this->userManager->get($userId); + $this->verificationToken->check($token, $user, 'verifyMail' . $ref, $email); + + $userAccount = $this->accountManager->getAccount($user); + $emailProperty = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL) + ->getPropertyByValue($email); + + if ($emailProperty === null) { + throw new InvalidArgumentException($this->l10n->t('Email was already removed from account and cannot be confirmed anymore.')); + } + $emailProperty->setLocallyVerified(IAccountManager::VERIFIED); + $this->accountManager->updateAccount($userAccount); + } catch (InvalidTokenException $e) { + $error = $e->getCode() === InvalidTokenException::TOKEN_EXPIRED + ? $this->l10n->t('Could not verify mail because the token is expired.') + : $this->l10n->t('Could not verify mail because the token is invalid.'); + } catch (InvalidArgumentException $e) { + $error = $e->getMessage(); + } catch (\Exception $e) { + $error = $this->l10n->t('An unexpected error occurred. Please consult your sysadmin.'); + } + + if (isset($error)) { + return new TemplateResponse( + 'core', 'error', [ + 'errors' => [['error' => $error]] + ], 'guest'); + } + + return new TemplateResponse( + 'core', 'success', [ + 'title' => $this->l10n->t('Email confirmation successful'), + 'message' => $this->l10n->t('Email confirmation successful'), + ], 'guest'); + } +} |