2017-04-21 13:09:42 +03:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org>
|
|
|
|
*
|
2019-12-03 21:57:53 +03:00
|
|
|
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
|
2017-11-06 17:56:42 +03:00
|
|
|
* @author Bjoern Schiessle <bjoern@schiessle.org>
|
2019-12-03 21:57:53 +03:00
|
|
|
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
|
|
|
* @author Joas Schilling <coding@schilljs.com>
|
2017-11-06 17:56:42 +03:00
|
|
|
* @author Lukas Reschke <lukas@statuscode.ch>
|
2019-12-03 21:57:53 +03:00
|
|
|
* @author Morris Jobke <hey@morrisjobke.de>
|
2017-11-06 17:56:42 +03:00
|
|
|
* @author Patrik Kernstock <info@pkern.at>
|
2020-12-16 16:54:15 +03:00
|
|
|
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
2017-11-06 17:56:42 +03:00
|
|
|
*
|
2017-04-21 13:09:42 +03:00
|
|
|
* @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
|
2019-12-03 21:57:53 +03:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2017-04-21 13:09:42 +03:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2019-09-17 17:33:27 +03:00
|
|
|
namespace OCA\Settings\BackgroundJobs;
|
2017-04-21 13:09:42 +03:00
|
|
|
|
2020-12-01 16:33:22 +03:00
|
|
|
use OCP\Accounts\IAccountManager;
|
2021-05-12 13:17:07 +03:00
|
|
|
use OCP\Accounts\PropertyDoesNotExistException;
|
2017-04-21 13:09:42 +03:00
|
|
|
use OCP\AppFramework\Http;
|
2020-12-14 18:35:12 +03:00
|
|
|
use OCP\AppFramework\Utility\ITimeFactory;
|
2017-04-21 18:39:00 +03:00
|
|
|
use OCP\BackgroundJob\IJobList;
|
2020-11-24 16:18:47 +03:00
|
|
|
use OCP\BackgroundJob\Job;
|
2017-04-21 13:09:42 +03:00
|
|
|
use OCP\Http\Client\IClientService;
|
|
|
|
use OCP\IConfig;
|
|
|
|
use OCP\ILogger;
|
|
|
|
use OCP\IUserManager;
|
|
|
|
|
|
|
|
class VerifyUserData extends Job {
|
|
|
|
|
|
|
|
/** @var bool */
|
|
|
|
private $retainJob = true;
|
|
|
|
|
|
|
|
/** @var int max number of attempts to send the request */
|
|
|
|
private $maxTry = 24;
|
|
|
|
|
|
|
|
/** @var int how much time should be between two tries (1 hour) */
|
|
|
|
private $interval = 3600;
|
|
|
|
|
2021-05-12 13:17:07 +03:00
|
|
|
/** @var IAccountManager */
|
2017-04-21 13:09:42 +03:00
|
|
|
private $accountManager;
|
|
|
|
|
|
|
|
/** @var IUserManager */
|
|
|
|
private $userManager;
|
|
|
|
|
|
|
|
/** @var IClientService */
|
|
|
|
private $httpClientService;
|
|
|
|
|
|
|
|
/** @var ILogger */
|
|
|
|
private $logger;
|
|
|
|
|
|
|
|
/** @var string */
|
|
|
|
private $lookupServerUrl;
|
|
|
|
|
2018-10-30 19:53:28 +03:00
|
|
|
/** @var IConfig */
|
|
|
|
private $config;
|
|
|
|
|
2021-05-12 13:17:07 +03:00
|
|
|
public function __construct(IAccountManager $accountManager,
|
2017-04-26 14:19:15 +03:00
|
|
|
IUserManager $userManager,
|
|
|
|
IClientService $clientService,
|
|
|
|
ILogger $logger,
|
2020-12-14 18:35:12 +03:00
|
|
|
ITimeFactory $timeFactory,
|
2017-04-26 14:19:15 +03:00
|
|
|
IConfig $config
|
2017-04-21 13:09:42 +03:00
|
|
|
) {
|
2020-12-14 18:35:12 +03:00
|
|
|
parent::__construct($timeFactory);
|
2017-04-26 14:19:15 +03:00
|
|
|
$this->accountManager = $accountManager;
|
|
|
|
$this->userManager = $userManager;
|
|
|
|
$this->httpClientService = $clientService;
|
|
|
|
$this->logger = $logger;
|
|
|
|
|
|
|
|
$lookupServerUrl = $config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
|
|
|
|
$this->lookupServerUrl = rtrim($lookupServerUrl, '/');
|
2018-10-30 19:53:28 +03:00
|
|
|
$this->config = $config;
|
2017-04-21 13:09:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* run the job, then remove it from the jobList
|
|
|
|
*
|
2020-11-24 16:18:47 +03:00
|
|
|
* @param IJobList $jobList
|
2017-07-19 17:06:22 +03:00
|
|
|
* @param ILogger|null $logger
|
2017-04-21 13:09:42 +03:00
|
|
|
*/
|
2020-11-24 16:18:47 +03:00
|
|
|
public function execute(IJobList $jobList, ILogger $logger = null) {
|
2017-04-21 13:09:42 +03:00
|
|
|
if ($this->shouldRun($this->argument)) {
|
|
|
|
parent::execute($jobList, $logger);
|
|
|
|
$jobList->remove($this, $this->argument);
|
|
|
|
if ($this->retainJob) {
|
|
|
|
$this->reAddJob($jobList, $this->argument);
|
2017-05-04 15:53:24 +03:00
|
|
|
} else {
|
|
|
|
$this->resetVerificationState();
|
2017-04-21 13:09:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function run($argument) {
|
|
|
|
$try = (int)$argument['try'] + 1;
|
|
|
|
|
2020-04-10 15:19:56 +03:00
|
|
|
switch ($argument['type']) {
|
2020-12-01 16:33:22 +03:00
|
|
|
case IAccountManager::PROPERTY_WEBSITE:
|
2017-04-21 13:09:42 +03:00
|
|
|
$result = $this->verifyWebsite($argument);
|
|
|
|
break;
|
2020-12-01 16:33:22 +03:00
|
|
|
case IAccountManager::PROPERTY_TWITTER:
|
|
|
|
case IAccountManager::PROPERTY_EMAIL:
|
2017-04-21 13:09:42 +03:00
|
|
|
$result = $this->verifyViaLookupServer($argument, $argument['type']);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// no valid type given, no need to retry
|
|
|
|
$this->logger->error($argument['type'] . ' is no valid type for user account data.');
|
|
|
|
$result = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($result === true || $try > $this->maxTry) {
|
|
|
|
$this->retainJob = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* verify web page
|
|
|
|
*
|
|
|
|
* @param array $argument
|
|
|
|
* @return bool true if we could check the verification code, otherwise false
|
|
|
|
*/
|
|
|
|
protected function verifyWebsite(array $argument) {
|
|
|
|
$result = false;
|
|
|
|
|
2017-04-27 18:58:30 +03:00
|
|
|
$url = rtrim($argument['data'], '/') . '/.well-known/' . 'CloudIdVerificationCode.txt';
|
2017-04-21 13:09:42 +03:00
|
|
|
|
|
|
|
$client = $this->httpClientService->newClient();
|
|
|
|
try {
|
|
|
|
$response = $client->get($url);
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($response->getStatusCode() === Http::STATUS_OK) {
|
|
|
|
$result = true;
|
|
|
|
$publishedCode = $response->getBody();
|
2017-04-21 18:39:00 +03:00
|
|
|
// remove new lines and spaces
|
2017-04-24 14:37:53 +03:00
|
|
|
$publishedCodeSanitized = trim(preg_replace('/\s\s+/', ' ', $publishedCode));
|
2017-04-21 13:09:42 +03:00
|
|
|
$user = $this->userManager->get($argument['uid']);
|
|
|
|
// we don't check a valid user -> give up
|
|
|
|
if ($user === null) {
|
|
|
|
$this->logger->error($argument['uid'] . ' doesn\'t exist, can\'t verify user data.');
|
|
|
|
return $result;
|
|
|
|
}
|
2021-05-12 13:17:07 +03:00
|
|
|
$userAccount = $this->accountManager->getAccount($user);
|
|
|
|
$websiteProp = $userAccount->getProperty(IAccountManager::PROPERTY_WEBSITE);
|
|
|
|
$websiteProp->setVerified($publishedCodeSanitized === $argument['verificationCode']
|
|
|
|
? IAccountManager::VERIFIED
|
|
|
|
: IAccountManager::NOT_VERIFIED
|
|
|
|
);
|
|
|
|
$this->accountManager->updateAccount($userAccount);
|
2017-04-21 13:09:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2021-05-12 13:17:07 +03:00
|
|
|
protected function verifyViaLookupServer(array $argument, string $dataType): bool {
|
2020-04-10 15:19:56 +03:00
|
|
|
if (empty($this->lookupServerUrl) ||
|
2019-06-21 11:54:34 +03:00
|
|
|
$this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes') !== 'yes' ||
|
|
|
|
$this->config->getSystemValue('has_internet_connection', true) === false) {
|
2018-10-17 14:09:11 +03:00
|
|
|
return false;
|
|
|
|
}
|
2017-04-21 13:09:42 +03:00
|
|
|
|
|
|
|
$user = $this->userManager->get($argument['uid']);
|
|
|
|
|
|
|
|
// we don't check a valid user -> give up
|
|
|
|
if ($user === null) {
|
2018-06-22 15:11:25 +03:00
|
|
|
$this->logger->info($argument['uid'] . ' doesn\'t exist, can\'t verify user data.');
|
2017-04-21 13:09:42 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$cloudId = $user->getCloudId();
|
|
|
|
$lookupServerData = $this->queryLookupServer($cloudId);
|
|
|
|
|
|
|
|
// for some reasons we couldn't read any data from the lookup server, try again later
|
2019-04-16 09:40:17 +03:00
|
|
|
if (empty($lookupServerData) || empty($lookupServerData[$dataType])) {
|
2017-04-21 13:09:42 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// lookup server has verification data for wrong user data (e.g. email address), try again later
|
|
|
|
if ($lookupServerData[$dataType]['value'] !== $argument['data']) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// lookup server hasn't verified the email address so far, try again later
|
2021-05-12 13:17:07 +03:00
|
|
|
if ($lookupServerData[$dataType]['verified'] === IAccountManager::NOT_VERIFIED) {
|
2017-04-21 13:09:42 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-05-12 13:17:07 +03:00
|
|
|
try {
|
|
|
|
$userAccount = $this->accountManager->getAccount($user);
|
|
|
|
$property = $userAccount->getProperty($dataType);
|
|
|
|
$property->setVerified(IAccountManager::VERIFIED);
|
|
|
|
$this->accountManager->updateAccount($userAccount);
|
|
|
|
} catch (PropertyDoesNotExistException $e) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-04-21 13:09:42 +03:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string $cloudId
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
protected function queryLookupServer($cloudId) {
|
|
|
|
try {
|
2017-04-21 18:39:00 +03:00
|
|
|
$client = $this->httpClientService->newClient();
|
2017-04-21 13:09:42 +03:00
|
|
|
$response = $client->get(
|
2017-04-21 18:39:00 +03:00
|
|
|
$this->lookupServerUrl . '/users?search=' . urlencode($cloudId) . '&exactCloudId=1',
|
2017-04-21 13:09:42 +03:00
|
|
|
[
|
|
|
|
'timeout' => 10,
|
|
|
|
'connect_timeout' => 3,
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
$body = json_decode($response->getBody(), true);
|
|
|
|
|
2017-05-24 16:08:29 +03:00
|
|
|
if (is_array($body) && isset($body['federationId']) && $body['federationId'] === $cloudId) {
|
2017-04-21 18:39:00 +03:00
|
|
|
return $body;
|
2017-04-21 13:09:42 +03:00
|
|
|
}
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
// do nothing, we will just re-try later
|
|
|
|
}
|
|
|
|
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* re-add background job with new arguments
|
|
|
|
*
|
|
|
|
* @param IJobList $jobList
|
|
|
|
* @param array $argument
|
|
|
|
*/
|
|
|
|
protected function reAddJob(IJobList $jobList, array $argument) {
|
2018-01-26 01:16:13 +03:00
|
|
|
$jobList->add(VerifyUserData::class,
|
2017-04-21 13:09:42 +03:00
|
|
|
[
|
|
|
|
'verificationCode' => $argument['verificationCode'],
|
|
|
|
'data' => $argument['data'],
|
|
|
|
'type' => $argument['type'],
|
|
|
|
'uid' => $argument['uid'],
|
|
|
|
'try' => (int)$argument['try'] + 1,
|
|
|
|
'lastRun' => time()
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* test if it is time for the next run
|
|
|
|
*
|
|
|
|
* @param array $argument
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
protected function shouldRun(array $argument) {
|
|
|
|
$lastRun = (int)$argument['lastRun'];
|
|
|
|
return ((time() - $lastRun) > $this->interval);
|
|
|
|
}
|
|
|
|
|
2017-05-04 15:53:24 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* reset verification state after max tries are reached
|
|
|
|
*/
|
2021-05-12 13:17:07 +03:00
|
|
|
protected function resetVerificationState(): void {
|
2017-05-04 15:53:24 +03:00
|
|
|
$user = $this->userManager->get($this->argument['uid']);
|
|
|
|
if ($user !== null) {
|
2021-05-12 13:17:07 +03:00
|
|
|
$userAccount = $this->accountManager->getAccount($user);
|
|
|
|
try {
|
|
|
|
$property = $userAccount->getProperty($this->argument['type']);
|
|
|
|
$property->setVerified(IAccountManager::NOT_VERIFIED);
|
|
|
|
$this->accountManager->updateAccount($userAccount);
|
|
|
|
} catch (PropertyDoesNotExistException $e) {
|
|
|
|
return;
|
|
|
|
}
|
2017-05-04 15:53:24 +03:00
|
|
|
}
|
|
|
|
}
|
2017-04-21 13:09:42 +03:00
|
|
|
}
|