Completed
Push — master ( c57b95...1d3e11 )
by Maxence
30s queued 14s
created

FederatedUserService::getGroupCircle()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

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