Passed
Push — master ( a65c00...940a31 )
by Roeland
11:18 queued 02:26
created

RetryJob::getUserAccountData()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 27
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 19
c 0
b 0
f 0
nc 6
nop 1
dl 0
loc 27
rs 9.6333
1
<?php
2
declare(strict_types=1);
3
/**
4
 * @copyright Copyright (c) 2016 Bjoern Schiessle <[email protected]>
5
 * @copyright Copyright (c) 2019 Joas Schilling <[email protected]>
6
 *
7
 * @license GNU AGPL version 3 or any later version
8
 *
9
 * This program is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License as
11
 * published by the Free Software Foundation, either version 3 of the
12
 * License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 *
22
 */
23
24
namespace OCA\LookupServerConnector\BackgroundJobs;
25
26
27
use OC\Security\IdentityProof\Signer;
28
use OCP\Accounts\IAccountManager;
29
use OCP\AppFramework\Utility\ITimeFactory;
30
use OCP\BackgroundJob\Job;
31
use OCP\BackgroundJob\IJobList;
32
use OCP\Http\Client\IClientService;
33
use OCP\IConfig;
34
use OCP\ILogger;
35
use OCP\IUser;
36
use OCP\IUserManager;
37
38
class RetryJob extends Job {
39
	/** @var IClientService */
40
	private $clientService;
41
	/** @var string */
42
	private $lookupServer;
43
	/** @var IConfig */
44
	private $config;
45
	/** @var IUserManager */
46
	private $userManager;
47
	/** @var IAccountManager */
48
	private $accountManager;
49
	/** @var Signer */
50
	private $signer;
51
	/** @var int */
52
	protected $retries = 0;
53
	/** @var bool */
54
	protected $retainJob = false;
55
56
	/**
57
	 * @param ITimeFactory $time
58
	 * @param IClientService $clientService
59
	 * @param IConfig $config
60
	 * @param IUserManager $userManager
61
	 * @param IAccountManager $accountManager
62
	 * @param Signer $signer
63
	 */
64
	public function __construct(ITimeFactory $time,
65
								IClientService $clientService,
66
								IConfig $config,
67
								IUserManager $userManager,
68
								IAccountManager $accountManager,
69
								Signer $signer) {
70
		parent::__construct($time);
71
		$this->clientService = $clientService;
72
		$this->config = $config;
73
		$this->userManager = $userManager;
74
		$this->accountManager = $accountManager;
75
		$this->signer = $signer;
76
77
		$this->lookupServer = $config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
78
		if (!empty($this->lookupServer)) {
79
			$this->lookupServer = rtrim($this->lookupServer, '/');
80
			$this->lookupServer .= '/users';
81
		}
82
	}
83
84
	/**
85
	 * run the job, then remove it from the jobList
86
	 *
87
	 * @param IJobList $jobList
88
	 * @param ILogger|null $logger
89
	 */
90
	public function execute($jobList, ILogger $logger = null): void {
91
		if (!isset($this->argument['userId'])) {
92
			// Old background job without user id, just drop it.
93
			$jobList->remove($this, $this->argument);
94
			return;
95
		}
96
97
		$this->retries = (int) $this->config->getUserValue($this->argument['userId'], 'lookup_server_connector', 'update_retries', 0);
98
99
		if ($this->shouldRemoveBackgroundJob()) {
100
			$jobList->remove($this, $this->argument);
101
			return;
102
		}
103
104
		if ($this->shouldRun()) {
105
			parent::execute($jobList, $logger);
106
			if (!$this->retainJob) {
107
				$jobList->remove($this, $this->argument);
108
			}
109
		}
110
	}
111
112
	/**
113
	 * Check if we should kill the background job:
114
	 *
115
	 * - internet connection is disabled
116
	 * - no valid lookup server URL given
117
	 * - lookup server was disabled by the admin
118
	 * - max retries are reached (set to 5)
119
	 *
120
	 * @return bool
121
	 */
122
	protected function shouldRemoveBackgroundJob(): bool {
123
		return $this->config->getSystemValueBool('has_internet_connection', true) === false ||
124
			$this->config->getSystemValueString('lookup_server', 'https://lookup.nextcloud.com') === '' ||
125
			$this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes') !== 'yes' ||
126
			$this->retries >= 5;
127
	}
128
129
	protected function shouldRun(): bool {
130
		$delay = 100 * 6 ** $this->retries;
131
		return ($this->time->getTime() - $this->lastRun) > $delay;
132
	}
133
134
	protected function run($argument): void {
135
		$user = $this->userManager->get($this->argument['userId']);
136
		if (!$user instanceof IUser) {
137
			// User does not exist anymore
138
			return;
139
		}
140
141
		$data = $this->getUserAccountData($user);
142
		$signedData = $this->signer->sign('lookupserver', $data, $user);
143
		$client = $this->clientService->newClient();
144
145
		try {
146
			if (count($data) === 1) {
147
				// No public data, just the federation Id
148
				$client->delete($this->lookupServer,
149
					[
150
						'body' => json_encode($signedData),
151
						'timeout' => 10,
152
						'connect_timeout' => 3,
153
					]
154
				);
155
			} else {
156
				$client->post($this->lookupServer,
157
					[
158
						'body' => json_encode($signedData),
159
						'timeout' => 10,
160
						'connect_timeout' => 3,
161
					]
162
				);
163
			}
164
165
			// Reset retry counter
166
			$this->config->deleteUserValue(
167
				$user->getUID(),
168
				'lookup_server_connector',
169
				'update_retries'
170
			);
171
172
		} catch (\Exception $e) {
173
			// An error occurred, retry later
174
			$this->retainJob = true;
175
			$this->config->setUserValue(
176
				$user->getUID(),
177
				'lookup_server_connector',
178
				'update_retries',
179
				$this->retries + 1
180
			);
181
		}
182
	}
183
184
	protected function getUserAccountData(IUser $user): array {
185
		$account = $this->accountManager->getAccount($user);
186
187
		$publicData = [];
188
		foreach ($account->getProperties() as $property) {
189
			if ($property->getScope() === IAccountManager::VISIBILITY_PUBLIC) {
190
				$publicData[$property->getName()] = $property->getValue();
191
			}
192
		}
193
194
		$data = ['federationId' => $user->getCloudId()];
195
		if (!empty($publicData)) {
196
			$data['name'] = $publicData[IAccountManager::PROPERTY_DISPLAYNAME]['value'] ?? '';
197
			$data['email'] = $publicData[IAccountManager::PROPERTY_EMAIL]['value'] ?? '';
198
			$data['address'] = $publicData[IAccountManager::PROPERTY_ADDRESS]['value'] ?? '';
199
			$data['website'] = $publicData[IAccountManager::PROPERTY_WEBSITE]['value'] ?? '';
200
			$data['twitter'] = $publicData[IAccountManager::PROPERTY_TWITTER]['value'] ?? '';
201
			$data['phone'] = $publicData[IAccountManager::PROPERTY_PHONE]['value'] ?? '';
202
			$data['twitter_signature'] = $publicData[IAccountManager::PROPERTY_TWITTER]['signature'] ?? '';
203
			$data['website_signature'] = $publicData[IAccountManager::PROPERTY_WEBSITE]['signature'] ?? '';
204
			$data['verificationStatus'] = [
205
				IAccountManager::PROPERTY_WEBSITE => $publicData[IAccountManager::PROPERTY_WEBSITE]['verified'] ?? '',
206
				IAccountManager::PROPERTY_TWITTER => $publicData[IAccountManager::PROPERTY_TWITTER]['verified'] ?? '',
207
			];
208
		}
209
210
		return $data;
211
	}
212
}
213