Completed
Push — master ( 2493d7...51beeb )
by Maxence
03:12 queued 12s
created

SyncService::sync()   B

Complexity

Conditions 7
Paths 64

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 8.5866
c 0
b 0
f 0
cc 7
nc 64
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 ArtificialOwl\MySmallPhpTools\Traits\Nextcloud\nc22\TNC22Logger;
36
use ArtificialOwl\MySmallPhpTools\Traits\TStringTools;
37
use Exception;
38
use OCA\Circles\AppInfo\Application;
39
use OCA\Circles\Db\CircleRequest;
40
use OCA\Circles\Db\MemberRequest;
41
use OCA\Circles\Exceptions\CircleNotFoundException;
42
use OCA\Circles\Exceptions\ContactAddressBookNotFoundException;
43
use OCA\Circles\Exceptions\ContactFormatException;
44
use OCA\Circles\Exceptions\ContactNotFoundException;
45
use OCA\Circles\Exceptions\FederatedEventException;
46
use OCA\Circles\Exceptions\FederatedItemException;
47
use OCA\Circles\Exceptions\FederatedUserException;
48
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
49
use OCA\Circles\Exceptions\GroupNotFoundException;
50
use OCA\Circles\Exceptions\InitiatorNotConfirmedException;
51
use OCA\Circles\Exceptions\InvalidIdException;
52
use OCA\Circles\Exceptions\OwnerNotFoundException;
53
use OCA\Circles\Exceptions\RemoteInstanceException;
54
use OCA\Circles\Exceptions\RemoteNotFoundException;
55
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
56
use OCA\Circles\Exceptions\RequestBuilderException;
57
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
58
use OCA\Circles\Exceptions\UnknownRemoteException;
59
use OCA\Circles\FederatedItems\SingleMemberAdd;
60
use OCA\Circles\Model\Circle;
61
use OCA\Circles\Model\Federated\FederatedEvent;
62
use OCA\Circles\Model\FederatedUser;
63
use OCA\Circles\Model\ManagedModel;
64
use OCA\Circles\Model\Member;
65
use OCP\IGroupManager;
66
use OCP\IUserManager;
67
68
69
/**
70
 * Class SyncService
71
 *
72
 * @package OCA\Circles\Service
73
 */
74
class SyncService {
75
76
77
	use TStringTools;
78
	use TNC22Logger;
79
80
81
	const SYNC_APPS = 1;
82
	const SYNC_USERS = 2;
83
	const SYNC_GROUPS = 4;
84
	const SYNC_GLOBALSCALE = 8;
85
	const SYNC_REMOTES = 16;
86
	const SYNC_CONTACTS = 32;
87
	const SYNC_ALL = 63;
88
89
90
	/** @var IUserManager */
91
	private $userManager;
92
93
	/** @var IGroupManager */
94
	private $groupManager;
95
96
97
	/** @var CircleRequest */
98
	private $circleRequest;
99
100
	/** @var MemberRequest */
101
	private $memberRequest;
102
103
	/** @var FederatedUserService */
104
	private $federatedUserService;
105
106
	/** @var federatedEventService */
107
	private $federatedEventService;
108
109
	/** @var CircleService */
110
	private $circleService;
111
112
	/** @var MembershipService */
113
	private $membershipService;
114
115
	/** @var OutputService */
116
	private $outputService;
117
118
	/** @var ConfigService */
119
	private $configService;
120
121
122
	/**
123
	 * SyncService constructor.
124
	 *
125
	 * @param IUserManager $userManager
126
	 * @param IGroupManager $groupManager
127
	 * @param CircleRequest $circleRequest
128
	 * @param MemberRequest $memberRequest
129
	 * @param FederatedUserService $federatedUserService
130
	 * @param federatedEventService $federatedEventService
131
	 * @param CircleService $circleService
132
	 * @param MembershipService $membershipService
133
	 * @param OutputService $outputService
134
	 * @param ConfigService $configService
135
	 */
136
	public function __construct(
137
		IUserManager $userManager,
138
		IGroupManager $groupManager,
139
		CircleRequest $circleRequest,
140
		MemberRequest $memberRequest,
141
		FederatedUserService $federatedUserService,
142
		federatedEventService $federatedEventService,
143
		CircleService $circleService,
144
		MembershipService $membershipService,
145
		OutputService $outputService,
146
		ConfigService $configService
147
	) {
148
		$this->userManager = $userManager;
149
		$this->groupManager = $groupManager;
150
		$this->circleRequest = $circleRequest;
151
		$this->memberRequest = $memberRequest;
152
		$this->federatedUserService = $federatedUserService;
153
		$this->federatedEventService = $federatedEventService;
154
		$this->circleService = $circleService;
155
		$this->membershipService = $membershipService;
156
		$this->outputService = $outputService;
157
		$this->configService = $configService;
158
159
		$this->setup('app', Application::APP_ID);
160
	}
161
162
163
	/**
164
	 * @param int $sync
165
	 *
166
	 * @return void
167
	 */
168
	public function sync(int $sync = self::SYNC_ALL): void {
169
		if ($this->shouldSync(self::SYNC_APPS, $sync)) {
170
			$this->syncApps();
171
		}
172
173
		if ($this->shouldSync(self::SYNC_USERS, $sync)) {
174
			$this->syncNextcloudUsers();
175
		}
176
177
		if ($this->shouldSync(self::SYNC_GROUPS, $sync)) {
178
			$this->syncNextcloudGroups();
179
		}
180
181
		if ($this->shouldSync(self::SYNC_CONTACTS, $sync)) {
182
			$this->syncContacts();
183
		}
184
185
		if ($this->shouldSync(self::SYNC_GLOBALSCALE, $sync)) {
186
			$this->syncGlobalScale();
187
		}
188
189
		if ($this->shouldSync(self::SYNC_REMOTES, $sync)) {
190
			$this->syncRemote();
191
		}
192
	}
193
194
195
	/**
196
	 * @param int $item
197
	 * @param int $all
198
	 *
199
	 * @return bool
200
	 */
201
	private function shouldSync(int $item, int $all): bool {
202
		return (($item & $all) !== 0);
203
	}
204
205
206
	/**
207
	 */
208
	public function syncApps(): void {
209
		$this->outputService->output('Syncing Nextcloud Apps');
210
211
		try {
212
			$this->federatedUserService->getAppInitiator('circles', Member::APP_CIRCLES);
213
			$this->federatedUserService->getAppInitiator('occ', Member::APP_OCC);
214
		} catch (Exception $e) {
215
			$this->e($e);
216
		}
217
	}
218
219
220
	/**
221
	 * @return void
222
	 */
223 View Code Duplication
	public function syncNextcloudUsers(): void {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
224
		$this->outputService->output('Syncing Nextcloud Users');
225
226
		$users = $this->userManager->search('');
227
		$this->outputService->startMigrationProgress(count($users));
228
229
		foreach ($users as $user) {
230
			try {
231
				$this->syncNextcloudUser($user->getUID());
232
			} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
233
			}
234
		}
235
236
		$this->outputService->finishMigrationProgress();
237
	}
238
239
	/**
240
	 * @param string $userId
241
	 *
242
	 * @return FederatedUser
243
	 * @throws ContactAddressBookNotFoundException
244
	 * @throws ContactFormatException
245
	 * @throws ContactNotFoundException
246
	 * @throws FederatedUserException
247
	 * @throws FederatedUserNotFoundException
248
	 * @throws InvalidIdException
249
	 * @throws RequestBuilderException
250
	 * @throws SingleCircleNotFoundException
251
	 */
252
	public function syncNextcloudUser(string $userId): FederatedUser {
253
		$this->outputService->output('Syncing Nextcloud User \'' . $userId . '\'', true);
254
255
		return $this->federatedUserService->getLocalFederatedUser($userId);
256
	}
257
258
259
	/**
260
	 * @return void
261
	 */
262 View Code Duplication
	public function syncNextcloudGroups(): void {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
263
		$this->outputService->output('Syncing Nextcloud Groups');
264
265
		$groups = $this->groupManager->search('');
266
		$this->outputService->startMigrationProgress(count($groups));
267
		foreach ($groups as $group) {
268
			try {
269
				$this->syncNextcloudGroup($group->getGID());
270
			} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
271
			}
272
		}
273
274
		$this->outputService->finishMigrationProgress();
275
	}
276
277
	/**
278
	 * @param string $groupId
279
	 *
280
	 * @return Circle
281
	 * @throws FederatedUserException
282
	 * @throws FederatedUserNotFoundException
283
	 * @throws GroupNotFoundException
284
	 * @throws InvalidIdException
285
	 * @throws SingleCircleNotFoundException
286
	 * @throws FederatedEventException
287
	 * @throws FederatedItemException
288
	 * @throws InitiatorNotConfirmedException
289
	 * @throws OwnerNotFoundException
290
	 * @throws RemoteInstanceException
291
	 * @throws RemoteNotFoundException
292
	 * @throws RemoteResourceNotFoundException
293
	 * @throws UnknownRemoteException
294
	 * @throws RequestBuilderException
295
	 */
296
	public function syncNextcloudGroup(string $groupId): Circle {
297
		$this->outputService->output('Syncing Nextcloud Group \'' . $groupId . '\'', true);
298
299
		$circle = $this->federatedUserService->getGroupCircle($groupId);
300
		$group = $this->groupManager->get($groupId);
301
		foreach ($group->getUsers() as $user) {
302
			$member = $this->generateGroupMember($circle, $user->getUID());
303
			$event = new FederatedEvent(SingleMemberAdd::class);
304
			$event->setCircle($circle);
305
			$event->setMember($member);
306
			$this->federatedEventService->newEvent($event);
307
308
//			$this->memberRequest->insertOrUpdate($member);
309
		}
310
311
//		$this->membershipService->onUpdate($circle->getSingleId());
312
313
		return $circle;
314
	}
315
316
317
	/**
318
	 * @param string $userId
319
	 *
320
	 * @throws ContactAddressBookNotFoundException
321
	 * @throws ContactFormatException
322
	 * @throws ContactNotFoundException
323
	 * @throws FederatedUserException
324
	 * @throws FederatedUserNotFoundException
325
	 * @throws InvalidIdException
326
	 * @throws RequestBuilderException
327
	 */
328
	public function userDeleted(string $userId): void {
329
		try {
330
			$federatedUser = $this->federatedUserService->getLocalFederatedUser($userId, false);
331
		} catch (SingleCircleNotFoundException $e) {
332
			return;
333
		}
334
335
		$this->federatedUserService->setCurrentUser($federatedUser);
336
337
		$memberships = $federatedUser->getMemberships();
338
		foreach ($memberships as $membership) {
339
			if ($membership->getInheritanceDepth() > 1) {
340
				continue;
341
			}
342
343
			try {
344
				$this->circleService->circleLeave($membership->getCircleId(), true);
345
			} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
346
			}
347
		}
348
349
		$this->federatedUserService->deleteFederatedUser($federatedUser);
350
	}
351
352
353
	/**
354
	 * @param string $groupId
355
	 *
356
	 * @throws ContactAddressBookNotFoundException
357
	 * @throws ContactFormatException
358
	 * @throws ContactNotFoundException
359
	 * @throws FederatedUserException
360
	 * @throws InvalidIdException
361
	 * @throws RequestBuilderException
362
	 * @throws SingleCircleNotFoundException
363
	 */
364
	public function groupDeleted(string $groupId): void {
365
		$circle = new Circle();
366
		$circle->setName('group:' . $groupId)
367
			   ->setConfig(Circle::CFG_SYSTEM | Circle::CFG_NO_OWNER | Circle::CFG_HIDDEN)
368
			   ->setSource(Member::TYPE_GROUP);
369
370
		$owner = $this->federatedUserService->getAppInitiator(
371
			Application::APP_ID,
372
			Member::APP_CIRCLES,
373
			Application::APP_NAME
374
		);
375
376
		$member = new Member();
377
		$member->importFromIFederatedUser($owner);
378
		$member->setLevel(Member::LEVEL_OWNER)
379
			   ->setStatus(Member::STATUS_MEMBER);
380
		$circle->setOwner($member);
381
382
		try {
383
			$circle = $this->circleRequest->searchCircle($circle);
384
		} catch (CircleNotFoundException $e) {
385
			return;
386
		}
387
388
		$this->circleRequest->delete($circle);
389
		$this->memberRequest->deleteAllFromCircle($circle);
390
391
		$this->membershipService->onUpdate($circle->getSingleId());
392
	}
393
394
395
	/**
396
	 * @param Circle $circle
397
	 * @param string $userId
398
	 *
399
	 * @return Member
400
	 * @throws ContactAddressBookNotFoundException
401
	 * @throws ContactFormatException
402
	 * @throws ContactNotFoundException
403
	 * @throws FederatedUserException
404
	 * @throws FederatedUserNotFoundException
405
	 * @throws InvalidIdException
406
	 * @throws RequestBuilderException
407
	 * @throws SingleCircleNotFoundException
408
	 */
409
	private function generateGroupMember(Circle $circle, string $userId): Member {
410
		$federatedUser = $this->federatedUserService->getLocalFederatedUser($userId);
411
412
		$member = new Member();
413
		$member->importFromIFederatedUser($federatedUser);
414
		$member->setId($this->token(ManagedModel::ID_LENGTH));
415
		$member->setCircleId($circle->getSingleId());
416
		$member->setLevel(Member::LEVEL_MEMBER);
417
		$member->setStatus(Member::STATUS_MEMBER);
418
		$member->setInvitedBy($this->federatedUserService->getCurrentApp());
0 ignored issues
show
Bug introduced by
It seems like $this->federatedUserService->getCurrentApp() can be null; however, setInvitedBy() 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...
419
420
		return $member;
421
	}
422
423
424
	/**
425
	 * @param string $groupId
426
	 * @param string $userId
427
	 *
428
	 * @return void
429
	 * @throws ContactAddressBookNotFoundException
430
	 * @throws ContactFormatException
431
	 * @throws ContactNotFoundException
432
	 * @throws FederatedEventException
433
	 * @throws FederatedItemException
434
	 * @throws FederatedUserException
435
	 * @throws FederatedUserNotFoundException
436
	 * @throws GroupNotFoundException
437
	 * @throws InitiatorNotConfirmedException
438
	 * @throws InvalidIdException
439
	 * @throws OwnerNotFoundException
440
	 * @throws RemoteInstanceException
441
	 * @throws RemoteNotFoundException
442
	 * @throws RemoteResourceNotFoundException
443
	 * @throws RequestBuilderException
444
	 * @throws SingleCircleNotFoundException
445
	 * @throws UnknownRemoteException
446
	 */
447
	public function groupMemberAdded(string $groupId, string $userId): void {
448
		$circle = $this->federatedUserService->getGroupCircle($groupId);
449
		$member = $this->generateGroupMember($circle, $userId);
450
451
		$event = new FederatedEvent(SingleMemberAdd::class);
452
		$event->setCircle($circle);
453
		$event->setMember($member);
454
		$this->federatedEventService->newEvent($event);
455
456
//		$this->memberRequest->insertOrUpdate($member);
457
458
//		$this->membershipService->onUpdate($member->getSingleId());
459
	}
460
461
462
	/**
463
	 * @param string $groupId
464
	 * @param string $userId
465
	 *
466
	 * @throws FederatedEventException
467
	 * @throws FederatedItemException
468
	 * @throws FederatedUserException
469
	 * @throws FederatedUserNotFoundException
470
	 * @throws GroupNotFoundException
471
	 * @throws InitiatorNotConfirmedException
472
	 * @throws InvalidIdException
473
	 * @throws OwnerNotFoundException
474
	 * @throws RemoteInstanceException
475
	 * @throws RemoteNotFoundException
476
	 * @throws RemoteResourceNotFoundException
477
	 * @throws RequestBuilderException
478
	 * @throws SingleCircleNotFoundException
479
	 * @throws UnknownRemoteException
480
	 */
481
	public function groupMemberRemoved(string $groupId, string $userId): void {
482
		$circle = $this->federatedUserService->getGroupCircle($groupId);
483
		$federatedUser = $this->federatedUserService->getLocalFederatedUser($userId);
484
485
		$this->memberRequest->deleteFederatedUserFromCircle($federatedUser, $circle);
486
		$this->membershipService->onUpdate($federatedUser->getSingleId());
487
	}
488
489
490
	/**
491
	 * @return void
492
	 */
493
	public function syncContacts(): void {
494
		$this->outputService->output('Syncing Contacts');
495
	}
496
497
498
	/**
499
	 * @return void
500
	 */
501
	public function syncGlobalScale(): void {
502
		$this->outputService->output('Syncing GlobalScale');
503
	}
504
505
506
	/**
507
	 * @return void
508
	 */
509
	public function syncRemote(): void {
510
		$this->outputService->output('Syncing Remote Instance');
511
	}
512
513
514
	/**
515
	 * @param string $circleId
516
	 *
517
	 * @return void
518
	 */
519
	public function syncRemoteCircle(string $circleId): void {
0 ignored issues
show
Unused Code introduced by
The parameter $circleId is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
520
	}
521
522
}
523
524