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