Completed
Pull Request — master (#551)
by Maxence
02:47
created

RemoteEventService::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.584
c 0
b 0
f 0
cc 1
nc 1
nop 9

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
6
/**
7
 * Circles - Bring cloud-users closer together.
8
 *
9
 * This file is licensed under the Affero General Public License version 3 or
10
 * later. See the COPYING file.
11
 *
12
 * @author Maxence Lange <[email protected]>
13
 * @copyright 2021
14
 * @license GNU AGPL version 3 or any later version
15
 *
16
 * This program is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License as
18
 * published by the Free Software Foundation, either version 3 of the
19
 * License, or (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28
 *
29
 */
30
31
namespace OCA\Circles\Service;
32
33
34
use daita\MySmallPhpTools\ActivityPub\Nextcloud\nc21\NC21Signature;
35
use daita\MySmallPhpTools\Exceptions\RequestNetworkException;
36
use daita\MySmallPhpTools\Model\Nextcloud\nc21\NC21Request;
37
use daita\MySmallPhpTools\Model\Request;
38
use daita\MySmallPhpTools\Traits\Nextcloud\nc21\TNC21Request;
39
use daita\MySmallPhpTools\Traits\TStringTools;
40
use OC;
41
use OC\Security\IdentityProof\Signer;
42
use OCA\Circles\Db\CircleRequest;
43
use OCA\Circles\Db\RemoteRequest;
44
use OCA\Circles\Db\RemoteWrapperRequest;
45
use OCA\Circles\Exceptions\CircleNotFoundException;
46
use OCA\Circles\Exceptions\JsonException;
47
use OCA\Circles\Exceptions\ModelException;
48
use OCA\Circles\Exceptions\OwnerNotFoundException;
49
use OCA\Circles\Exceptions\RemoteEventException;
50
use OCA\Circles\IRemoteEvent;
51
use OCA\Circles\Model\Circle;
52
use OCA\Circles\Model\GlobalScale\GSWrapper;
53
use OCA\Circles\Model\Remote\RemoteEvent;
54
use OCA\Circles\Model\Remote\RemoteInstance;
55
use OCA\Circles\Model\Remote\RemoteWrapper;
56
use OCP\IURLGenerator;
57
use OCP\IUserManager;
58
use OCP\IUserSession;
59
use ReflectionClass;
60
use ReflectionException;
61
62
63
/**
64
 * Class RemoteService
65
 *
66
 * @package OCA\Circles\Service
67
 */
68
class RemoteEventService extends NC21Signature {
69
70
71
	use TNC21Request;
72
	use TStringTools;
73
74
75
	/** @var IURLGenerator */
76
	private $urlGenerator;
77
78
	/** @var IUserManager */
79
	private $userManager;
80
81
	/** @var IUserSession */
82
	private $userSession;
83
84
	/** @var Signer */
85
	private $signer;
86
87
	/** @var RemoteWrapperRequest */
88
	private $remoteWrapperRequest;
89
90
	/** @var RemoteRequest */
91
	private $remoteRequest;
92
93
	/** @var CircleRequest */
94
	private $circleRequest;
95
96
97
	/** @var ConfigService */
98
	private $configService;
99
100
	/** @var MiscService */
101
	private $miscService;
102
103
104
	/**
105
	 * GlobalScaleService constructor.
106
	 *
107
	 * @param IURLGenerator $urlGenerator
108
	 * @param IUserManager $userManager
109
	 * @param IUserSession $userSession
110
	 * @param Signer $signer
111
	 * @param RemoteWrapperRequest $remoteWrapperRequest
112
	 * @param RemoteRequest $remoteRequest
113
	 * @param CircleRequest $circleRequest
114
	 * @param ConfigService $configService
115
	 * @param MiscService $miscService
116
	 */
117
	public function __construct(
118
		IURLGenerator $urlGenerator,
119
		IUserManager $userManager,
120
		IUserSession $userSession,
121
		Signer $signer,
122
		RemoteWrapperRequest $remoteWrapperRequest,
123
		RemoteRequest $remoteRequest,
124
		CircleRequest $circleRequest,
125
		ConfigService $configService,
126
		MiscService $miscService
127
	) {
128
		$this->urlGenerator = $urlGenerator;
129
		$this->userManager = $userManager;
130
		$this->userSession = $userSession;
131
		$this->signer = $signer;
132
		$this->remoteWrapperRequest = $remoteWrapperRequest;
133
		$this->remoteRequest = $remoteRequest;
134
		$this->circleRequest = $circleRequest;
135
		$this->configService = $configService;
136
		$this->miscService = $miscService;
137
	}
138
139
140
	/**
141
	 * Called when creating a new Event.
142
	 * This method will manage the event locally and upstream the payload if needed.
143
	 *
144
	 * @param RemoteEvent $event
145
	 *
146
	 * @throws RemoteEventException
147
	 */
148
	public function newEvent(RemoteEvent $event): void {
149
		$event->setSource($this->configService->getLocalInstance());
150
		if (!$event->hasCircle()) {
151
			throw new RemoteEventException('Event does not contains Circle');
152
		}
153
154
		$this->verifyViewer($event);
155
		try {
156
			$gs = $this->getRemoteEvent($event);
157
		} catch (RemoteEventException $e) {
158
			$this->e($e);
159
			throw $e;
160
		}
161
162
		try {
163
			if ($this->isLocalEvent($event)) {
164
				$gs->verify($event);
165
				if (!$event->isAsync()) {
166
					$gs->manage($event);
167
				}
168
169
				$this->initBroadcast($event);
170
			} else {
171
				//	$this->confirmEvent($event);
172
			}
173
		} catch (CircleNotFoundException $e) {
174
			$this->e($e, ['event' => $event]);
175
		}
176
177
	}
178
179
180
	/**
181
	 * @param RemoteEvent $event
182
	 */
183
	private function verifyViewer(RemoteEvent $event): void {
184
		if (!$event->getCircle()->hasViewer()) {
185
			return;
186
		}
187
188
		$circle = $event->getCircle();
189
		$viewer = $circle->getViewer();
190
191
		// TODO: Verify Origin of Viewer (check based on the source of the request)
192
//		if ($event->isLocal()) {
193
//		}
194
195
		try {
196
			$localCircle = $this->circleRequest->getCircle($circle->getId(), $viewer);
197
		} catch (CircleNotFoundException $e) {
198
			return;
199
		}
200
201
		if (!$circle->compareWith($localCircle) || !$viewer->compareWith($localCircle->getViewer())) {
202
			return;
203
		}
204
205
		$event->setVerifiedViewer(true)
206
			  ->setVerifiedCircle(true);
207
	}
208
209
210
	/**
211
	 * We check that the event can be managed/checked locally or if the owner of the circle belongs to
212
	 * an other instance of Nextcloud
213
	 *
214
	 * @param RemoteEvent $event
215
	 *
216
	 * @return bool
217
	 * @throws CircleNotFoundException
218
	 * @throws OwnerNotFoundException
219
	 */
220
	public function isLocalEvent(RemoteEvent $event): bool {
221
		if ($event->isLocal()) {
222
			return true;
223
		}
224
225
		$circle = $event->getCircle();
226
		if (!$circle->hasOwner()) {
227
			return ($this->configService->isLocalInstance($circle->getInstance()));
228
		}
229
230
		if ($event->isVerifiedCircle()) {
231
			$localCircle = $event->getCircle();
232
		} else {
233
			$localCircle = $this->circleRequest->getCircle($circle->getId());
234
		}
235
236
		$owner = $localCircle->getOwner();
237
		if ($owner->getInstance() === ''
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return $owner->getInstan...$owner->getInstance());.
Loading history...
238
			|| $this->configService->isLocalInstance($owner->getInstance())) {
239
			return true;
240
		}
241
242
		return false;
243
	}
244
245
246
	/**
247
	 * @param RemoteEvent $event
248
	 *
249
	 * @return IRemoteEvent
250
	 * @throws RemoteEventException
251
	 */
252
	public function getRemoteEvent(RemoteEvent $event): IRemoteEvent {
253
		$class = $event->getClass();
254
		try {
255
			$test = new ReflectionClass($class);
256
		} catch (ReflectionException $e) {
257
			throw new RemoteEventException('ReflectionException with ' . $class . ': ' . $e->getMessage());
258
		}
259
260
		if (!in_array(IRemoteEvent::class, $test->getInterfaceNames())) {
261
			throw new RemoteEventException($class . ' does not implements IRemoteEvent');
262
		}
263
264
		$gs = OC::$server->get($class);
265
		if (!$gs instanceof IRemoteEvent) {
266
			throw new RemoteEventException($class . ' not an IRemoteEvent');
267
		}
268
269
		return $gs;
270
	}
271
272
273
	/**
274
	 * async the process, generate a local request that will be closed.
275
	 *
276
	 * @param RemoteEvent $event
277
	 */
278
	public function initBroadcast(RemoteEvent $event): void {
279
		$instances = $this->getInstances($event->isAsync());
280
		if (empty($instances)) {
281
			return;
282
		}
283
284
		$wrapper = new RemoteWrapper();
285
		$wrapper->setEvent($event);
286
		$wrapper->setToken($this->uuid());
287
		$wrapper->setCreation(time());
288
		$wrapper->setSeverity($event->getSeverity());
289
290
		foreach ($instances as $instance) {
291
			$wrapper->setInstance($instance);
292
			$this->remoteWrapperRequest->create($wrapper);
293
		}
294
295
		$request = new NC21Request('', Request::TYPE_POST);
296
		$this->configService->configureRequest(
297
			$request, 'circles.RemoteWrapper.asyncBroadcast', ['token' => $wrapper->getToken()]
298
		);
299
300
		$event->setWrapperToken($wrapper->getToken());
301
302
		try {
303
			$this->doRequest($request);
304
		} catch (RequestNetworkException $e) {
305
			$this->e($e, ['wrapper' => $wrapper]);
306
		}
307
	}
308
309
310
	/**
311
	 * @param bool $all
312
	 * @param Circle|null $circle
313
	 *
314
	 * @return array
315
	 */
316
	public function getInstances(bool $all = false, ?Circle $circle = null): array {
317
		$local = $this->configService->getLocalInstance();
318
		$instances = $this->remoteRequest->getOutgoingRecipient($circle);
319
		$instances = array_merge(
320
			[$local], array_map(
321
						function(RemoteInstance $instance): string {
322
							return $instance->getInstance();
323
						}, $instances
324
					)
325
		);
326
327
		if ($all) {
328
			return $instances;
329
		}
330
331
		return array_values(
332
			array_diff($instances, array_merge($this->configService->getTrustedDomains(), [$local]))
333
		);
334
	}
335
336
337
	/**
338
	 * @param array $current
339
	 */
340
	private function updateGlobalScaleInstances(array $current): void {
0 ignored issues
show
Unused Code introduced by
The parameter $current 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...
341
//		$known = $this->remoteRequest->getFromType(RemoteInstance::TYPE_GLOBAL_SCALE);
342
	}
343
344
	/**
345
	 * @return array
346
	 */
347
	private function getRemoteInstances(): array {
348
		return [];
349
	}
350
351
352
	/**
353
	 * should be used to manage results from events, like sending mails on user creation
354
	 *
355
	 * @param string $token
356
	 */
357
	public function manageResults(string $token): void {
358
		try {
359
			$wrappers = $this->remoteWrapperRequest->getByToken($token);
360
		} catch (JsonException | ModelException $e) {
361
			return;
362
		}
363
364
		$event = null;
365
		$events = [];
366
		foreach ($wrappers as $wrapper) {
367
			if ($wrapper->getStatus() !== GSWrapper::STATUS_DONE) {
368
				return;
369
			}
370
371
			$events[$wrapper->getInstance()] = $event = $wrapper->getEvent();
372
		}
373
374
		if ($event === null) {
375
			return;
376
		}
377
378
		try {
379
			$gs = $this->getRemoteEvent($event);
380
			$gs->result($events);
381
		} catch (RemoteEventException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
382
		}
383
	}
384
385
386
	/**
387
	 * @param RemoteEvent $event
388
	 */
389
	private function confirmEvent(RemoteEvent $event): void {
390
//		$this->signEvent($event);
391
392
		$circle = $event->getCircle();
393
		$owner = $circle->getOwner();
394
		$path = $this->urlGenerator->linkToRoute('circles.RemoteWrapper.event');
395
396
		$request = new NC21Request($path, Request::TYPE_POST);
397
		$this->configService->configureRequest($request);
398
		$request->basedOnUrl($owner->getInstance());
399
400
		$request->setDataSerialize($event);
401
402
		$result = $this->retrieveJson($request);
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
403
		$this->debug('confirming RemoteEvent', ['event' => $event, 'request' => $request]);
404
//
405
//		if ($this->getInt('status', $result) === 0) {
406
//			throw new GlobalScaleEventException($this->get('error', $result));
407
//		}
408
409
//		$updatedData = $this->getArray('event', $result);
410
//		$this->miscService->log('updatedEvent: ' . json_encode($updatedData), 0);
411
//		if (!empty($updatedData)) {
412
//			$updated = new GSEvent();
413
//			try {
414
//				$updated->import($updatedData);
415
//				$event = $updated;
416
//			} catch (Exception $e) {
417
//			}
418
//		}
419
	}
420
421
}
422
423