Completed
Pull Request — master (#551)
by Maxence
03:11
created

FederatedUserService::updateMemberships()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 30
rs 9.44
c 0
b 0
f 0
cc 3
nc 4
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\Exceptions\InvalidItemException;
36
use daita\MySmallPhpTools\Exceptions\RequestNetworkException;
37
use daita\MySmallPhpTools\Exceptions\SignatoryException;
38
use daita\MySmallPhpTools\Traits\Nextcloud\nc21\TNC21Logger;
39
use daita\MySmallPhpTools\Traits\TArrayTools;
40
use daita\MySmallPhpTools\Traits\TStringTools;
41
use OC\User\NoUserException;
42
use OCA\Circles\Db\CircleRequest;
43
use OCA\Circles\Db\MemberRequest;
44
use OCA\Circles\Db\MembershipRequest;
45
use OCA\Circles\Exceptions\CircleNotFoundException;
46
use OCA\Circles\Exceptions\FederatedUserException;
47
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
48
use OCA\Circles\Exceptions\InitiatorNotFoundException;
49
use OCA\Circles\Exceptions\InvalidIdException;
50
use OCA\Circles\Exceptions\OwnerNotFoundException;
51
use OCA\Circles\Exceptions\RemoteInstanceException;
52
use OCA\Circles\Exceptions\RemoteNotFoundException;
53
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
54
use OCA\Circles\Exceptions\UnknownRemoteException;
55
use OCA\Circles\Exceptions\UserTypeNotFoundException;
56
use OCA\Circles\IFederatedUser;
57
use OCA\Circles\Model\Circle;
58
use OCA\Circles\Model\Federated\RemoteInstance;
59
use OCA\Circles\Model\FederatedUser;
60
use OCA\Circles\Model\ManagedModel;
61
use OCA\Circles\Model\Member;
62
use OCA\Circles\Model\Membership;
63
use OCP\IUserManager;
64
65
66
/**
67
 * Class FederatedUserService
68
 *
69
 * @package OCA\Circles\Service
70
 */
71
class FederatedUserService {
72
73
74
	use TArrayTools;
75
	use TStringTools;
76
	use TNC21Logger;
77
78
79
	/** @var IUserManager */
80
	private $userManager;
81
82
	/** @var MembershipRequest */
83
	private $membershipRequest;
84
85
	/** @var CircleRequest */
86
	private $circleRequest;
87
88
	/** @var MemberRequest */
89
	private $memberRequest;
90
91
	/** @var RemoteService */
92
	private $remoteService;
93
94
	/** @var ConfigService */
95
	private $configService;
96
97
98
	/** @var FederatedUser */
99
	private $currentUser = null;
100
101
	/** @var RemoteInstance */
102
	private $remoteInstance = null;
103
104
	/** @var bool */
105
	private $bypass = false;
106
107
108
	/**
109
	 * FederatedUserService constructor.
110
	 *
111
	 * @param IUserManager $userManager
112
	 * @param MembershipRequest $membershipRequest
113
	 * @param CircleRequest $circleRequest
114
	 * @param MemberRequest $memberRequest
115
	 * @param RemoteService $remoteService
116
	 * @param ConfigService $configService
117
	 */
118
	public function __construct(
119
		IUserManager $userManager, MembershipRequest $membershipRequest, CircleRequest $circleRequest,
120
		MemberRequest $memberRequest, RemoteService $remoteService, ConfigService $configService
121
	) {
122
		$this->userManager = $userManager;
123
		$this->membershipRequest = $membershipRequest;
124
		$this->circleRequest = $circleRequest;
125
		$this->memberRequest = $memberRequest;
126
		$this->remoteService = $remoteService;
127
		$this->configService = $configService;
128
	}
129
130
131
	/**
132
	 * @param string $userId
133
	 *
134
	 * @throws CircleNotFoundException
135
	 * @throws NoUserException
136
	 */
137
	public function setLocalInitiator(string $userId): void {
138
		$this->currentUser = $this->createLocalFederatedUser($userId);
139
	}
140
141
	/**
142
	 * @param IFederatedUser $federatedUser
143
	 *
144
	 * @throws FederatedUserNotFoundException
145
	 * @throws FederatedUserException
146
	 */
147
	public function setCurrentUser(IFederatedUser $federatedUser): void {
148
		if (!($federatedUser instanceof FederatedUser)) {
149
			$tmp = new FederatedUser();
150
			$tmp->importFromIFederatedUser($federatedUser);
151
			$federatedUser = $tmp;
152
		}
153
154
		$this->confirmFederatedUser($federatedUser);
155
156
		$this->currentUser = $federatedUser;
157
	}
158
159
	/**
160
	 * @return FederatedUser|null
161
	 */
162
	public function getCurrentUser(): ?FederatedUser {
163
		return $this->currentUser;
164
	}
165
166
	/**
167
	 * @return bool
168
	 */
169
	public function hasCurrentUser(): bool {
170
		return ($this->currentUser !== null);
171
	}
172
173
	/**
174
	 * @throws InitiatorNotFoundException
175
	 */
176
	public function mustHaveCurrentUser(): void {
177
		if ($this->bypass) {
178
			return;
179
		}
180
		if (!$this->hasCurrentUser()) {
181
			throw new InitiatorNotFoundException();
182
		}
183
	}
184
185
	/**
186
	 * @param bool $bypass
187
	 */
188
	public function bypassCurrentUserCondition(bool $bypass): void {
189
		$this->bypass = $bypass;
190
	}
191
192
193
	/**
194
	 * @param RemoteInstance $remoteInstance
195
	 */
196
	public function setRemoteInstance(RemoteInstance $remoteInstance): void {
197
		$this->remoteInstance = $remoteInstance;
198
	}
199
200
	/**
201
	 * @return RemoteInstance|null
202
	 */
203
	public function getRemoteInstance(): ?RemoteInstance {
204
		return $this->remoteInstance;
205
	}
206
207
208
	/**
209
	 * @param string $userId
210
	 *
211
	 * @return FederatedUser
212
	 * @throws CircleNotFoundException
213
	 * @throws FederatedUserNotFoundException
214
	 * @throws InvalidIdException
215
	 */
216
	public function createLocalFederatedUser(string $userId): FederatedUser {
217
		$user = $this->userManager->get($userId);
218
		if ($user === null) {
219
			throw new FederatedUserNotFoundException('user ' . $userId . ' not found');
220
		}
221
222
		$federatedUser = new FederatedUser();
223
		$federatedUser->set($user->getUID());
224
		$this->fillSingleCircleId($federatedUser);
225
226
		return $federatedUser;
227
	}
228
229
230
	/**
231
	 * @param string $federatedId
232
	 * @param int $userType
233
	 *
234
	 * @return FederatedUser
235
	 * @throws CircleNotFoundException
236
	 * @throws FederatedUserException
237
	 * @throws FederatedUserNotFoundException
238
	 * @throws InvalidIdException
239
	 * @throws InvalidItemException
240
	 * @throws RemoteInstanceException
241
	 * @throws RemoteNotFoundException
242
	 * @throws RemoteResourceNotFoundException
243
	 * @throws RequestNetworkException
244
	 * @throws SignatoryException
245
	 * @throws UnknownRemoteException
246
	 * @throws UserTypeNotFoundException
247
	 */
248
	public function createFederatedUser(string $federatedId, int $userType = Member::TYPE_USER
249
	): FederatedUser {
250
		switch ($userType) {
251
			case Member::TYPE_USER:
252
				return $this->createFederatedUserTypeUser($federatedId);
253
		}
254
255
		throw new UserTypeNotFoundException();
256
	}
257
258
	/**
259
	 * @param string $userId
260
	 *
261
	 * @return FederatedUser
262
	 * @throws CircleNotFoundException
263
	 * @throws FederatedUserNotFoundException
264
	 * @throws InvalidIdException
265
	 * @throws RemoteInstanceException
266
	 * @throws RemoteNotFoundException
267
	 * @throws RemoteResourceNotFoundException
268
	 * @throws UnknownRemoteException
269
	 * @throws InvalidItemException
270
	 * @throws RequestNetworkException
271
	 * @throws SignatoryException
272
	 * @throws FederatedUserException
273
	 */
274
	public function createFederatedUserTypeUser(string $userId): FederatedUser {
275
		$userId = trim($userId, '@');
276
		if (strpos($userId, '@') === false) {
277
			$instance = $this->configService->getLocalInstance();
278
		} else {
279
			list($userId, $instance) = explode('@', $userId);
280
		}
281
282
		if ($this->configService->isLocalInstance($instance)) {
283
			return $this->createLocalFederatedUser($userId);
284
		} else {
285
			$federatedUser =
286
				$this->remoteService->getFederatedUserFromInstance($userId, $instance, Member::TYPE_USER);
287
			$this->confirmSingleId($federatedUser);
288
289
			return $federatedUser;
290
		}
291
	}
292
293
294
	/**
295
	 * some ./occ commands allows to add an Initiator
296
	 * TODO: manage non-user type
297
	 *
298
	 * @param string $userId
299
	 * @param string $circleId
300
	 * @param bool $bypass
301
	 *
302
	 * @throws CircleNotFoundException
303
	 * @throws FederatedUserException
304
	 * @throws FederatedUserNotFoundException
305
	 * @throws InvalidIdException
306
	 * @throws InvalidItemException
307
	 * @throws OwnerNotFoundException
308
	 * @throws RemoteInstanceException
309
	 * @throws RemoteNotFoundException
310
	 * @throws RemoteResourceNotFoundException
311
	 * @throws RequestNetworkException
312
	 * @throws SignatoryException
313
	 * @throws UnknownRemoteException
314
	 */
315
	public function commandLineInitiator(string $userId, string $circleId = '', bool $bypass = false): void {
316
		if ($userId !== '') {
317
			$this->setCurrentUser($this->createFederatedUserTypeUser($userId));
318
319
			return;
320
		}
321
322
		if ($circleId !== '') {
323
			$localCircle = $this->circleRequest->getCircle($circleId);
324
			if ($this->configService->isLocalInstance($localCircle->getInstance())) {
325
				// TODO: manage NO_OWNER circles
326
				echo json_encode($localCircle->getOwner());
327
				$this->setCurrentUser($localCircle->getOwner());
328
329
				return;
330
			}
331
		}
332
333
		if (!$bypass) {
334
			throw new CircleNotFoundException(
335
				'This Circle is not managed from this instance, please use --initiator'
336
			);
337
		}
338
339
		$this->bypassCurrentUserCondition($bypass);
340
	}
341
342
343
	/**
344
	 * TODO: Is it needed outside of CirclesList ?
345
	 *
346
	 * @param string $userId
347
	 * @param int $level
348
	 *
349
	 * @return Member
350
	 * @throws CircleNotFoundException
351
	 * @throws NoUserException
352
	 */
353
	public function createFilterMember(string $userId, int $level = Member::LEVEL_MEMBER): Member {
354
		$userId = trim($userId, ',');
355
		if (strpos($userId, ',') !== false) {
356
			list($userId, $level) = explode(',', $userId);
357
		}
358
359
		$federatedUser = $this->createFederatedUserTypeUser($userId);
360
		$member = new Member();
361
		$member->importFromIFederatedUser($federatedUser);
362
		$member->setLevel((int)$level);
363
364
		return $member;
365
	}
366
367
368
	/**
369
	 * @param FederatedUser $federatedUser
370
	 *
371
	 * @throws CircleNotFoundException
372
	 * @throws InvalidIdException
373
	 */
374
	private function fillSingleCircleId(FederatedUser $federatedUser): void {
375
		if ($federatedUser->getSingleId() !== '') {
376
			return;
377
		}
378
379
		$circle = $this->getSingleCircle($federatedUser);
380
		$federatedUser->setSingleId($circle->getId());
381
	}
382
383
384
	/**
385
	 * @param FederatedUser $federatedUser
386
	 *
387
	 * @return Circle
388
	 * @throws CircleNotFoundException
389
	 * @throws InvalidIdException
390
	 */
391
	public function getSingleCircle(FederatedUser $federatedUser): Circle {
392
		try {
393
			return $this->circleRequest->getInitiatorCircle($federatedUser);
394
		} catch (CircleNotFoundException $e) {
395
			$circle = new Circle();
396
			$id = $this->token(ManagedModel::ID_LENGTH);
397
398
			$circle->setName('single:' . $federatedUser->getUserId() . ':' . $id)
399
				   ->setId($id)
400
				   ->setConfig(Circle::CFG_SINGLE);
401
			$this->circleRequest->save($circle);
402
403
			$owner = new Member();
404
			$owner->importFromIFederatedUser($federatedUser);
405
			$owner->setLevel(Member::LEVEL_OWNER)
406
				  ->setCircleId($id)
407
				  ->setId($id)
408
				  ->setCachedName($owner->getUserId())
409
				  ->setStatus('Member');
410
			$this->memberRequest->save($owner);
411
		}
412
413
		return $this->circleRequest->getInitiatorCircle($federatedUser);
414
	}
415
416
417
	/**
418
	 * @param FederatedUser $federatedUser
419
	 *
420
	 * @throws FederatedUserException
421
	 */
422
	public function confirmFederatedUser(FederatedUser $federatedUser): void {
423
		if ($federatedUser->getUserId() === ''
424
			|| $federatedUser->getSingleId() === ''
425
			|| $federatedUser->getUserType() === ''
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $federatedUser->getUserType() (integer) and '' (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
426
			|| $federatedUser->getInstance() === '') {
427
			$this->debug('FederatedUser is not empty', ['federatedUser' => $federatedUser]);
428
			throw new FederatedUserException('FederatedUser is not complete');
429
		}
430
	}
431
432
	/**
433
	 * @param FederatedUser $federatedUser
434
	 *
435
	 * @throws FederatedUserException
436
	 */
437
	public function confirmSingleId(FederatedUser $federatedUser): void {
438
		$members = $this->memberRequest->getMembersBySingleId($federatedUser->getSingleId());
439
440
		foreach ($members as $member) {
441
			if (!$federatedUser->compareWith($member)) {
442
				$this->debug(
443
					'uniqueness of SingleId could not be confirmed',
444
					['federatedUser' => $federatedUser, 'localMember' => $member]
445
				);
446
				throw new FederatedUserException('uniqueness of SingleId could not be confirmed');
447
			}
448
		}
449
	}
450
451
452
	/**
453
	 * @param FederatedUser $federatedUser
454
	 *
455
	 * @return Membership[]
456
	 */
457
	public function generateMemberships(FederatedUser $federatedUser): array {
458
		$circles = $this->circleRequest->getCircles(null, $federatedUser);
459
		$memberships = [];
460
		foreach ($circles as $circle) {
461
			$initiator = $circle->getInitiator();
462
			if (!$initiator->isMember()) {
463
				continue;
464
			}
465
466
			$memberships[] = new Membership(
467
				$initiator->getId(), $circle->getId(), $federatedUser->getSingleId(), $initiator->getLevel()
468
			);
469
470
//			$newUser = new CurrentUser($circle->getId(), Member::TYPE_CIRCLE, '');
471
//			$circles = $this->circleRequest->getCircles(null, $currentUser);
472
		}
473
474
		return $memberships;
475
	}
476
477
478
	/**
479
	 * @param FederatedUser|null $federatedUser
480
	 */
481
	public function updateMemberships(?FederatedUser $federatedUser = null) {
482
		if (is_null($federatedUser)) {
483
			$federatedUser = $this->getCurrentUser();
484
		} else {
485
			$federatedUser->setMemberships($this->membershipRequest->getMemberships($federatedUser));
0 ignored issues
show
Documentation introduced by
$federatedUser is of type object<OCA\Circles\Model\FederatedUser>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
486
		}
487
488
		if (is_null($federatedUser)) {
489
			return;
490
		}
491
492
		$last = $this->generateMemberships($federatedUser);
493
494
		echo 'known: ' . json_encode($federatedUser->getMemberships()) . "\n";
495
		echo 'last: ' . json_encode($last) . "\n";
496
497
//
498
//		$circles = $this->circleRequest->getCircles(null, $viewer);
499
//		foreach ($circles as $circle) {
500
//			$viewer = $circle->getViewer();
501
//			if (!$viewer->isMember()) {
502
//				continue;
503
//			}
504
//
505
//			echo 'new member: ' . json_encode($viewer) . "\n";
506
////			$this->federatedUserService->updateMembership($circle);
507
//		}
508
509
510
	}
511
512
}
513
514