Completed
Pull Request — master (#551)
by Maxence
01:55
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\IRemoteEventBypassLocalCircleCheck;
52
use OCA\Circles\Model\Circle;
53
use OCA\Circles\Model\GlobalScale\GSWrapper;
54
use OCA\Circles\Model\Remote\RemoteEvent;
55
use OCA\Circles\Model\Remote\RemoteInstance;
56
use OCA\Circles\Model\Remote\RemoteWrapper;
57
use OCP\IURLGenerator;
58
use OCP\IUserManager;
59
use OCP\IUserSession;
60
use ReflectionClass;
61
use ReflectionException;
62
63
64
/**
65
 * Class RemoteService
66
 *
67
 * @package OCA\Circles\Service
68
 */
69
class RemoteEventService extends NC21Signature {
70
71
72
	use TNC21Request;
73
	use TStringTools;
74
75
76
	/** @var IURLGenerator */
77
	private $urlGenerator;
78
79
	/** @var IUserManager */
80
	private $userManager;
81
82
	/** @var IUserSession */
83
	private $userSession;
84
85
	/** @var Signer */
86
	private $signer;
87
88
	/** @var RemoteWrapperRequest */
89
	private $remoteWrapperRequest;
90
91
	/** @var RemoteRequest */
92
	private $remoteRequest;
93
94
	/** @var CircleRequest */
95
	private $circleRequest;
96
97
98
	/** @var ConfigService */
99
	private $configService;
100
101
	/** @var MiscService */
102
	private $miscService;
103
104
105
	/**
106
	 * GlobalScaleService constructor.
107
	 *
108
	 * @param IURLGenerator $urlGenerator
109
	 * @param IUserManager $userManager
110
	 * @param IUserSession $userSession
111
	 * @param Signer $signer
112
	 * @param RemoteWrapperRequest $remoteWrapperRequest
113
	 * @param RemoteRequest $remoteRequest
114
	 * @param CircleRequest $circleRequest
115
	 * @param ConfigService $configService
116
	 * @param MiscService $miscService
117
	 */
118
	public function __construct(
119
		IURLGenerator $urlGenerator,
120
		IUserManager $userManager,
121
		IUserSession $userSession,
122
		Signer $signer,
123
		RemoteWrapperRequest $remoteWrapperRequest,
124
		RemoteRequest $remoteRequest,
125
		CircleRequest $circleRequest,
126
		ConfigService $configService,
127
		MiscService $miscService
128
	) {
129
		$this->urlGenerator = $urlGenerator;
130
		$this->userManager = $userManager;
131
		$this->userSession = $userSession;
132
		$this->signer = $signer;
133
		$this->remoteWrapperRequest = $remoteWrapperRequest;
134
		$this->remoteRequest = $remoteRequest;
135
		$this->circleRequest = $circleRequest;
136
		$this->configService = $configService;
137
		$this->miscService = $miscService;
138
	}
139
140
141
	/**
142
	 * Called when creating a new Event.
143
	 * This method will manage the event locally and upstream the payload if needed.
144
	 *
145
	 * @param RemoteEvent $event
146
	 *
147
	 * @throws RemoteEventException
148
	 * @throws OwnerNotFoundException
149
	 */
150
	public function newEvent(RemoteEvent $event): void {
151
		$event->setSource($this->configService->getLocalInstance());
152
		if (!$event->hasCircle()) {
153
			throw new RemoteEventException('Event does not contains Circle');
154
		}
155
156
		$this->verifyViewer($event);
157
		try {
158
			$gs = $this->getRemoteEvent($event);
159
		} catch (RemoteEventException $e) {
160
			$this->e($e);
161
			throw $e;
162
		}
163
164
		try {
165
			if ($this->isLocalEvent($event)) {
166
				$gs->verify($event);
167
				if (!$event->isAsync()) {
168
					$gs->manage($event);
169
				}
170
171
				$this->initBroadcast($event);
172
			} else {
173
				//	$this->confirmEvent($event);
174
			}
175
		} catch (CircleNotFoundException $e) {
176
			$this->e($e, ['event' => $event]);
177
		}
178
179
	}
180
181
182
	/**
183
	 * @param RemoteEvent $event
184
	 */
185
	private function verifyViewer(RemoteEvent $event): void {
186
		if (!$event->getCircle()->hasViewer()) {
187
			return;
188
		}
189
190
		$circle = $event->getCircle();
191
		$viewer = $circle->getViewer();
192
193
		if (!$this->configService->isLocalInstance($viewer->getInstance())) {
194
			return;
195
		}
196
197
		try {
198
			$localCircle = $this->circleRequest->getCircle($circle->getId(), $viewer);
199
		} catch (CircleNotFoundException $e) {
200
			return;
201
		}
202
203
		if (!$circle->compareWith($localCircle) || !$viewer->compareWith($localCircle->getViewer())) {
204
			return;
205
		}
206
207
		$event->setVerifiedViewer(true)
208
			  ->setVerifiedCircle(true);
209
	}
210
211
212
	/**
213
	 * We check that the event can be managed/checked locally or if the owner of the circle belongs to
214
	 * an other instance of Nextcloud
215
	 *
216
	 * @param RemoteEvent $event
217
	 *
218
	 * @return bool
219
	 * @throws CircleNotFoundException
220
	 * @throws OwnerNotFoundException
221
	 */
222
	public function isLocalEvent(RemoteEvent $event): bool {
223
		if ($event->isLocal()) {
224
			return true;
225
		}
226
227
		$circle = $event->getCircle();
228
		if (!$circle->hasOwner()) {
229
			return ($this->configService->isLocalInstance($circle->getInstance()));
230
		}
231
232
		if ($event->isVerifiedCircle()) {
233
			$localCircle = $event->getCircle();
234
		} else {
235
			$localCircle = $this->circleRequest->getCircle($circle->getId());
236
		}
237
238
		$owner = $localCircle->getOwner();
239
		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...
240
			|| $this->configService->isLocalInstance($owner->getInstance())) {
241
			return true;
242
		}
243
244
		return false;
245
	}
246
247
248
	/**
249
	 * // TODO Rename Model/RemoteEvent and/or IRemoteEvent
250
	 *
251
	 * @param RemoteEvent $event
252
	 *
253
	 * @return IRemoteEvent
254
	 * @throws RemoteEventException
255
	 */
256
	public function getRemoteEvent(RemoteEvent $event): IRemoteEvent {
257
		$class = $event->getClass();
258
		try {
259
			$test = new ReflectionClass($class);
260
		} catch (ReflectionException $e) {
261
			throw new RemoteEventException('ReflectionException with ' . $class . ': ' . $e->getMessage());
262
		}
263
264
		if (!in_array(IRemoteEvent::class, $test->getInterfaceNames())) {
265
			throw new RemoteEventException($class . ' does not implements IRemoteEvent');
266
		}
267
268
		$gs = OC::$server->get($class);
269
		if (!$gs instanceof IRemoteEvent) {
270
			throw new RemoteEventException($class . ' not an IRemoteEvent');
271
		}
272
273
		$this->setRemoteEventBypass($event, $gs);
274
275
		return $gs;
276
	}
277
278
279
	/**
280
	 * Some event might need to bypass some checks
281
	 *
282
	 * @param RemoteEvent $event
283
	 * @param IRemoteEvent $gs
284
	 */
285
	private function setRemoteEventBypass(RemoteEvent $event, IRemoteEvent $gs) {
286
		if ($gs instanceof IRemoteEventBypassLocalCircleCheck) {
287
			$event->bypass(RemoteEvent::BYPASS_LOCALCIRCLECHECK);
288
		}
289
	}
290
291
292
	/**
293
	 * async the process, generate a local request that will be closed.
294
	 *
295
	 * @param RemoteEvent $event
296
	 */
297
	public function initBroadcast(RemoteEvent $event): void {
298
		$instances = $this->getInstances($event->isAsync());
299
		if (empty($instances)) {
300
			return;
301
		}
302
303
		$wrapper = new RemoteWrapper();
304
		$wrapper->setEvent($event);
305
		$wrapper->setToken($this->uuid());
306
		$wrapper->setCreation(time());
307
		$wrapper->setSeverity($event->getSeverity());
308
309
		foreach ($instances as $instance) {
310
			$wrapper->setInstance($instance);
311
			$this->remoteWrapperRequest->create($wrapper);
312
		}
313
314
		$request = new NC21Request('', Request::TYPE_POST);
315
		$this->configService->configureRequest(
316
			$request, 'circles.RemoteWrapper.asyncBroadcast', ['token' => $wrapper->getToken()]
317
		);
318
319
		$event->setWrapperToken($wrapper->getToken());
320
321
		try {
322
			$this->doRequest($request);
323
		} catch (RequestNetworkException $e) {
324
			$this->e($e, ['wrapper' => $wrapper]);
325
		}
326
	}
327
328
329
	/**
330
	 * @param bool $all
331
	 * @param Circle|null $circle
332
	 *
333
	 * @return array
334
	 */
335
	public function getInstances(bool $all = false, ?Circle $circle = null): array {
336
		$local = $this->configService->getLocalInstance();
337
		$instances = $this->remoteRequest->getOutgoingRecipient($circle);
338
		$instances = array_merge(
339
			[$local], array_map(
340
						function(RemoteInstance $instance): string {
341
							return $instance->getInstance();
342
						}, $instances
343
					)
344
		);
345
346
		if ($all) {
347
			return $instances;
348
		}
349
350
		return array_values(
351
			array_diff($instances, array_merge($this->configService->getTrustedDomains(), [$local]))
352
		);
353
	}
354
355
356
	/**
357
	 * @param array $current
358
	 */
359
	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...
360
//		$known = $this->remoteRequest->getFromType(RemoteInstance::TYPE_GLOBAL_SCALE);
361
	}
362
363
	/**
364
	 * @return array
365
	 */
366
	private function getRemoteInstances(): array {
367
		return [];
368
	}
369
370
371
	/**
372
	 * should be used to manage results from events, like sending mails on user creation
373
	 *
374
	 * @param string $token
375
	 */
376
	public function manageResults(string $token): void {
377
		try {
378
			$wrappers = $this->remoteWrapperRequest->getByToken($token);
379
		} catch (JsonException | ModelException $e) {
380
			return;
381
		}
382
383
		$event = null;
384
		$events = [];
385
		foreach ($wrappers as $wrapper) {
386
			if ($wrapper->getStatus() !== GSWrapper::STATUS_DONE) {
387
				return;
388
			}
389
390
			$events[$wrapper->getInstance()] = $event = $wrapper->getEvent();
391
		}
392
393
		if ($event === null) {
394
			return;
395
		}
396
397
		try {
398
			$gs = $this->getRemoteEvent($event);
399
			$gs->result($events);
400
		} catch (RemoteEventException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
401
		}
402
	}
403
404
405
	/**
406
	 * @param RemoteEvent $event
407
	 */
408
	private function confirmEvent(RemoteEvent $event): void {
409
//		$this->signEvent($event);
410
411
		$circle = $event->getCircle();
412
		$owner = $circle->getOwner();
413
		$path = $this->urlGenerator->linkToRoute('circles.RemoteWrapper.event');
414
415
		$request = new NC21Request($path, Request::TYPE_POST);
416
		$this->configService->configureRequest($request);
417
		$request->basedOnUrl($owner->getInstance());
418
419
		$request->setDataSerialize($event);
420
421
		$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...
422
		$this->debug('confirming RemoteEvent', ['event' => $event, 'request' => $request]);
423
//
424
//		if ($this->getInt('status', $result) === 0) {
425
//			throw new GlobalScaleEventException($this->get('error', $result));
426
//		}
427
428
//		$updatedData = $this->getArray('event', $result);
429
//		$this->miscService->log('updatedEvent: ' . json_encode($updatedData), 0);
430
//		if (!empty($updatedData)) {
431
//			$updated = new GSEvent();
432
//			try {
433
//				$updated->import($updatedData);
434
//				$event = $updated;
435
//			} catch (Exception $e) {
436
//			}
437
//		}
438
	}
439
440
}
441
442