Passed
Push — master ( 6846a8...5b6112 )
by Julius
15:13
created

GetSharedSecret::run()   C

Complexity

Conditions 13
Paths 218

Size

Total Lines 80
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 56
nc 218
nop 1
dl 0
loc 80
rs 5.5583
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Bjoern Schiessle <[email protected]>
7
 * @author Björn Schießle <[email protected]>
8
 * @author Joas Schilling <[email protected]>
9
 * @author Lukas Reschke <[email protected]>
10
 * @author Morris Jobke <[email protected]>
11
 * @author Robin Appelman <[email protected]>
12
 * @author Roeland Jago Douma <[email protected]>
13
 *
14
 * @license AGPL-3.0
15
 *
16
 * This code is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License, version 3,
18
 * as published by the Free Software Foundation.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License, version 3,
26
 * along with this program. If not, see <http://www.gnu.org/licenses/>
27
 *
28
 */
29
30
namespace OCA\Federation\BackgroundJob;
31
32
use GuzzleHttp\Exception\ClientException;
33
use GuzzleHttp\Exception\RequestException;
34
use OCA\Federation\TrustedServers;
35
use OCP\AppFramework\Http;
36
use OCP\AppFramework\Utility\ITimeFactory;
37
use OCP\BackgroundJob\IJobList;
38
use OCP\BackgroundJob\Job;
39
use OCP\Http\Client\IClient;
40
use OCP\Http\Client\IClientService;
41
use OCP\Http\Client\IResponse;
42
use OCP\ILogger;
43
use OCP\IURLGenerator;
44
use OCP\OCS\IDiscoveryService;
45
46
/**
47
 * Class GetSharedSecret
48
 *
49
 * request shared secret from remote Nextcloud
50
 *
51
 * @package OCA\Federation\Backgroundjob
52
 */
53
class GetSharedSecret extends Job {
54
55
	/** @var IClient */
56
	private $httpClient;
57
58
	/** @var IJobList */
59
	private $jobList;
60
61
	/** @var IURLGenerator */
62
	private $urlGenerator;
63
64
	/** @var TrustedServers  */
65
	private $trustedServers;
66
67
	/** @var IDiscoveryService  */
68
	private $ocsDiscoveryService;
69
70
	/** @var ILogger */
71
	private $logger;
72
73
	/** @var bool */
74
	protected $retainJob = false;
75
76
	private $defaultEndPoint = '/ocs/v2.php/apps/federation/api/v1/shared-secret';
77
78
	/** @var  int  30 day = 2592000sec */
79
	private $maxLifespan = 2592000;
80
81
	/**
82
	 * RequestSharedSecret constructor.
83
	 *
84
	 * @param IClientService $httpClientService
85
	 * @param IURLGenerator $urlGenerator
86
	 * @param IJobList $jobList
87
	 * @param TrustedServers $trustedServers
88
	 * @param ILogger $logger
89
	 * @param IDiscoveryService $ocsDiscoveryService
90
	 * @param ITimeFactory $timeFactory
91
	 */
92
	public function __construct(
93
		IClientService $httpClientService,
94
		IURLGenerator $urlGenerator,
95
		IJobList $jobList,
96
		TrustedServers $trustedServers,
97
		ILogger $logger,
98
		IDiscoveryService $ocsDiscoveryService,
99
		ITimeFactory $timeFactory
100
	) {
101
		parent::__construct($timeFactory);
102
		$this->logger = $logger;
103
		$this->httpClient = $httpClientService->newClient();
104
		$this->jobList = $jobList;
105
		$this->urlGenerator = $urlGenerator;
106
		$this->ocsDiscoveryService = $ocsDiscoveryService;
107
		$this->trustedServers = $trustedServers;
108
	}
109
110
	/**
111
	 * run the job, then remove it from the joblist
112
	 *
113
	 * @param IJobList $jobList
114
	 * @param ILogger|null $logger
115
	 */
116
	public function execute(IJobList $jobList, ILogger $logger = null) {
117
		$target = $this->argument['url'];
118
		// only execute if target is still in the list of trusted domains
119
		if ($this->trustedServers->isTrustedServer($target)) {
120
			$this->parentExecute($jobList, $logger);
121
		}
122
123
		$jobList->remove($this, $this->argument);
124
125
		if ($this->retainJob) {
126
			$this->reAddJob($this->argument);
127
		}
128
	}
129
130
	/**
131
	 * call execute() method of parent
132
	 *
133
	 * @param IJobList $jobList
134
	 * @param ILogger $logger
135
	 */
136
	protected function parentExecute($jobList, $logger = null) {
137
		parent::execute($jobList, $logger);
138
	}
139
140
	protected function run($argument) {
141
		$target = $argument['url'];
142
		$created = isset($argument['created']) ? (int)$argument['created'] : $this->time->getTime();
143
		$currentTime = $this->time->getTime();
144
		$source = $this->urlGenerator->getAbsoluteURL('/');
145
		$source = rtrim($source, '/');
146
		$token = $argument['token'];
147
148
		// kill job after 30 days of trying
149
		$deadline = $currentTime - $this->maxLifespan;
150
		if ($created < $deadline) {
151
			$this->retainJob = false;
152
			$this->trustedServers->setServerStatus($target,TrustedServers::STATUS_FAILURE);
153
			return;
154
		}
155
156
		$endPoints = $this->ocsDiscoveryService->discover($target, 'FEDERATED_SHARING');
157
		$endPoint = isset($endPoints['shared-secret']) ? $endPoints['shared-secret'] : $this->defaultEndPoint;
158
159
		// make sure that we have a well formatted url
160
		$url = rtrim($target, '/') . '/' . trim($endPoint, '/');
161
162
		$result = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
163
		try {
164
			$result = $this->httpClient->get(
165
				$url,
166
				[
167
					'query' =>
168
						[
169
							'url' => $source,
170
							'token' => $token,
171
							'format' => 'json',
172
						],
173
					'timeout' => 3,
174
					'connect_timeout' => 3,
175
				]
176
			);
177
178
			$status = $result->getStatusCode();
179
		} catch (ClientException $e) {
180
			$status = $e->getCode();
181
			if ($status === Http::STATUS_FORBIDDEN) {
182
				$this->logger->info($target . ' refused to exchange a shared secret with you.', ['app' => 'federation']);
183
			} else {
184
				$this->logger->info($target . ' responded with a ' . $status . ' containing: ' . $e->getMessage(), ['app' => 'federation']);
185
			}
186
		} catch (RequestException $e) {
187
			$status = -1; // There is no status code if we could not connect
188
			$this->logger->logException($e, [
189
				'message' => 'Could not connect to ' . $target,
190
				'level' => ILogger::INFO,
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::INFO has been deprecated: 20.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

190
				'level' => /** @scrutinizer ignore-deprecated */ ILogger::INFO,

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
191
				'app' => 'federation',
192
			]);
193
		} catch (\Throwable $e) {
194
			$status = Http::STATUS_INTERNAL_SERVER_ERROR;
195
			$this->logger->logException($e, ['app' => 'federation']);
196
		}
197
198
		// if we received a unexpected response we try again later
199
		if (
200
			$status !== Http::STATUS_OK
201
			&& $status !== Http::STATUS_FORBIDDEN
202
		) {
203
			$this->retainJob = true;
204
		}
205
206
		if ($status === Http::STATUS_OK && $result instanceof IResponse) {
207
			$body = $result->getBody();
208
			$result = json_decode($body, true);
209
			if (isset($result['ocs']['data']['sharedSecret'])) {
210
				$this->trustedServers->addSharedSecret(
211
						$target,
212
						$result['ocs']['data']['sharedSecret']
213
				);
214
			} else {
215
				$this->logger->error(
216
						'remote server "' . $target . '"" does not return a valid shared secret. Received data: ' . $body,
0 ignored issues
show
Bug introduced by
Are you sure $body of type resource|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

216
						'remote server "' . $target . '"" does not return a valid shared secret. Received data: ' . /** @scrutinizer ignore-type */ $body,
Loading history...
217
						['app' => 'federation']
218
				);
219
				$this->trustedServers->setServerStatus($target, TrustedServers::STATUS_FAILURE);
220
			}
221
		}
222
	}
223
224
	/**
225
	 * re-add background job
226
	 *
227
	 * @param array $argument
228
	 */
229
	protected function reAddJob(array $argument) {
230
		$url = $argument['url'];
231
		$created = isset($argument['created']) ? (int)$argument['created'] : $this->time->getTime();
232
		$token = $argument['token'];
233
		$this->jobList->add(
234
			GetSharedSecret::class,
235
			[
236
				'url' => $url,
237
				'token' => $token,
238
				'created' => $created
239
			]
240
		);
241
	}
242
}
243