Completed
Pull Request — master (#32303)
by Victor
09:37
created

Notifications   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 333
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
dl 0
loc 333
rs 9.2
c 0
b 0
f 0
wmc 40
lcom 1
cbo 8

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
A sendRemoteShare() 0 6 2
B requestReShare() 0 26 7
A sendRemoteUnShare() 0 3 1
A sendRevokeShare() 0 3 1
A sendPermissionChange() 0 3 1
A sendAcceptShare() 0 3 1
A sendDeclineShare() 0 3 1
B sendUpdateToRemote() 0 33 6
A getTimestamp() 0 3 1
B tryHttpPostToShareEndpoint() 0 45 7
A sendOcmRemoteShare() 0 32 5
B sendPreOcmRemoteShare() 0 30 6

How to fix   Complexity   

Complex Class

Complex classes like Notifications often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Notifications, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @author Björn Schießle <[email protected]>
4
 * @author Joas Schilling <[email protected]>
5
 * @author Lukas Reschke <[email protected]>
6
 * @author Thomas Müller <[email protected]>
7
 * @author Vincent Petry <[email protected]>
8
 *
9
 * @copyright Copyright (c) 2018, ownCloud GmbH
10
 * @license AGPL-3.0
11
 *
12
 * This code is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License, version 3,
14
 * as published by the Free Software Foundation.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License, version 3,
22
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
23
 *
24
 */
25
26
namespace OCA\FederatedFileSharing;
27
28
use OCP\AppFramework\Http;
29
use OCP\BackgroundJob\IJobList;
30
use OCP\Http\Client\IClientService;
31
use OCP\IConfig;
32
33
class Notifications {
34
	const RESPONSE_FORMAT = 'json'; // default response format for ocs calls
35
36
	/** @var AddressHandler */
37
	private $addressHandler;
38
39
	/** @var IClientService */
40
	private $httpClientService;
41
42
	/** @var DiscoveryManager */
43
	private $discoveryManager;
44
45 1
	/** @var IJobList  */
46
	private $jobList;
47
48
	/** @var IConfig */
49 1
	private $config;
50 1
51 1
	/**
52
	 * @param AddressHandler $addressHandler
53
	 * @param IClientService $httpClientService
54
	 * @param DiscoveryManager $discoveryManager
55
	 * @param IJobList $jobList
56
	 * @param IConfig $config
57
	 */
58
	public function __construct(
59
		AddressHandler $addressHandler,
60
		IClientService $httpClientService,
61
		DiscoveryManager $discoveryManager,
62
		IJobList $jobList,
63
		IConfig $config
64
	) {
65
		$this->addressHandler = $addressHandler;
66
		$this->httpClientService = $httpClientService;
67
		$this->discoveryManager = $discoveryManager;
68
		$this->jobList = $jobList;
69
		$this->config = $config;
70
	}
71
72
	/**
73
	 * send server-to-server share to remote server
74
	 *
75
	 * @param string $token
76
	 * @param string $shareWith
77
	 * @param string $name
78
	 * @param int $remote_id
79
	 * @param string $owner
80
	 * @param string $ownerFederatedId
81
	 * @param string $sharedBy
82
	 * @param string $sharedByFederatedId
83
	 * @return bool
84
	 * @throws \OC\HintException
85
	 * @throws \OC\ServerNotAvailableException
86
	 */
87
	public function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner, $ownerFederatedId, $sharedBy, $sharedByFederatedId) {
88
		$ocmRemoteShareSuccess = $this->sendOcmRemoteShare($token, $shareWith, $name, $remote_id, $owner, $ownerFederatedId, $sharedBy, $sharedByFederatedId);
89
		if (!$ocmRemoteShareSuccess) {
90
			return $this->sendPreOcmRemoteShare($token, $shareWith, $name, $remote_id, $owner, $ownerFederatedId, $sharedBy, $sharedByFederatedId);
91
		}
92
	}
93
94
	/**
95
	 * ask owner to re-share the file with the given user
96
	 *
97
	 * @param string $token
98
	 * @param int $id remote Id
99
	 * @param int $shareId internal share Id
100
	 * @param string $remote remote address of the owner
101
	 * @param string $shareWith
102
	 * @param int $permission
103
	 * @return bool
104
	 * @throws \Exception
105
	 */
106
	public function requestReShare($token, $id, $shareId, $remote, $shareWith, $permission) {
107
		$fields = [
108
			'shareWith' => $shareWith,
109
			'token' => $token,
110
			'permission' => $permission,
111
			'remoteId' => $shareId
112
		];
113
114
		$url = $this->addressHandler->removeProtocolFromUrl($remote);
115
		$result = $this->tryHttpPostToShareEndpoint(\rtrim($url, '/'), '/' . $id . '/reshare', $fields);
116
		$status = \json_decode($result['result'], true);
117
118
		$httpRequestSuccessful = $result['success'];
119
		$ocsCallSuccessful = $status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200;
120
		$validToken = isset($status['ocs']['data']['token']) && \is_string($status['ocs']['data']['token']);
121
		$validRemoteId = isset($status['ocs']['data']['remoteId']);
122
123
		if ($httpRequestSuccessful && $ocsCallSuccessful && $validToken && $validRemoteId) {
124
			return [
125
				$status['ocs']['data']['token'],
126
				(int)$status['ocs']['data']['remoteId']
127
			];
128
		}
129
130
		return false;
131
	}
132
133
	/**
134
	 * send server-to-server unshare to remote server
135
	 *
136
	 * @param string $remote url
137
	 * @param int $id share id
138
	 * @param string $token
139
	 * @return bool
140
	 */
141
	public function sendRemoteUnShare($remote, $id, $token) {
142
		return $this->sendUpdateToRemote($remote, $id, $token, 'unshare');
143
	}
144
145
	/**
146
	 * send server-to-server unshare to remote server
147
	 *
148
	 * @param string $remote url
149
	 * @param int $id share id
150
	 * @param string $token
151
	 * @return bool
152
	 */
153
	public function sendRevokeShare($remote, $id, $token) {
154
		return $this->sendUpdateToRemote($remote, $id, $token, 'revoke');
155
	}
156
157
	/**
158
	 * send notification to remote server if the permissions was changed
159
	 *
160
	 * @param string $remote
161
	 * @param int $remoteId
162
	 * @param string $token
163
	 * @param int $permissions
164
	 * @return bool
165
	 */
166
	public function sendPermissionChange($remote, $remoteId, $token, $permissions) {
167
		return $this->sendUpdateToRemote($remote, $remoteId, $token, 'permissions', ['permissions' => $permissions]);
168
	}
169
170
	/**
171
	 * forward accept reShare to remote server
172
	 *
173
	 * @param string $remote
174
	 * @param int $remoteId
175
	 * @param string $token
176
	 */
177
	public function sendAcceptShare($remote, $remoteId, $token) {
178
		$this->sendUpdateToRemote($remote, $remoteId, $token, 'accept');
179
	}
180
181
	/**
182
	 * forward decline reShare to remote server
183
	 *
184
	 * @param string $remote
185
	 * @param int $remoteId
186
	 * @param string $token
187
	 */
188
	public function sendDeclineShare($remote, $remoteId, $token) {
189
		$this->sendUpdateToRemote($remote, $remoteId, $token, 'decline');
190
	}
191
192
	/**
193
	 * inform remote server whether server-to-server share was accepted/declined
194
	 *
195
	 * @param string $remote
196
	 * @param string $token
197
	 * @param int $remoteId Share id on the remote host
198
	 * @param string $action possible actions: accept, decline, unshare, revoke, permissions
199
	 * @param array $data
200
	 * @param int $try
201
	 * @return boolean
202
	 * @throws \Exception
203
	 */
204
	public function sendUpdateToRemote($remote, $remoteId, $token, $action, $data = [], $try = 0) {
205
		$fields = ['token' => $token];
206
		foreach ($data as $key => $value) {
207
			$fields[$key] = $value;
208
		}
209
210
		$url = $this->addressHandler->removeProtocolFromUrl($remote);
211
		$result = $this->tryHttpPostToShareEndpoint(\rtrim($url, '/'), '/' . $remoteId . '/' . $action, $fields);
212
		$status = \json_decode($result['result'], true);
213
214
		if ($result['success'] &&
215
			($status['ocs']['meta']['statuscode'] === 100 ||
216
				$status['ocs']['meta']['statuscode'] === 200
217
			)
218
		) {
219
			return true;
220
		} elseif ($try === 0) {
221
			// only add new job on first try
222
			$this->jobList->add('OCA\FederatedFileSharing\BackgroundJob\RetryJob',
223
				[
224
					'remote' => $remote,
225
					'remoteId' => $remoteId,
226
					'token' => $token,
227
					'action' => $action,
228
					'data' => \json_encode($data),
229
					'try' => $try,
230
					'lastRun' => $this->getTimestamp()
231
				]
232
			);
233
		}
234
235
		return false;
236
	}
237
238
	/**
239
	 * return current timestamp
240
	 *
241
	 * @return int
242
	 */
243
	protected function getTimestamp() {
244
		return \time();
245
	}
246
247
	/**
248
	 * try http post first with https and then with http as a fallback
249
	 *
250
	 * @param string $remoteDomain
251
	 * @param string $urlSuffix
252
	 * @param array $fields post parameters
253
	 * @return array
254
	 * @throws \Exception
255
	 */
256
	protected function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields, $useOcm = false) {
257
		$client = $this->httpClientService->newClient();
258
		$protocol = 'https://';
259
		$result = [
260
			'success' => false,
261
			'result' => '',
262
		];
263
		$try = 0;
264
265
		while ($result['success'] === false && $try < 2) {
266
			if ($useOcm) {
267
				$endpoint = $this->discoveryManager->getOcmShareEndpoint($protocol . $remoteDomain);
268
			} else {
269
				$relativePath = $this->discoveryManager->getShareEndpoint($protocol . $remoteDomain);
270
				$endpoint = $protocol . $remoteDomain . $relativePath . $urlSuffix . '?format=' . self::RESPONSE_FORMAT;
271
			}
272
273
			try {
274
				$response = $client->post($endpoint, [
275
					'body' => $fields,
276
					'timeout' => 10,
277
					'connect_timeout' => 10,
278
				]);
279
				$result['result'] = $response->getBody();
280
				$result['statusCode'] = $response->getStatusCode();
281
				$result['success'] = true;
282
				break;
283
			} catch (\Exception $e) {
284
				// if flat re-sharing is not supported by the remote server
285
				// we re-throw the exception and fall back to the old behaviour.
286
				// (flat re-shares has been introduced in ownCloud 9.1)
287
				if ($e->getCode() === Http::STATUS_INTERNAL_SERVER_ERROR) {
288
					throw $e;
289
				}
290
				$allowHttpFallback = $this->config->getSystemValue('sharing.federation.allowHttpFallback', false) === true;
291
				if (!$allowHttpFallback) {
292
					break;
293
				}
294
				$try++;
295
				$protocol = 'http://';
296
			}
297
		}
298
299
		return $result;
300
	}
301
302
	protected function sendOcmRemoteShare($token, $shareWith, $name, $remote_id, $owner, $ownerFederatedId, $sharedBy, $sharedByFederatedId) {
0 ignored issues
show
Unused Code introduced by
The parameter $owner is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $sharedBy is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
303
		list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith);
304
		if ($user && $remote) {
305
			$fields = [
306
				'shareWith' => $shareWith,
307
				'name' => $name,
308
				'providerId' => $remote_id,
309
				'owner' => $ownerFederatedId,
310
				'ownerDisplayName' => '',
311
				'sender' => $sharedByFederatedId,
312
				'senderDisplayName' => '',
313
				'token' => $token,
314
				'shareType' => 'user',
315
				'resourceType' => 'file',
316
				'protocol' => [
317
					'name' => 'webdav',
318
					'options' => [
319
						'sharedSecret' => $token
320
					]
321
				]
322
			];
323
324
			$url = $this->addressHandler->removeProtocolFromUrl($remote);
325
			$result = $this->tryHttpPostToShareEndpoint($url, '', $fields, true);
326
327
			if (isset($result['statusCode']) && $result['statusCode'] === 201) {
328
				\OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $remote]);
329
				return true;
330
			}
331
		}
332
		return false;
333
	}
334
335
	protected function sendPreOcmRemoteShare($token, $shareWith, $name, $remote_id, $owner, $ownerFederatedId, $sharedBy, $sharedByFederatedId) {
336
		list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith);
337
338
		if ($user && $remote) {
339
			$url = $remote;
340
			$local = $this->addressHandler->generateRemoteURL();
341
342
			$fields = [
343
				'shareWith' => $user,
344
				'token' => $token,
345
				'name' => $name,
346
				'remoteId' => $remote_id,
347
				'owner' => $owner,
348
				'ownerFederatedId' => $ownerFederatedId,
349
				'sharedBy' => $sharedBy,
350
				'sharedByFederatedId' => $sharedByFederatedId,
351
				'remote' => $local,
352
			];
353
354
			$url = $this->addressHandler->removeProtocolFromUrl($url);
355
			$result = $this->tryHttpPostToShareEndpoint($url, '', $fields);
356
			$status = \json_decode($result['result'], true);
357
358
			if ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200)) {
359
				\OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $remote]);
360
				return true;
361
			}
362
		}
363
		return false;
364
	}
365
}
366