Completed
Push — master ( 6a3d7a...383329 )
by Maxence
03:05 queued 10s
created

FederatedUserService   F

Complexity

Total Complexity 76

Size/Duplication

Total Lines 828
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 22

Importance

Changes 0
Metric Value
wmc 76
lcom 1
cbo 22
dl 0
loc 828
rs 2.092
c 0
b 0
f 0

34 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 1
A initCurrentUser() 0 8 2
A setLocalCurrentUser() 0 7 2
A setLocalCurrentUserId() 0 3 1
A setLocalCurrentApp() 0 3 1
A setCurrentUser() 0 11 2
A getCurrentUser() 0 3 1
A hasCurrentUser() 0 3 1
A mustHaveCurrentUser() 0 8 4
A bypassCurrentUserCondition() 0 3 1
A getCurrentApp() 0 3 1
A hasCurrentApp() 0 3 1
A setRemoteInstance() 0 3 1
A getRemoteInstance() 0 3 1
A hasRemoteInstance() 0 3 1
A getLocalFederatedUser() 0 12 2
A getAppInitiator() 0 11 1
A commandLineInitiator() 0 29 5
A getFederatedMember() 0 13 2
B getFederatedUser() 0 25 9
A generateFederatedUser() 0 12 2
A getFederatedUser_SingleId() 0 15 3
A getFederatedUser_User() 0 11 2
A getFederatedUser_Group() 0 10 2
A getFederatedUser_Mail() 0 7 1
A getFederatedUser_Contact() 0 10 1
A extractIdAndInstance() 0 11 2
A fillSingleCircleId() 0 9 2
B getSingleCircle() 0 48 6
A getLocalDisplayName() 0 7 2
A confirmFederatedUser() 0 9 5
A confirmLocalSingleId() 0 13 3
A confirmSingleIdUniqueness() 0 7 2
A getGroupCircle() 0 37 3

How to fix   Complexity   

Complex Class

Complex classes like FederatedUserService 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 FederatedUserService, and based on these observations, apply Extract Interface, too.

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
32
namespace OCA\Circles\Service;
33
34
35
use daita\MySmallPhpTools\Traits\Nextcloud\nc22\TNC22Logger;
36
use daita\MySmallPhpTools\Traits\TArrayTools;
37
use daita\MySmallPhpTools\Traits\TStringTools;
38
use Exception;
39
use OCA\Circles\AppInfo\Application;
40
use OCA\Circles\Db\CircleRequest;
41
use OCA\Circles\Db\MemberRequest;
42
use OCA\Circles\Exceptions\CircleNotFoundException;
43
use OCA\Circles\Exceptions\ContactAddressBookNotFoundException;
44
use OCA\Circles\Exceptions\ContactFormatException;
45
use OCA\Circles\Exceptions\ContactNotFoundException;
46
use OCA\Circles\Exceptions\FederatedEventException;
47
use OCA\Circles\Exceptions\FederatedItemException;
48
use OCA\Circles\Exceptions\FederatedUserException;
49
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
50
use OCA\Circles\Exceptions\GroupNotFoundException;
51
use OCA\Circles\Exceptions\InitiatorNotConfirmedException;
52
use OCA\Circles\Exceptions\InitiatorNotFoundException;
53
use OCA\Circles\Exceptions\InvalidIdException;
54
use OCA\Circles\Exceptions\MemberNotFoundException;
55
use OCA\Circles\Exceptions\OwnerNotFoundException;
56
use OCA\Circles\Exceptions\RemoteInstanceException;
57
use OCA\Circles\Exceptions\RemoteNotFoundException;
58
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
59
use OCA\Circles\Exceptions\RequestBuilderException;
60
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
61
use OCA\Circles\Exceptions\UnknownRemoteException;
62
use OCA\Circles\Exceptions\UserTypeNotFoundException;
63
use OCA\Circles\FederatedItems\CircleCreate;
64
use OCA\Circles\IFederatedUser;
65
use OCA\Circles\Model\Circle;
66
use OCA\Circles\Model\Federated\FederatedEvent;
67
use OCA\Circles\Model\Federated\RemoteInstance;
68
use OCA\Circles\Model\FederatedUser;
69
use OCA\Circles\Model\ManagedModel;
70
use OCA\Circles\Model\Member;
71
use OCP\IGroupManager;
72
use OCP\IUser;
73
use OCP\IUserManager;
74
use OCP\IUserSession;
75
76
77
/**
78
 * Class FederatedUserService
79
 *
80
 * @package OCA\Circles\Service
81
 */
82
class FederatedUserService {
83
84
85
	use TArrayTools;
86
	use TStringTools;
87
	use TNC22Logger;
88
89
90
	/** @var IUserSession */
91
	private $userSession;
92
93
	/** @var IUserManager */
94
	private $userManager;
95
96
	/** @var IGroupManager */
97
	private $groupManager;
98
99
	/** @var FederatedEventService */
100
	private $federatedEventService;
101
102
	/** @var MembershipService */
103
	private $membershipService;
104
105
	/** @var CircleRequest */
106
	private $circleRequest;
107
108
	/** @var MemberRequest */
109
	private $memberRequest;
110
111
	/** @var RemoteService */
112
	private $remoteService;
113
114
	/** @var ContactService */
115
	private $contactService;
116
117
	/** @var InterfaceService */
118
	private $interfaceService;
119
120
	/** @var ConfigService */
121
	private $configService;
122
123
124
	/** @var FederatedUser */
125
	private $currentUser = null;
126
127
	/** @var FederatedUser */
128
	private $currentApp = null;
129
130
	/** @var RemoteInstance */
131
	private $remoteInstance = null;
132
133
	/** @var bool */
134
	private $bypass = false;
135
136
137
	/**
138
	 * FederatedUserService constructor.
139
	 *
140
	 * @param IUserSession $userSession
141
	 * @param IUserManager $userManager
142
	 * @param IGroupManager $groupManager
143
	 * @param FederatedEventService $federatedEventService
144
	 * @param MembershipService $membershipService
145
	 * @param CircleRequest $circleRequest
146
	 * @param MemberRequest $memberRequest
147
	 * @param RemoteService $remoteService
148
	 * @param ContactService $contactService
149
	 * @param ConfigService $configService
150
	 */
151
	public function __construct(
152
		IUserSession $userSession, IUserManager $userManager, IGroupManager $groupManager,
153
		FederatedEventService $federatedEventService, MembershipService $membershipService,
154
		CircleRequest $circleRequest, MemberRequest $memberRequest, RemoteService $remoteService,
155
		ContactService $contactService, InterfaceService $interfaceService, ConfigService $configService
156
	) {
157
		$this->userSession = $userSession;
158
		$this->userManager = $userManager;
159
		$this->groupManager = $groupManager;
160
		$this->federatedEventService = $federatedEventService;
161
		$this->membershipService = $membershipService;
162
		$this->circleRequest = $circleRequest;
163
		$this->memberRequest = $memberRequest;
164
		$this->remoteService = $remoteService;
165
		$this->contactService = $contactService;
166
		$this->interfaceService = $interfaceService;
167
		$this->configService = $configService;
168
	}
169
170
171
	/**
172
	 * @throws FederatedUserException
173
	 * @throws FederatedUserNotFoundException
174
	 * @throws InvalidIdException
175
	 * @throws SingleCircleNotFoundException
176
	 */
177
	public function initCurrentUser() {
178
		$user = $this->userSession->getUser();
179
		if ($user === null) {
180
			return;
181
		}
182
183
		$this->setLocalCurrentUser($user);
184
	}
185
186
187
	/**
188
	 * @param IUser|null $user
189
	 *
190
	 * @throws FederatedUserException
191
	 * @throws FederatedUserNotFoundException
192
	 * @throws InvalidIdException
193
	 * @throws SingleCircleNotFoundException
194
	 * @throws RequestBuilderException
195
	 */
196
	public function setLocalCurrentUser(?IUser $user): void {
197
		if ($user === null) {
198
			return;
199
		}
200
201
		$this->setLocalCurrentUserId($user->getUID());
202
	}
203
204
	/**
205
	 * @param string $userId
206
	 *
207
	 * @throws FederatedUserException
208
	 * @throws FederatedUserNotFoundException
209
	 * @throws InvalidIdException
210
	 * @throws RequestBuilderException
211
	 * @throws SingleCircleNotFoundException
212
	 */
213
	public function setLocalCurrentUserId(string $userId): void {
214
		$this->currentUser = $this->getLocalFederatedUser($userId);
215
	}
216
217
	/**
218
	 * @param string $appId
219
	 * @param int $appNumber
220
	 *
221
	 * @throws FederatedUserException
222
	 * @throws InvalidIdException
223
	 * @throws SingleCircleNotFoundException
224
	 */
225
	public function setLocalCurrentApp(string $appId, int $appNumber): void {
226
		$this->currentApp = $this->getAppInitiator($appId, $appNumber);
227
	}
228
229
230
	/**
231
	 * set a CurrentUser, based on a IFederatedUser.
232
	 * CurrentUser is mainly used to manage rights when requesting the database.
233
	 *
234
	 * @param IFederatedUser $federatedUser
235
	 *
236
	 * @throws FederatedUserException
237
	 */
238
	public function setCurrentUser(IFederatedUser $federatedUser): void {
239
		if (!($federatedUser instanceof FederatedUser)) {
240
			$tmp = new FederatedUser();
241
			$tmp->importFromIFederatedUser($federatedUser);
242
			$federatedUser = $tmp;
243
		}
244
245
		$this->confirmFederatedUser($federatedUser);
246
247
		$this->currentUser = $federatedUser;
248
	}
249
250
	/**
251
	 * @return FederatedUser|null
252
	 */
253
	public function getCurrentUser(): ?FederatedUser {
254
		return $this->currentUser;
255
	}
256
257
	/**
258
	 * @return bool
259
	 */
260
	public function hasCurrentUser(): bool {
261
		return !is_null($this->currentUser);
262
	}
263
264
	/**
265
	 * @throws InitiatorNotFoundException
266
	 */
267
	public function mustHaveCurrentUser(): void {
268
		if ($this->bypass) {
269
			return;
270
		}
271
		if (!$this->hasCurrentUser() && !$this->hasRemoteInstance()) {
272
			throw new InitiatorNotFoundException('Invalid initiator');
273
		}
274
	}
275
276
	/**
277
	 * @param bool $bypass
278
	 */
279
	public function bypassCurrentUserCondition(bool $bypass): void {
280
		$this->bypass = $bypass;
281
	}
282
283
284
	/**
285
	 * @return FederatedUser|null
286
	 */
287
	public function getCurrentApp(): ?FederatedUser {
288
		return $this->currentApp;
289
	}
290
291
	/**
292
	 * @return bool
293
	 */
294
	public function hasCurrentApp(): bool {
295
		return !is_null($this->currentApp);
296
	}
297
298
299
	/**
300
	 * set a RemoteInstance, mostly from a remote request (RemoteController)
301
	 * Used to limit rights in some request in the local database.
302
	 *
303
	 * @param RemoteInstance $remoteInstance
304
	 */
305
	public function setRemoteInstance(RemoteInstance $remoteInstance): void {
306
		$this->remoteInstance = $remoteInstance;
307
	}
308
309
	/**
310
	 * @return RemoteInstance|null
311
	 */
312
	public function getRemoteInstance(): ?RemoteInstance {
313
		return $this->remoteInstance;
314
	}
315
316
	/**
317
	 * @return bool
318
	 */
319
	public function hasRemoteInstance(): bool {
320
		return !is_null($this->remoteInstance);
321
	}
322
323
324
	/**
325
	 * Get the full FederatedUser for a local user.
326
	 * Will generate the SingleId if none exist
327
	 *
328
	 * @param string $userId
329
	 *
330
	 * @return FederatedUser
331
	 * @throws FederatedUserNotFoundException
332
	 * @throws InvalidIdException
333
	 * @throws SingleCircleNotFoundException
334
	 * @throws FederatedUserException
335
	 * @throws RequestBuilderException
336
	 */
337
	public function getLocalFederatedUser(string $userId): FederatedUser {
338
		$user = $this->userManager->get($userId);
339
		if ($user === null) {
340
			throw new FederatedUserNotFoundException('user ' . $userId . ' not found');
341
		}
342
343
		$federatedUser = new FederatedUser();
344
		$federatedUser->set($user->getUID());
345
		$this->fillSingleCircleId($federatedUser);
346
347
		return $federatedUser;
348
	}
349
350
351
	/**
352
	 * Get the full FederatedUser for a local user.
353
	 * Will generate the SingleId if none exist
354
	 *
355
	 * @param string $appId
356
	 * @param int $appNumber
357
	 *
358
	 * @return FederatedUser
359
	 * @throws FederatedUserException
360
	 * @throws InvalidIdException
361
	 * @throws SingleCircleNotFoundException
362
	 * @throws RequestBuilderException
363
	 */
364
	public function getAppInitiator(string $appId, int $appNumber): FederatedUser {
365
		$circle = new Circle();
366
		$circle->setSource($appNumber);
367
368
		$federatedUser = new FederatedUser();
369
		$federatedUser->set($appId, '', Member::TYPE_APP, $circle);
370
371
		$this->fillSingleCircleId($federatedUser);
372
373
		return $federatedUser;
374
	}
375
376
377
	/**
378
	 * some ./occ commands allows to add an Initiator, or force the PoV from the local circles' owner
379
	 *
380
	 * TODO: manage non-user type ?
381
	 *
382
	 * @param string $userId
383
	 * @param string $circleId
384
	 * @param bool $bypass
385
	 *
386
	 * @throws CircleNotFoundException
387
	 * @throws FederatedItemException
388
	 * @throws FederatedUserException
389
	 * @throws FederatedUserNotFoundException
390
	 * @throws InvalidIdException
391
	 * @throws MemberNotFoundException
392
	 * @throws OwnerNotFoundException
393
	 * @throws RemoteInstanceException
394
	 * @throws RemoteNotFoundException
395
	 * @throws RemoteResourceNotFoundException
396
	 * @throws SingleCircleNotFoundException
397
	 * @throws UnknownRemoteException
398
	 * @throws UserTypeNotFoundException
399
	 * @throws RequestBuilderException
400
	 */
401
	public function commandLineInitiator(
402
		string $userId,
403
		int $userType = Member::TYPE_SINGLE,
404
		string $circleId = '',
405
		bool $bypass = false
406
	): void {
407
		if ($userId !== '') {
408
			$this->setCurrentUser($this->getFederatedUser($userId, $userType));
409
410
			return;
411
		}
412
413
		if ($circleId !== '') {
414
			$localCircle = $this->circleRequest->getCircle($circleId, null, null, 0);
415
			if ($this->configService->isLocalInstance($localCircle->getInstance())) {
416
				$this->setCurrentUser($localCircle->getOwner());
417
418
				return;
419
			}
420
		}
421
422
		if (!$bypass) {
423
			throw new CircleNotFoundException(
424
				'This Circle is not managed from this instance, please use --initiator'
425
			);
426
		}
427
428
		$this->bypassCurrentUserCondition($bypass);
429
	}
430
431
432
	/**
433
	 * Works like getFederatedUser, but returns a member.
434
	 * Allow to specify a level: handle@instance,level
435
	 *
436
	 * Used for filters when searching for Circles
437
	 * TODO: Used outside of ./occ circles:manage:list ?
438
	 *
439
	 * @param string $userId
440
	 * @param int $level
441
	 *
442
	 * @return Member
443
	 * @throws CircleNotFoundException
444
	 * @throws FederatedItemException
445
	 * @throws FederatedUserException
446
	 * @throws FederatedUserNotFoundException
447
	 * @throws InvalidIdException
448
	 * @throws MemberNotFoundException
449
	 * @throws OwnerNotFoundException
450
	 * @throws RemoteInstanceException
451
	 * @throws RemoteNotFoundException
452
	 * @throws RemoteResourceNotFoundException
453
	 * @throws SingleCircleNotFoundException
454
	 * @throws UnknownRemoteException
455
	 * @throws UserTypeNotFoundException
456
	 */
457
	public function getFederatedMember(string $userId, int $level = Member::LEVEL_MEMBER): Member {
458
		$userId = trim($userId, ',');
459
		if (strpos($userId, ',') !== false) {
460
			list($userId, $level) = explode(',', $userId);
461
		}
462
463
		$federatedUser = $this->getFederatedUser($userId, Member::TYPE_USER);
464
		$member = new Member();
465
		$member->importFromIFederatedUser($federatedUser);
466
		$member->setLevel((int)$level);
467
468
		return $member;
469
	}
470
471
472
	/**
473
	 * get a valid FederatedUser, based on the federatedId (userId@instance) its the type.
474
	 * If instance is local, get the local valid FederatedUser
475
	 * If instance is not local, get the remote valid FederatedUser
476
	 *
477
	 * @param string $federatedId
478
	 * @param int $type
479
	 *
480
	 * @return FederatedUser
481
	 * @throws CircleNotFoundException
482
	 * @throws FederatedItemException
483
	 * @throws FederatedUserException
484
	 * @throws FederatedUserNotFoundException
485
	 * @throws InvalidIdException
486
	 * @throws MemberNotFoundException
487
	 * @throws OwnerNotFoundException
488
	 * @throws RemoteInstanceException
489
	 * @throws RemoteNotFoundException
490
	 * @throws RemoteResourceNotFoundException
491
	 * @throws SingleCircleNotFoundException
492
	 * @throws UnknownRemoteException
493
	 * @throws UserTypeNotFoundException
494
	 * @throws RequestBuilderException
495
	 */
496
	public function getFederatedUser(string $federatedId, int $type = Member::TYPE_SINGLE): FederatedUser {
497
		if ($type === Member::TYPE_USER) {
498
			try {
499
				return $this->getLocalFederatedUser($federatedId);
500
			} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
501
			}
502
		}
503
504
		list($singleId, $instance) = $this->extractIdAndInstance($federatedId);
505
		switch ($type) {
506
			case Member::TYPE_SINGLE:
507
			case Member::TYPE_CIRCLE:
508
				return $this->getFederatedUser_SingleId($singleId, $instance);
509
			case Member::TYPE_USER:
510
				return $this->getFederatedUser_User($singleId, $instance);
511
			case Member::TYPE_GROUP:
512
				return $this->getFederatedUser_Group($singleId, $instance);
513
			case Member::TYPE_MAIL:
514
				return $this->getFederatedUser_Mail($federatedId);
515
			case Member::TYPE_CONTACT:
516
				return $this->getFederatedUser_Contact($federatedId);
517
		}
518
519
		throw new UserTypeNotFoundException();
520
	}
521
522
523
	/**
524
	 * Generate a FederatedUser based on local data.
525
	 * WARNING: There is no confirmation that the returned FederatedUser exists or is valid at this point.
526
	 * Use getFederatedUser() instead if a valid and confirmed FederatedUser is needed.
527
	 *
528
	 * if $federatedId is a known SingleId, will returns data from the local database.
529
	 * if $federatedId is a local username, will returns data from the local database.
530
	 * Otherwise, the FederatedUser will not contains a SingleId.
531
	 *
532
	 * @param string $federatedId
533
	 * @param int $type
534
	 *
535
	 * @return FederatedUser
536
	 */
537
	public function generateFederatedUser(string $federatedId, int $type = 0): FederatedUser {
538
		try {
539
			return $this->getFederatedUser($federatedId, $type);
540
		} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
541
		}
542
543
		list($userId, $instance) = $this->extractIdAndInstance($federatedId);
544
		$federatedUser = new FederatedUser();
545
		$federatedUser->set($userId, $instance, $type);
546
547
		return $federatedUser;
548
	}
549
550
551
	/**
552
	 * @param string $singleId
553
	 * @param string $instance
554
	 *
555
	 * @return FederatedUser
556
	 * @throws CircleNotFoundException
557
	 * @throws FederatedItemException
558
	 * @throws FederatedUserException
559
	 * @throws FederatedUserNotFoundException
560
	 * @throws MemberNotFoundException
561
	 * @throws OwnerNotFoundException
562
	 * @throws RemoteInstanceException
563
	 * @throws RemoteNotFoundException
564
	 * @throws RemoteResourceNotFoundException
565
	 * @throws UnknownRemoteException
566
	 */
567
	public function getFederatedUser_SingleId(string $singleId, string $instance): FederatedUser {
568
		if (strlen($singleId) !== ManagedModel::ID_LENGTH) {
569
			throw new MemberNotFoundException();
570
		}
571
572
		if ($this->configService->isLocalInstance($instance)) {
573
			return $this->circleRequest->getFederatedUserBySingleId($singleId);
574
		} else {
575
			$federatedUser =
576
				$this->remoteService->getFederatedUserFromInstance($singleId, $instance, Member::TYPE_SINGLE);
577
			$this->confirmLocalSingleId($federatedUser);
578
579
			return $federatedUser;
580
		}
581
	}
582
583
584
	/**
585
	 * @param string $userId
586
	 * @param string $instance
587
	 *
588
	 * @return FederatedUser
589
	 * @throws FederatedUserException
590
	 * @throws FederatedUserNotFoundException
591
	 * @throws InvalidIdException
592
	 * @throws RemoteInstanceException
593
	 * @throws RemoteNotFoundException
594
	 * @throws RemoteResourceNotFoundException
595
	 * @throws SingleCircleNotFoundException
596
	 * @throws UnknownRemoteException
597
	 * @throws FederatedItemException
598
	 */
599
	private function getFederatedUser_User(string $userId, string $instance): FederatedUser {
600
		if ($this->configService->isLocalInstance($instance)) {
601
			return $this->getLocalFederatedUser($userId);
602
		} else {
603
			$federatedUser =
604
				$this->remoteService->getFederatedUserFromInstance($userId, $instance, Member::TYPE_USER);
605
			$this->confirmLocalSingleId($federatedUser);
606
607
			return $federatedUser;
608
		}
609
	}
610
611
612
	/**
613
	 * @param string $groupName
614
	 * @param string $instance
615
	 *
616
	 * @return FederatedUser
617
	 * @throws FederatedEventException
618
	 * @throws FederatedItemException
619
	 * @throws FederatedUserException
620
	 * @throws GroupNotFoundException
621
	 * @throws InitiatorNotConfirmedException
622
	 * @throws InvalidIdException
623
	 * @throws OwnerNotFoundException
624
	 * @throws RemoteInstanceException
625
	 * @throws RemoteNotFoundException
626
	 * @throws RemoteResourceNotFoundException
627
	 * @throws SingleCircleNotFoundException
628
	 * @throws UnknownRemoteException
629
	 */
630
	public function getFederatedUser_Group(string $groupName, string $instance): FederatedUser {
631
		if ($this->configService->isLocalInstance($instance)) {
632
			$circle = $this->getGroupCircle($groupName);
633
			$federatedGroup = new FederatedUser();
634
635
			return $federatedGroup->importFromCircle($circle);
636
		} else {
637
			// TODO: implement remote groups
638
		}
639
	}
640
641
642
	/**
643
	 * @param string $mailAddress
644
	 *
645
	 * @return FederatedUser
646
	 * @throws FederatedUserException
647
	 * @throws InvalidIdException
648
	 * @throws RequestBuilderException
649
	 * @throws SingleCircleNotFoundException
650
	 */
651
	public function getFederatedUser_Mail(string $mailAddress): FederatedUser {
652
		$federatedUser = new FederatedUser();
653
		$federatedUser->set($mailAddress, '', Member::TYPE_MAIL);
654
		$this->fillSingleCircleId($federatedUser);
655
656
		return $federatedUser;
657
	}
658
659
660
	/**
661
	 * @param string $contactPath
662
	 *
663
	 * @return FederatedUser
664
	 * @throws FederatedUserException
665
	 * @throws InvalidIdException
666
	 * @throws RequestBuilderException
667
	 * @throws SingleCircleNotFoundException
668
	 */
669
	public function getFederatedUser_Contact(string $contactPath): FederatedUser {
670
		$federatedUser = new FederatedUser();
671
		$federatedUser->setUserId($contactPath);
672
		$federatedUser->setInstance('');
673
		$federatedUser->setUserType(Member::TYPE_CONTACT);
674
675
		$this->fillSingleCircleId($federatedUser);
676
677
		return $federatedUser;
678
	}
679
680
681
	/**
682
	 * extract userID and instance from a federatedId
683
	 *
684
	 * @param string $federatedId
685
	 *
686
	 * @return array
687
	 */
688
	public function extractIdAndInstance(string $federatedId): array {
689
		$federatedId = trim($federatedId, '@');
690
		if (strrpos($federatedId, '@') === false) {
691
			$userId = $federatedId;
692
			$instance = $this->interfaceService->getLocalInstance();
693
		} else {
694
			list($userId, $instance) = explode('@', $federatedId);
695
		}
696
697
		return [$userId, $instance];
698
	}
699
700
701
	/**
702
	 * @param FederatedUser $federatedUser
703
	 *
704
	 * @throws FederatedUserException
705
	 * @throws InvalidIdException
706
	 * @throws SingleCircleNotFoundException
707
	 * @throws RequestBuilderException
708
	 */
709
	private function fillSingleCircleId(FederatedUser $federatedUser): void {
710
		if ($federatedUser->getSingleId() !== '') {
711
			return;
712
		}
713
714
		$circle = $this->getSingleCircle($federatedUser);
715
		$federatedUser->setSingleId($circle->getSingleId());
716
		$federatedUser->setBasedOn($circle);
717
	}
718
719
720
	/**
721
	 * get the Single Circle from a local user
722
	 *
723
	 * @param FederatedUser $federatedUser
724
	 *
725
	 * @return Circle
726
	 * @throws InvalidIdException
727
	 * @throws FederatedUserException
728
	 * @throws SingleCircleNotFoundException
729
	 * @throws RequestBuilderException
730
	 */
731
	private function getSingleCircle(FederatedUser $federatedUser): Circle {
732
		if (!$this->configService->isLocalInstance($federatedUser->getInstance())) {
733
			throw new FederatedUserException('FederatedUser must be local');
734
		}
735
736
		try {
737
			return $this->circleRequest->getSingleCircle($federatedUser);
738
		} catch (SingleCircleNotFoundException $e) {
739
			$circle = new Circle();
740
			$id = $this->token(ManagedModel::ID_LENGTH);
741
742
			if ($federatedUser->hasBasedOn()) {
743
				$source = $federatedUser->getBasedOn()->getSource();
744
			} else {
745
				$source = $federatedUser->getUserType();
746
			}
747
748
			$prefix = ($federatedUser->getUserType() === Member::TYPE_APP) ? 'app'
749
				: Member::$TYPE[$federatedUser->getUserType()];
750
751
			$circle->setName($prefix . ':' . $federatedUser->getUserId() . ':' . $id)
752
				   ->setDisplayName($this->getLocalDisplayName($federatedUser))
753
				   ->setSingleId($id)
754
				   ->setSource($source);
755
756
			if ($federatedUser->getUserType() === Member::TYPE_APP) {
757
				$circle->setConfig(Circle::CFG_SINGLE | Circle::CFG_ROOT);
758
			} else {
759
				$circle->setConfig(Circle::CFG_SINGLE);
760
			}
761
			$this->circleRequest->save($circle);
762
763
			$owner = new Member();
764
			$owner->importFromIFederatedUser($federatedUser);
765
			$owner->setLevel(Member::LEVEL_OWNER)
766
				  ->setCircleId($id)
767
				  ->setSingleId($id)
768
				  ->setId($id)
769
				  ->setDisplayName($owner->getUserId())
770
				  ->setStatus('Member');
771
772
			$this->memberRequest->save($owner);
773
			// TODO: should not be needed
774
			// $this->membershipService->onUpdate($id);
775
		}
776
777
		return $this->circleRequest->getSingleCircle($federatedUser);
778
	}
779
780
781
	/**
782
	 * @param FederatedUser $federatedUser
783
	 *
784
	 * @return string
785
	 * @throws ContactAddressBookNotFoundException
786
	 * @throws ContactFormatException
787
	 * @throws ContactNotFoundException
788
	 */
789
	private function getLocalDisplayName(FederatedUser $federatedUser): string {
790
		if ($federatedUser->getUserType() === Member::TYPE_CONTACT) {
791
			return $this->contactService->getDisplayName($federatedUser->getUserId());
792
		}
793
794
		return $federatedUser->getUserId();
795
	}
796
797
798
	/**
799
	 * Confirm that all field of a FederatedUser are filled.
800
	 *
801
	 * @param FederatedUser $federatedUser
802
	 *
803
	 * @throws FederatedUserException
804
	 */
805
	private function confirmFederatedUser(FederatedUser $federatedUser): void {
806
		if ($federatedUser->getUserId() === ''
807
			|| $federatedUser->getSingleId() === ''
808
			|| $federatedUser->getUserType() === 0
809
			|| $federatedUser->getInstance() === '') {
810
			$this->debug('FederatedUser is not empty', ['federatedUser' => $federatedUser]);
811
			throw new FederatedUserException('FederatedUser is not complete');
812
		}
813
	}
814
815
	/**
816
	 * Confirm that the singleId of a FederatedUser is unique and not used to any other member of the
817
	 * database.
818
	 *
819
	 * @param FederatedUser $federatedUser
820
	 *
821
	 * @throws FederatedUserException
822
	 */
823
	public function confirmLocalSingleId(IFederatedUser $federatedUser): void {
824
		$members = $this->memberRequest->getMembersBySingleId($federatedUser->getSingleId());
825
826
		foreach ($members as $member) {
827
			if (!$federatedUser->compareWith($member)) {
828
				$this->debug(
829
					'uniqueness of SingleId could not be confirmed',
830
					['federatedUser' => $federatedUser, 'localMember' => $member]
831
				);
832
				throw new FederatedUserException('uniqueness of SingleId could not be confirmed');
833
			}
834
		}
835
	}
836
837
838
	/**
839
	 * @param IFederatedUser $federatedUser
840
	 *
841
	 * @throws FederatedUserException
842
	 * @throws RequestBuilderException
843
	 */
844
	public function confirmSingleIdUniqueness(IFederatedUser $federatedUser): void {
845
		if (empty($this->memberRequest->getAlternateSingleId($federatedUser))) {
846
			return;
847
		}
848
849
		throw new FederatedUserException('uniqueness of SingleId could not be confirmed');
850
	}
851
852
853
	/**
854
	 * @param string $groupId
855
	 *
856
	 * @return Circle
857
	 * @throws GroupNotFoundException
858
	 * @throws FederatedEventException
859
	 * @throws FederatedItemException
860
	 * @throws FederatedUserException
861
	 * @throws InitiatorNotConfirmedException
862
	 * @throws InvalidIdException
863
	 * @throws OwnerNotFoundException
864
	 * @throws RemoteInstanceException
865
	 * @throws RemoteNotFoundException
866
	 * @throws RemoteResourceNotFoundException
867
	 * @throws SingleCircleNotFoundException
868
	 * @throws UnknownRemoteException
869
	 * @throws RequestBuilderException
870
	 */
871
	public function getGroupCircle(string $groupId): Circle {
872
		$group = $this->groupManager->get($groupId);
873
		if ($group === null) {
874
			throw new GroupNotFoundException('group not found');
875
		}
876
877
		$this->setLocalCurrentApp(Application::APP_ID, Member::APP_CIRCLES);
878
		$owner = $this->getCurrentApp();
879
880
		$circle = new Circle();
881
		$circle->setName('group:' . $groupId)
882
			   ->setConfig(Circle::CFG_SYSTEM | Circle::CFG_NO_OWNER | Circle::CFG_HIDDEN)
883
			   ->setSingleId($this->token(ManagedModel::ID_LENGTH))
884
			   ->setSource(Member::TYPE_GROUP);
885
886
		$member = new Member();
887
		$member->importFromIFederatedUser($owner);
0 ignored issues
show
Bug introduced by
It seems like $owner defined by $this->getCurrentApp() on line 878 can be null; however, OCA\Circles\Model\Manage...ortFromIFederatedUser() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
888
		$member->setId($this->token(ManagedModel::ID_LENGTH))
889
			   ->setCircleId($circle->getSingleId())
890
			   ->setLevel(Member::LEVEL_OWNER)
891
			   ->setStatus(Member::STATUS_MEMBER);
892
		$circle->setOwner($member)
893
			   ->setInitiator($member);
894
895
		try {
896
			return $this->circleRequest->searchCircle($circle, $owner);
897
		} catch (CircleNotFoundException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
898
		}
899
900
		$circle->setDisplayName($groupId);
901
902
		$event = new FederatedEvent(CircleCreate::class);
903
		$event->setCircle($circle);
904
		$this->federatedEventService->newEvent($event);
905
906
		return $circle;
907
	}
908
909
}
910
911