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

RemoteEventService::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
rs 9.6333
c 0
b 0
f 0
cc 1
nc 1
nop 8

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