Passed
Push — master ( fd6313...773e28 )
by Joas
13:23 queued 29s
created
apps/dav/lib/Connector/Sabre/Principal.php 2 patches
Indentation   +527 added lines, -527 removed lines patch added patch discarded remove patch
@@ -56,531 +56,531 @@
 block discarded – undo
56 56
 
57 57
 class Principal implements BackendInterface {
58 58
 
59
-	/** @var IUserManager */
60
-	private $userManager;
61
-
62
-	/** @var IGroupManager */
63
-	private $groupManager;
64
-
65
-	/** @var IShareManager */
66
-	private $shareManager;
67
-
68
-	/** @var IUserSession */
69
-	private $userSession;
70
-
71
-	/** @var IAppManager */
72
-	private $appManager;
73
-
74
-	/** @var string */
75
-	private $principalPrefix;
76
-
77
-	/** @var bool */
78
-	private $hasGroups;
79
-
80
-	/** @var bool */
81
-	private $hasCircles;
82
-
83
-	/** @var ProxyMapper */
84
-	private $proxyMapper;
85
-
86
-	/** @var KnownUserService */
87
-	private $knownUserService;
88
-
89
-	/** @var IConfig */
90
-	private $config;
91
-
92
-	public function __construct(IUserManager $userManager,
93
-								IGroupManager $groupManager,
94
-								IShareManager $shareManager,
95
-								IUserSession $userSession,
96
-								IAppManager $appManager,
97
-								ProxyMapper $proxyMapper,
98
-								KnownUserService $knownUserService,
99
-								IConfig $config,
100
-								string $principalPrefix = 'principals/users/') {
101
-		$this->userManager = $userManager;
102
-		$this->groupManager = $groupManager;
103
-		$this->shareManager = $shareManager;
104
-		$this->userSession = $userSession;
105
-		$this->appManager = $appManager;
106
-		$this->principalPrefix = trim($principalPrefix, '/');
107
-		$this->hasGroups = $this->hasCircles = ($principalPrefix === 'principals/users/');
108
-		$this->proxyMapper = $proxyMapper;
109
-		$this->knownUserService = $knownUserService;
110
-		$this->config = $config;
111
-	}
112
-
113
-	use PrincipalProxyTrait {
114
-		getGroupMembership as protected traitGetGroupMembership;
115
-	}
116
-
117
-	/**
118
-	 * Returns a list of principals based on a prefix.
119
-	 *
120
-	 * This prefix will often contain something like 'principals'. You are only
121
-	 * expected to return principals that are in this base path.
122
-	 *
123
-	 * You are expected to return at least a 'uri' for every user, you can
124
-	 * return any additional properties if you wish so. Common properties are:
125
-	 *   {DAV:}displayname
126
-	 *
127
-	 * @param string $prefixPath
128
-	 * @return string[]
129
-	 */
130
-	public function getPrincipalsByPrefix($prefixPath) {
131
-		$principals = [];
132
-
133
-		if ($prefixPath === $this->principalPrefix) {
134
-			foreach ($this->userManager->search('') as $user) {
135
-				$principals[] = $this->userToPrincipal($user);
136
-			}
137
-		}
138
-
139
-		return $principals;
140
-	}
141
-
142
-	/**
143
-	 * Returns a specific principal, specified by it's path.
144
-	 * The returned structure should be the exact same as from
145
-	 * getPrincipalsByPrefix.
146
-	 *
147
-	 * @param string $path
148
-	 * @return array
149
-	 */
150
-	public function getPrincipalByPath($path) {
151
-		[$prefix, $name] = \Sabre\Uri\split($path);
152
-		$decodedName = urldecode($name);
153
-
154
-		if ($name === 'calendar-proxy-write' || $name === 'calendar-proxy-read') {
155
-			[$prefix2, $name2] = \Sabre\Uri\split($prefix);
156
-
157
-			if ($prefix2 === $this->principalPrefix) {
158
-				$user = $this->userManager->get($name2);
159
-
160
-				if ($user !== null) {
161
-					return [
162
-						'uri' => 'principals/users/' . $user->getUID() . '/' . $name,
163
-					];
164
-				}
165
-				return null;
166
-			}
167
-		}
168
-
169
-		if ($prefix === $this->principalPrefix) {
170
-			// Depending on where it is called, it may happen that this function
171
-			// is called either with a urlencoded version of the name or with a non-urlencoded one.
172
-			// The urldecode function replaces %## and +, both of which are forbidden in usernames.
173
-			// Hence there can be no ambiguity here and it is safe to call urldecode on all usernames
174
-			$user = $this->userManager->get($decodedName);
175
-
176
-			if ($user !== null) {
177
-				return $this->userToPrincipal($user);
178
-			}
179
-		} elseif ($prefix === 'principals/circles') {
180
-			if ($this->userSession->getUser() !== null) {
181
-				// At the time of writing - 2021-01-19 — a mixed state is possible.
182
-				// The second condition can be removed when this is fixed.
183
-				return $this->circleToPrincipal($decodedName)
184
-					?: $this->circleToPrincipal($name);
185
-			}
186
-		} elseif ($prefix === 'principals/groups') {
187
-			// At the time of writing - 2021-01-19 — a mixed state is possible.
188
-			// The second condition can be removed when this is fixed.
189
-			$group = $this->groupManager->get($decodedName)
190
-				?: $this->groupManager->get($name);
191
-			if ($group instanceof IGroup) {
192
-				return [
193
-					'uri' => 'principals/groups/' . $name,
194
-					'{DAV:}displayname' => $group->getDisplayName(),
195
-				];
196
-			}
197
-		}
198
-		return null;
199
-	}
200
-
201
-	/**
202
-	 * Returns the list of groups a principal is a member of
203
-	 *
204
-	 * @param string $principal
205
-	 * @param bool $needGroups
206
-	 * @return array
207
-	 * @throws Exception
208
-	 */
209
-	public function getGroupMembership($principal, $needGroups = false) {
210
-		[$prefix, $name] = \Sabre\Uri\split($principal);
211
-
212
-		if ($prefix !== $this->principalPrefix) {
213
-			return [];
214
-		}
215
-
216
-		$user = $this->userManager->get($name);
217
-		if (!$user) {
218
-			throw new Exception('Principal not found');
219
-		}
220
-
221
-		$groups = [];
222
-
223
-		if ($this->hasGroups || $needGroups) {
224
-			$userGroups = $this->groupManager->getUserGroups($user);
225
-			foreach ($userGroups as $userGroup) {
226
-				$groups[] = 'principals/groups/' . urlencode($userGroup->getGID());
227
-			}
228
-		}
229
-
230
-		$groups = array_unique(array_merge(
231
-			$groups,
232
-			$this->traitGetGroupMembership($principal, $needGroups)
233
-		));
234
-
235
-		return $groups;
236
-	}
237
-
238
-	/**
239
-	 * @param string $path
240
-	 * @param PropPatch $propPatch
241
-	 * @return int
242
-	 */
243
-	public function updatePrincipal($path, PropPatch $propPatch) {
244
-		return 0;
245
-	}
246
-
247
-	/**
248
-	 * Search user principals
249
-	 *
250
-	 * @param array $searchProperties
251
-	 * @param string $test
252
-	 * @return array
253
-	 */
254
-	protected function searchUserPrincipals(array $searchProperties, $test = 'allof') {
255
-		$results = [];
256
-
257
-		// If sharing is disabled, return the empty array
258
-		$shareAPIEnabled = $this->shareManager->shareApiEnabled();
259
-		if (!$shareAPIEnabled) {
260
-			return [];
261
-		}
262
-
263
-		$allowEnumeration = $this->shareManager->allowEnumeration();
264
-		$limitEnumerationGroup = $this->shareManager->limitEnumerationToGroups();
265
-		$limitEnumerationPhone = $this->shareManager->limitEnumerationToPhone();
266
-		$allowEnumerationFullMatch = $this->shareManager->allowEnumerationFullMatch();
267
-
268
-		// If sharing is restricted to group members only,
269
-		// return only members that have groups in common
270
-		$restrictGroups = false;
271
-		$currentUser = $this->userSession->getUser();
272
-		if ($this->shareManager->shareWithGroupMembersOnly()) {
273
-			if (!$currentUser instanceof IUser) {
274
-				return [];
275
-			}
276
-
277
-			$restrictGroups = $this->groupManager->getUserGroupIds($currentUser);
278
-		}
279
-
280
-		$currentUserGroups = [];
281
-		if ($limitEnumerationGroup) {
282
-			if ($currentUser instanceof IUser) {
283
-				$currentUserGroups = $this->groupManager->getUserGroupIds($currentUser);
284
-			}
285
-		}
286
-
287
-		$searchLimit = $this->config->getSystemValueInt('sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT);
288
-		if ($searchLimit <= 0) {
289
-			$searchLimit = null;
290
-		}
291
-		foreach ($searchProperties as $prop => $value) {
292
-			switch ($prop) {
293
-				case '{http://sabredav.org/ns}email-address':
294
-					if (!$allowEnumeration) {
295
-						if ($allowEnumerationFullMatch) {
296
-							$users = $this->userManager->getByEmail($value);
297
-							$users = \array_filter($users, static function (IUser $user) use ($value) {
298
-								return $user->getEMailAddress() === $value;
299
-							});
300
-						} else {
301
-							$users = [];
302
-						}
303
-					} else {
304
-						$users = $this->userManager->getByEmail($value);
305
-						$users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $allowEnumerationFullMatch, $currentUserGroups) {
306
-							if ($allowEnumerationFullMatch && $user->getEMailAddress() === $value) {
307
-								return true;
308
-							}
309
-
310
-							if ($limitEnumerationPhone
311
-								&& $currentUser instanceof IUser
312
-								&& $this->knownUserService->isKnownToUser($currentUser->getUID(), $user->getUID())) {
313
-								// Synced phonebook match
314
-								return true;
315
-							}
316
-
317
-							if (!$limitEnumerationGroup) {
318
-								// No limitation on enumeration, all allowed
319
-								return true;
320
-							}
321
-
322
-							return !empty($currentUserGroups) && !empty(array_intersect(
323
-								$this->groupManager->getUserGroupIds($user),
324
-								$currentUserGroups
325
-							));
326
-						});
327
-					}
328
-
329
-					$results[] = array_reduce($users, function (array $carry, IUser $user) use ($restrictGroups) {
330
-						// is sharing restricted to groups only?
331
-						if ($restrictGroups !== false) {
332
-							$userGroups = $this->groupManager->getUserGroupIds($user);
333
-							if (count(array_intersect($userGroups, $restrictGroups)) === 0) {
334
-								return $carry;
335
-							}
336
-						}
337
-
338
-						$carry[] = $this->principalPrefix . '/' . $user->getUID();
339
-						return $carry;
340
-					}, []);
341
-					break;
342
-
343
-				case '{DAV:}displayname':
344
-
345
-					if (!$allowEnumeration) {
346
-						if ($allowEnumerationFullMatch) {
347
-							$users = $this->userManager->searchDisplayName($value, $searchLimit);
348
-							$users = \array_filter($users, static function (IUser $user) use ($value) {
349
-								return $user->getDisplayName() === $value;
350
-							});
351
-						} else {
352
-							$users = [];
353
-						}
354
-					} else {
355
-						$users = $this->userManager->searchDisplayName($value, $searchLimit);
356
-						$users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $allowEnumerationFullMatch, $currentUserGroups) {
357
-							if ($allowEnumerationFullMatch && $user->getDisplayName() === $value) {
358
-								return true;
359
-							}
360
-
361
-							if ($limitEnumerationPhone
362
-								&& $currentUser instanceof IUser
363
-								&& $this->knownUserService->isKnownToUser($currentUser->getUID(), $user->getUID())) {
364
-								// Synced phonebook match
365
-								return true;
366
-							}
367
-
368
-							if (!$limitEnumerationGroup) {
369
-								// No limitation on enumeration, all allowed
370
-								return true;
371
-							}
372
-
373
-							return !empty($currentUserGroups) && !empty(array_intersect(
374
-								$this->groupManager->getUserGroupIds($user),
375
-								$currentUserGroups
376
-							));
377
-						});
378
-					}
379
-
380
-					$results[] = array_reduce($users, function (array $carry, IUser $user) use ($restrictGroups) {
381
-						// is sharing restricted to groups only?
382
-						if ($restrictGroups !== false) {
383
-							$userGroups = $this->groupManager->getUserGroupIds($user);
384
-							if (count(array_intersect($userGroups, $restrictGroups)) === 0) {
385
-								return $carry;
386
-							}
387
-						}
388
-
389
-						$carry[] = $this->principalPrefix . '/' . $user->getUID();
390
-						return $carry;
391
-					}, []);
392
-					break;
393
-
394
-				case '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set':
395
-					// If you add support for more search properties that qualify as a user-address,
396
-					// please also add them to the array below
397
-					$results[] = $this->searchUserPrincipals([
398
-						// In theory this should also search for principal:principals/users/...
399
-						// but that's used internally only anyway and i don't know of any client querying that
400
-						'{http://sabredav.org/ns}email-address' => $value,
401
-					], 'anyof');
402
-					break;
403
-
404
-				default:
405
-					$results[] = [];
406
-					break;
407
-			}
408
-		}
409
-
410
-		// results is an array of arrays, so this is not the first search result
411
-		// but the results of the first searchProperty
412
-		if (count($results) === 1) {
413
-			return $results[0];
414
-		}
415
-
416
-		switch ($test) {
417
-			case 'anyof':
418
-				return array_values(array_unique(array_merge(...$results)));
419
-
420
-			case 'allof':
421
-			default:
422
-				return array_values(array_intersect(...$results));
423
-		}
424
-	}
425
-
426
-	/**
427
-	 * @param string $prefixPath
428
-	 * @param array $searchProperties
429
-	 * @param string $test
430
-	 * @return array
431
-	 */
432
-	public function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
433
-		if (count($searchProperties) === 0) {
434
-			return [];
435
-		}
436
-
437
-		switch ($prefixPath) {
438
-			case 'principals/users':
439
-				return $this->searchUserPrincipals($searchProperties, $test);
440
-
441
-			default:
442
-				return [];
443
-		}
444
-	}
445
-
446
-	/**
447
-	 * @param string $uri
448
-	 * @param string $principalPrefix
449
-	 * @return string
450
-	 */
451
-	public function findByUri($uri, $principalPrefix) {
452
-		// If sharing is disabled, return the empty array
453
-		$shareAPIEnabled = $this->shareManager->shareApiEnabled();
454
-		if (!$shareAPIEnabled) {
455
-			return null;
456
-		}
457
-
458
-		// If sharing is restricted to group members only,
459
-		// return only members that have groups in common
460
-		$restrictGroups = false;
461
-		if ($this->shareManager->shareWithGroupMembersOnly()) {
462
-			$user = $this->userSession->getUser();
463
-			if (!$user) {
464
-				return null;
465
-			}
466
-
467
-			$restrictGroups = $this->groupManager->getUserGroupIds($user);
468
-		}
469
-
470
-		if (strpos($uri, 'mailto:') === 0) {
471
-			if ($principalPrefix === 'principals/users') {
472
-				$users = $this->userManager->getByEmail(substr($uri, 7));
473
-				if (count($users) !== 1) {
474
-					return null;
475
-				}
476
-				$user = $users[0];
477
-
478
-				if ($restrictGroups !== false) {
479
-					$userGroups = $this->groupManager->getUserGroupIds($user);
480
-					if (count(array_intersect($userGroups, $restrictGroups)) === 0) {
481
-						return null;
482
-					}
483
-				}
484
-
485
-				return $this->principalPrefix . '/' . $user->getUID();
486
-			}
487
-		}
488
-		if (substr($uri, 0, 10) === 'principal:') {
489
-			$principal = substr($uri, 10);
490
-			$principal = $this->getPrincipalByPath($principal);
491
-			if ($principal !== null) {
492
-				return $principal['uri'];
493
-			}
494
-		}
495
-
496
-		return null;
497
-	}
498
-
499
-	/**
500
-	 * @param IUser $user
501
-	 * @return array
502
-	 */
503
-	protected function userToPrincipal($user) {
504
-		$userId = $user->getUID();
505
-		$displayName = $user->getDisplayName();
506
-		$principal = [
507
-			'uri' => $this->principalPrefix . '/' . $userId,
508
-			'{DAV:}displayname' => is_null($displayName) ? $userId : $displayName,
509
-			'{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL',
510
-		];
511
-
512
-		$email = $user->getEMailAddress();
513
-		if (!empty($email)) {
514
-			$principal['{http://sabredav.org/ns}email-address'] = $email;
515
-		}
516
-
517
-		return $principal;
518
-	}
519
-
520
-	public function getPrincipalPrefix() {
521
-		return $this->principalPrefix;
522
-	}
523
-
524
-	/**
525
-	 * @param string $circleUniqueId
526
-	 * @return array|null
527
-	 */
528
-	protected function circleToPrincipal($circleUniqueId) {
529
-		if (!$this->appManager->isEnabledForUser('circles') || !class_exists('\OCA\Circles\Api\v1\Circles')) {
530
-			return null;
531
-		}
532
-
533
-		try {
534
-			$circle = \OCA\Circles\Api\v1\Circles::detailsCircle($circleUniqueId, true);
535
-		} catch (QueryException $ex) {
536
-			return null;
537
-		} catch (CircleNotFoundException $ex) {
538
-			return null;
539
-		}
540
-
541
-		if (!$circle) {
542
-			return null;
543
-		}
544
-
545
-		$principal = [
546
-			'uri' => 'principals/circles/' . $circleUniqueId,
547
-			'{DAV:}displayname' => $circle->getDisplayName(),
548
-		];
549
-
550
-		return $principal;
551
-	}
552
-
553
-	/**
554
-	 * Returns the list of circles a principal is a member of
555
-	 *
556
-	 * @param string $principal
557
-	 * @return array
558
-	 * @throws Exception
559
-	 * @throws \OCP\AppFramework\QueryException
560
-	 * @suppress PhanUndeclaredClassMethod
561
-	 */
562
-	public function getCircleMembership($principal):array {
563
-		if (!$this->appManager->isEnabledForUser('circles') || !class_exists('\OCA\Circles\Api\v1\Circles')) {
564
-			return [];
565
-		}
566
-
567
-		[$prefix, $name] = \Sabre\Uri\split($principal);
568
-		if ($this->hasCircles && $prefix === $this->principalPrefix) {
569
-			$user = $this->userManager->get($name);
570
-			if (!$user) {
571
-				throw new Exception('Principal not found');
572
-			}
573
-
574
-			$circles = \OCA\Circles\Api\v1\Circles::joinedCircles($name, true);
575
-
576
-			$circles = array_map(function ($circle) {
577
-				/** @var \OCA\Circles\Model\Circle $circle */
578
-				return 'principals/circles/' . urlencode($circle->getSingleId());
579
-			}, $circles);
580
-
581
-			return $circles;
582
-		}
583
-
584
-		return [];
585
-	}
59
+    /** @var IUserManager */
60
+    private $userManager;
61
+
62
+    /** @var IGroupManager */
63
+    private $groupManager;
64
+
65
+    /** @var IShareManager */
66
+    private $shareManager;
67
+
68
+    /** @var IUserSession */
69
+    private $userSession;
70
+
71
+    /** @var IAppManager */
72
+    private $appManager;
73
+
74
+    /** @var string */
75
+    private $principalPrefix;
76
+
77
+    /** @var bool */
78
+    private $hasGroups;
79
+
80
+    /** @var bool */
81
+    private $hasCircles;
82
+
83
+    /** @var ProxyMapper */
84
+    private $proxyMapper;
85
+
86
+    /** @var KnownUserService */
87
+    private $knownUserService;
88
+
89
+    /** @var IConfig */
90
+    private $config;
91
+
92
+    public function __construct(IUserManager $userManager,
93
+                                IGroupManager $groupManager,
94
+                                IShareManager $shareManager,
95
+                                IUserSession $userSession,
96
+                                IAppManager $appManager,
97
+                                ProxyMapper $proxyMapper,
98
+                                KnownUserService $knownUserService,
99
+                                IConfig $config,
100
+                                string $principalPrefix = 'principals/users/') {
101
+        $this->userManager = $userManager;
102
+        $this->groupManager = $groupManager;
103
+        $this->shareManager = $shareManager;
104
+        $this->userSession = $userSession;
105
+        $this->appManager = $appManager;
106
+        $this->principalPrefix = trim($principalPrefix, '/');
107
+        $this->hasGroups = $this->hasCircles = ($principalPrefix === 'principals/users/');
108
+        $this->proxyMapper = $proxyMapper;
109
+        $this->knownUserService = $knownUserService;
110
+        $this->config = $config;
111
+    }
112
+
113
+    use PrincipalProxyTrait {
114
+        getGroupMembership as protected traitGetGroupMembership;
115
+    }
116
+
117
+    /**
118
+     * Returns a list of principals based on a prefix.
119
+     *
120
+     * This prefix will often contain something like 'principals'. You are only
121
+     * expected to return principals that are in this base path.
122
+     *
123
+     * You are expected to return at least a 'uri' for every user, you can
124
+     * return any additional properties if you wish so. Common properties are:
125
+     *   {DAV:}displayname
126
+     *
127
+     * @param string $prefixPath
128
+     * @return string[]
129
+     */
130
+    public function getPrincipalsByPrefix($prefixPath) {
131
+        $principals = [];
132
+
133
+        if ($prefixPath === $this->principalPrefix) {
134
+            foreach ($this->userManager->search('') as $user) {
135
+                $principals[] = $this->userToPrincipal($user);
136
+            }
137
+        }
138
+
139
+        return $principals;
140
+    }
141
+
142
+    /**
143
+     * Returns a specific principal, specified by it's path.
144
+     * The returned structure should be the exact same as from
145
+     * getPrincipalsByPrefix.
146
+     *
147
+     * @param string $path
148
+     * @return array
149
+     */
150
+    public function getPrincipalByPath($path) {
151
+        [$prefix, $name] = \Sabre\Uri\split($path);
152
+        $decodedName = urldecode($name);
153
+
154
+        if ($name === 'calendar-proxy-write' || $name === 'calendar-proxy-read') {
155
+            [$prefix2, $name2] = \Sabre\Uri\split($prefix);
156
+
157
+            if ($prefix2 === $this->principalPrefix) {
158
+                $user = $this->userManager->get($name2);
159
+
160
+                if ($user !== null) {
161
+                    return [
162
+                        'uri' => 'principals/users/' . $user->getUID() . '/' . $name,
163
+                    ];
164
+                }
165
+                return null;
166
+            }
167
+        }
168
+
169
+        if ($prefix === $this->principalPrefix) {
170
+            // Depending on where it is called, it may happen that this function
171
+            // is called either with a urlencoded version of the name or with a non-urlencoded one.
172
+            // The urldecode function replaces %## and +, both of which are forbidden in usernames.
173
+            // Hence there can be no ambiguity here and it is safe to call urldecode on all usernames
174
+            $user = $this->userManager->get($decodedName);
175
+
176
+            if ($user !== null) {
177
+                return $this->userToPrincipal($user);
178
+            }
179
+        } elseif ($prefix === 'principals/circles') {
180
+            if ($this->userSession->getUser() !== null) {
181
+                // At the time of writing - 2021-01-19 — a mixed state is possible.
182
+                // The second condition can be removed when this is fixed.
183
+                return $this->circleToPrincipal($decodedName)
184
+                    ?: $this->circleToPrincipal($name);
185
+            }
186
+        } elseif ($prefix === 'principals/groups') {
187
+            // At the time of writing - 2021-01-19 — a mixed state is possible.
188
+            // The second condition can be removed when this is fixed.
189
+            $group = $this->groupManager->get($decodedName)
190
+                ?: $this->groupManager->get($name);
191
+            if ($group instanceof IGroup) {
192
+                return [
193
+                    'uri' => 'principals/groups/' . $name,
194
+                    '{DAV:}displayname' => $group->getDisplayName(),
195
+                ];
196
+            }
197
+        }
198
+        return null;
199
+    }
200
+
201
+    /**
202
+     * Returns the list of groups a principal is a member of
203
+     *
204
+     * @param string $principal
205
+     * @param bool $needGroups
206
+     * @return array
207
+     * @throws Exception
208
+     */
209
+    public function getGroupMembership($principal, $needGroups = false) {
210
+        [$prefix, $name] = \Sabre\Uri\split($principal);
211
+
212
+        if ($prefix !== $this->principalPrefix) {
213
+            return [];
214
+        }
215
+
216
+        $user = $this->userManager->get($name);
217
+        if (!$user) {
218
+            throw new Exception('Principal not found');
219
+        }
220
+
221
+        $groups = [];
222
+
223
+        if ($this->hasGroups || $needGroups) {
224
+            $userGroups = $this->groupManager->getUserGroups($user);
225
+            foreach ($userGroups as $userGroup) {
226
+                $groups[] = 'principals/groups/' . urlencode($userGroup->getGID());
227
+            }
228
+        }
229
+
230
+        $groups = array_unique(array_merge(
231
+            $groups,
232
+            $this->traitGetGroupMembership($principal, $needGroups)
233
+        ));
234
+
235
+        return $groups;
236
+    }
237
+
238
+    /**
239
+     * @param string $path
240
+     * @param PropPatch $propPatch
241
+     * @return int
242
+     */
243
+    public function updatePrincipal($path, PropPatch $propPatch) {
244
+        return 0;
245
+    }
246
+
247
+    /**
248
+     * Search user principals
249
+     *
250
+     * @param array $searchProperties
251
+     * @param string $test
252
+     * @return array
253
+     */
254
+    protected function searchUserPrincipals(array $searchProperties, $test = 'allof') {
255
+        $results = [];
256
+
257
+        // If sharing is disabled, return the empty array
258
+        $shareAPIEnabled = $this->shareManager->shareApiEnabled();
259
+        if (!$shareAPIEnabled) {
260
+            return [];
261
+        }
262
+
263
+        $allowEnumeration = $this->shareManager->allowEnumeration();
264
+        $limitEnumerationGroup = $this->shareManager->limitEnumerationToGroups();
265
+        $limitEnumerationPhone = $this->shareManager->limitEnumerationToPhone();
266
+        $allowEnumerationFullMatch = $this->shareManager->allowEnumerationFullMatch();
267
+
268
+        // If sharing is restricted to group members only,
269
+        // return only members that have groups in common
270
+        $restrictGroups = false;
271
+        $currentUser = $this->userSession->getUser();
272
+        if ($this->shareManager->shareWithGroupMembersOnly()) {
273
+            if (!$currentUser instanceof IUser) {
274
+                return [];
275
+            }
276
+
277
+            $restrictGroups = $this->groupManager->getUserGroupIds($currentUser);
278
+        }
279
+
280
+        $currentUserGroups = [];
281
+        if ($limitEnumerationGroup) {
282
+            if ($currentUser instanceof IUser) {
283
+                $currentUserGroups = $this->groupManager->getUserGroupIds($currentUser);
284
+            }
285
+        }
286
+
287
+        $searchLimit = $this->config->getSystemValueInt('sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT);
288
+        if ($searchLimit <= 0) {
289
+            $searchLimit = null;
290
+        }
291
+        foreach ($searchProperties as $prop => $value) {
292
+            switch ($prop) {
293
+                case '{http://sabredav.org/ns}email-address':
294
+                    if (!$allowEnumeration) {
295
+                        if ($allowEnumerationFullMatch) {
296
+                            $users = $this->userManager->getByEmail($value);
297
+                            $users = \array_filter($users, static function (IUser $user) use ($value) {
298
+                                return $user->getEMailAddress() === $value;
299
+                            });
300
+                        } else {
301
+                            $users = [];
302
+                        }
303
+                    } else {
304
+                        $users = $this->userManager->getByEmail($value);
305
+                        $users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $allowEnumerationFullMatch, $currentUserGroups) {
306
+                            if ($allowEnumerationFullMatch && $user->getEMailAddress() === $value) {
307
+                                return true;
308
+                            }
309
+
310
+                            if ($limitEnumerationPhone
311
+                                && $currentUser instanceof IUser
312
+                                && $this->knownUserService->isKnownToUser($currentUser->getUID(), $user->getUID())) {
313
+                                // Synced phonebook match
314
+                                return true;
315
+                            }
316
+
317
+                            if (!$limitEnumerationGroup) {
318
+                                // No limitation on enumeration, all allowed
319
+                                return true;
320
+                            }
321
+
322
+                            return !empty($currentUserGroups) && !empty(array_intersect(
323
+                                $this->groupManager->getUserGroupIds($user),
324
+                                $currentUserGroups
325
+                            ));
326
+                        });
327
+                    }
328
+
329
+                    $results[] = array_reduce($users, function (array $carry, IUser $user) use ($restrictGroups) {
330
+                        // is sharing restricted to groups only?
331
+                        if ($restrictGroups !== false) {
332
+                            $userGroups = $this->groupManager->getUserGroupIds($user);
333
+                            if (count(array_intersect($userGroups, $restrictGroups)) === 0) {
334
+                                return $carry;
335
+                            }
336
+                        }
337
+
338
+                        $carry[] = $this->principalPrefix . '/' . $user->getUID();
339
+                        return $carry;
340
+                    }, []);
341
+                    break;
342
+
343
+                case '{DAV:}displayname':
344
+
345
+                    if (!$allowEnumeration) {
346
+                        if ($allowEnumerationFullMatch) {
347
+                            $users = $this->userManager->searchDisplayName($value, $searchLimit);
348
+                            $users = \array_filter($users, static function (IUser $user) use ($value) {
349
+                                return $user->getDisplayName() === $value;
350
+                            });
351
+                        } else {
352
+                            $users = [];
353
+                        }
354
+                    } else {
355
+                        $users = $this->userManager->searchDisplayName($value, $searchLimit);
356
+                        $users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $allowEnumerationFullMatch, $currentUserGroups) {
357
+                            if ($allowEnumerationFullMatch && $user->getDisplayName() === $value) {
358
+                                return true;
359
+                            }
360
+
361
+                            if ($limitEnumerationPhone
362
+                                && $currentUser instanceof IUser
363
+                                && $this->knownUserService->isKnownToUser($currentUser->getUID(), $user->getUID())) {
364
+                                // Synced phonebook match
365
+                                return true;
366
+                            }
367
+
368
+                            if (!$limitEnumerationGroup) {
369
+                                // No limitation on enumeration, all allowed
370
+                                return true;
371
+                            }
372
+
373
+                            return !empty($currentUserGroups) && !empty(array_intersect(
374
+                                $this->groupManager->getUserGroupIds($user),
375
+                                $currentUserGroups
376
+                            ));
377
+                        });
378
+                    }
379
+
380
+                    $results[] = array_reduce($users, function (array $carry, IUser $user) use ($restrictGroups) {
381
+                        // is sharing restricted to groups only?
382
+                        if ($restrictGroups !== false) {
383
+                            $userGroups = $this->groupManager->getUserGroupIds($user);
384
+                            if (count(array_intersect($userGroups, $restrictGroups)) === 0) {
385
+                                return $carry;
386
+                            }
387
+                        }
388
+
389
+                        $carry[] = $this->principalPrefix . '/' . $user->getUID();
390
+                        return $carry;
391
+                    }, []);
392
+                    break;
393
+
394
+                case '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set':
395
+                    // If you add support for more search properties that qualify as a user-address,
396
+                    // please also add them to the array below
397
+                    $results[] = $this->searchUserPrincipals([
398
+                        // In theory this should also search for principal:principals/users/...
399
+                        // but that's used internally only anyway and i don't know of any client querying that
400
+                        '{http://sabredav.org/ns}email-address' => $value,
401
+                    ], 'anyof');
402
+                    break;
403
+
404
+                default:
405
+                    $results[] = [];
406
+                    break;
407
+            }
408
+        }
409
+
410
+        // results is an array of arrays, so this is not the first search result
411
+        // but the results of the first searchProperty
412
+        if (count($results) === 1) {
413
+            return $results[0];
414
+        }
415
+
416
+        switch ($test) {
417
+            case 'anyof':
418
+                return array_values(array_unique(array_merge(...$results)));
419
+
420
+            case 'allof':
421
+            default:
422
+                return array_values(array_intersect(...$results));
423
+        }
424
+    }
425
+
426
+    /**
427
+     * @param string $prefixPath
428
+     * @param array $searchProperties
429
+     * @param string $test
430
+     * @return array
431
+     */
432
+    public function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
433
+        if (count($searchProperties) === 0) {
434
+            return [];
435
+        }
436
+
437
+        switch ($prefixPath) {
438
+            case 'principals/users':
439
+                return $this->searchUserPrincipals($searchProperties, $test);
440
+
441
+            default:
442
+                return [];
443
+        }
444
+    }
445
+
446
+    /**
447
+     * @param string $uri
448
+     * @param string $principalPrefix
449
+     * @return string
450
+     */
451
+    public function findByUri($uri, $principalPrefix) {
452
+        // If sharing is disabled, return the empty array
453
+        $shareAPIEnabled = $this->shareManager->shareApiEnabled();
454
+        if (!$shareAPIEnabled) {
455
+            return null;
456
+        }
457
+
458
+        // If sharing is restricted to group members only,
459
+        // return only members that have groups in common
460
+        $restrictGroups = false;
461
+        if ($this->shareManager->shareWithGroupMembersOnly()) {
462
+            $user = $this->userSession->getUser();
463
+            if (!$user) {
464
+                return null;
465
+            }
466
+
467
+            $restrictGroups = $this->groupManager->getUserGroupIds($user);
468
+        }
469
+
470
+        if (strpos($uri, 'mailto:') === 0) {
471
+            if ($principalPrefix === 'principals/users') {
472
+                $users = $this->userManager->getByEmail(substr($uri, 7));
473
+                if (count($users) !== 1) {
474
+                    return null;
475
+                }
476
+                $user = $users[0];
477
+
478
+                if ($restrictGroups !== false) {
479
+                    $userGroups = $this->groupManager->getUserGroupIds($user);
480
+                    if (count(array_intersect($userGroups, $restrictGroups)) === 0) {
481
+                        return null;
482
+                    }
483
+                }
484
+
485
+                return $this->principalPrefix . '/' . $user->getUID();
486
+            }
487
+        }
488
+        if (substr($uri, 0, 10) === 'principal:') {
489
+            $principal = substr($uri, 10);
490
+            $principal = $this->getPrincipalByPath($principal);
491
+            if ($principal !== null) {
492
+                return $principal['uri'];
493
+            }
494
+        }
495
+
496
+        return null;
497
+    }
498
+
499
+    /**
500
+     * @param IUser $user
501
+     * @return array
502
+     */
503
+    protected function userToPrincipal($user) {
504
+        $userId = $user->getUID();
505
+        $displayName = $user->getDisplayName();
506
+        $principal = [
507
+            'uri' => $this->principalPrefix . '/' . $userId,
508
+            '{DAV:}displayname' => is_null($displayName) ? $userId : $displayName,
509
+            '{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL',
510
+        ];
511
+
512
+        $email = $user->getEMailAddress();
513
+        if (!empty($email)) {
514
+            $principal['{http://sabredav.org/ns}email-address'] = $email;
515
+        }
516
+
517
+        return $principal;
518
+    }
519
+
520
+    public function getPrincipalPrefix() {
521
+        return $this->principalPrefix;
522
+    }
523
+
524
+    /**
525
+     * @param string $circleUniqueId
526
+     * @return array|null
527
+     */
528
+    protected function circleToPrincipal($circleUniqueId) {
529
+        if (!$this->appManager->isEnabledForUser('circles') || !class_exists('\OCA\Circles\Api\v1\Circles')) {
530
+            return null;
531
+        }
532
+
533
+        try {
534
+            $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($circleUniqueId, true);
535
+        } catch (QueryException $ex) {
536
+            return null;
537
+        } catch (CircleNotFoundException $ex) {
538
+            return null;
539
+        }
540
+
541
+        if (!$circle) {
542
+            return null;
543
+        }
544
+
545
+        $principal = [
546
+            'uri' => 'principals/circles/' . $circleUniqueId,
547
+            '{DAV:}displayname' => $circle->getDisplayName(),
548
+        ];
549
+
550
+        return $principal;
551
+    }
552
+
553
+    /**
554
+     * Returns the list of circles a principal is a member of
555
+     *
556
+     * @param string $principal
557
+     * @return array
558
+     * @throws Exception
559
+     * @throws \OCP\AppFramework\QueryException
560
+     * @suppress PhanUndeclaredClassMethod
561
+     */
562
+    public function getCircleMembership($principal):array {
563
+        if (!$this->appManager->isEnabledForUser('circles') || !class_exists('\OCA\Circles\Api\v1\Circles')) {
564
+            return [];
565
+        }
566
+
567
+        [$prefix, $name] = \Sabre\Uri\split($principal);
568
+        if ($this->hasCircles && $prefix === $this->principalPrefix) {
569
+            $user = $this->userManager->get($name);
570
+            if (!$user) {
571
+                throw new Exception('Principal not found');
572
+            }
573
+
574
+            $circles = \OCA\Circles\Api\v1\Circles::joinedCircles($name, true);
575
+
576
+            $circles = array_map(function ($circle) {
577
+                /** @var \OCA\Circles\Model\Circle $circle */
578
+                return 'principals/circles/' . urlencode($circle->getSingleId());
579
+            }, $circles);
580
+
581
+            return $circles;
582
+        }
583
+
584
+        return [];
585
+    }
586 586
 }
Please login to merge, or discard this patch.
Spacing   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -159,7 +159,7 @@  discard block
 block discarded – undo
159 159
 
160 160
 				if ($user !== null) {
161 161
 					return [
162
-						'uri' => 'principals/users/' . $user->getUID() . '/' . $name,
162
+						'uri' => 'principals/users/'.$user->getUID().'/'.$name,
163 163
 					];
164 164
 				}
165 165
 				return null;
@@ -190,7 +190,7 @@  discard block
 block discarded – undo
190 190
 				?: $this->groupManager->get($name);
191 191
 			if ($group instanceof IGroup) {
192 192
 				return [
193
-					'uri' => 'principals/groups/' . $name,
193
+					'uri' => 'principals/groups/'.$name,
194 194
 					'{DAV:}displayname' => $group->getDisplayName(),
195 195
 				];
196 196
 			}
@@ -223,7 +223,7 @@  discard block
 block discarded – undo
223 223
 		if ($this->hasGroups || $needGroups) {
224 224
 			$userGroups = $this->groupManager->getUserGroups($user);
225 225
 			foreach ($userGroups as $userGroup) {
226
-				$groups[] = 'principals/groups/' . urlencode($userGroup->getGID());
226
+				$groups[] = 'principals/groups/'.urlencode($userGroup->getGID());
227 227
 			}
228 228
 		}
229 229
 
@@ -294,7 +294,7 @@  discard block
 block discarded – undo
294 294
 					if (!$allowEnumeration) {
295 295
 						if ($allowEnumerationFullMatch) {
296 296
 							$users = $this->userManager->getByEmail($value);
297
-							$users = \array_filter($users, static function (IUser $user) use ($value) {
297
+							$users = \array_filter($users, static function(IUser $user) use ($value) {
298 298
 								return $user->getEMailAddress() === $value;
299 299
 							});
300 300
 						} else {
@@ -302,7 +302,7 @@  discard block
 block discarded – undo
302 302
 						}
303 303
 					} else {
304 304
 						$users = $this->userManager->getByEmail($value);
305
-						$users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $allowEnumerationFullMatch, $currentUserGroups) {
305
+						$users = \array_filter($users, function(IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $allowEnumerationFullMatch, $currentUserGroups) {
306 306
 							if ($allowEnumerationFullMatch && $user->getEMailAddress() === $value) {
307 307
 								return true;
308 308
 							}
@@ -326,7 +326,7 @@  discard block
 block discarded – undo
326 326
 						});
327 327
 					}
328 328
 
329
-					$results[] = array_reduce($users, function (array $carry, IUser $user) use ($restrictGroups) {
329
+					$results[] = array_reduce($users, function(array $carry, IUser $user) use ($restrictGroups) {
330 330
 						// is sharing restricted to groups only?
331 331
 						if ($restrictGroups !== false) {
332 332
 							$userGroups = $this->groupManager->getUserGroupIds($user);
@@ -335,7 +335,7 @@  discard block
 block discarded – undo
335 335
 							}
336 336
 						}
337 337
 
338
-						$carry[] = $this->principalPrefix . '/' . $user->getUID();
338
+						$carry[] = $this->principalPrefix.'/'.$user->getUID();
339 339
 						return $carry;
340 340
 					}, []);
341 341
 					break;
@@ -345,7 +345,7 @@  discard block
 block discarded – undo
345 345
 					if (!$allowEnumeration) {
346 346
 						if ($allowEnumerationFullMatch) {
347 347
 							$users = $this->userManager->searchDisplayName($value, $searchLimit);
348
-							$users = \array_filter($users, static function (IUser $user) use ($value) {
348
+							$users = \array_filter($users, static function(IUser $user) use ($value) {
349 349
 								return $user->getDisplayName() === $value;
350 350
 							});
351 351
 						} else {
@@ -353,7 +353,7 @@  discard block
 block discarded – undo
353 353
 						}
354 354
 					} else {
355 355
 						$users = $this->userManager->searchDisplayName($value, $searchLimit);
356
-						$users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $allowEnumerationFullMatch, $currentUserGroups) {
356
+						$users = \array_filter($users, function(IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $allowEnumerationFullMatch, $currentUserGroups) {
357 357
 							if ($allowEnumerationFullMatch && $user->getDisplayName() === $value) {
358 358
 								return true;
359 359
 							}
@@ -377,7 +377,7 @@  discard block
 block discarded – undo
377 377
 						});
378 378
 					}
379 379
 
380
-					$results[] = array_reduce($users, function (array $carry, IUser $user) use ($restrictGroups) {
380
+					$results[] = array_reduce($users, function(array $carry, IUser $user) use ($restrictGroups) {
381 381
 						// is sharing restricted to groups only?
382 382
 						if ($restrictGroups !== false) {
383 383
 							$userGroups = $this->groupManager->getUserGroupIds($user);
@@ -386,7 +386,7 @@  discard block
 block discarded – undo
386 386
 							}
387 387
 						}
388 388
 
389
-						$carry[] = $this->principalPrefix . '/' . $user->getUID();
389
+						$carry[] = $this->principalPrefix.'/'.$user->getUID();
390 390
 						return $carry;
391 391
 					}, []);
392 392
 					break;
@@ -482,7 +482,7 @@  discard block
 block discarded – undo
482 482
 					}
483 483
 				}
484 484
 
485
-				return $this->principalPrefix . '/' . $user->getUID();
485
+				return $this->principalPrefix.'/'.$user->getUID();
486 486
 			}
487 487
 		}
488 488
 		if (substr($uri, 0, 10) === 'principal:') {
@@ -504,7 +504,7 @@  discard block
 block discarded – undo
504 504
 		$userId = $user->getUID();
505 505
 		$displayName = $user->getDisplayName();
506 506
 		$principal = [
507
-			'uri' => $this->principalPrefix . '/' . $userId,
507
+			'uri' => $this->principalPrefix.'/'.$userId,
508 508
 			'{DAV:}displayname' => is_null($displayName) ? $userId : $displayName,
509 509
 			'{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL',
510 510
 		];
@@ -543,7 +543,7 @@  discard block
 block discarded – undo
543 543
 		}
544 544
 
545 545
 		$principal = [
546
-			'uri' => 'principals/circles/' . $circleUniqueId,
546
+			'uri' => 'principals/circles/'.$circleUniqueId,
547 547
 			'{DAV:}displayname' => $circle->getDisplayName(),
548 548
 		];
549 549
 
@@ -573,9 +573,9 @@  discard block
 block discarded – undo
573 573
 
574 574
 			$circles = \OCA\Circles\Api\v1\Circles::joinedCircles($name, true);
575 575
 
576
-			$circles = array_map(function ($circle) {
576
+			$circles = array_map(function($circle) {
577 577
 				/** @var \OCA\Circles\Model\Circle $circle */
578
-				return 'principals/circles/' . urlencode($circle->getSingleId());
578
+				return 'principals/circles/'.urlencode($circle->getSingleId());
579 579
 			}, $circles);
580 580
 
581 581
 			return $circles;
Please login to merge, or discard this patch.