Completed
Push — master ( 7144d7...2f1209 )
by Morris
16:06
created
apps/user_ldap/lib/Group_LDAP.php 2 patches
Indentation   +1140 added lines, -1140 removed lines patch added patch discarded remove patch
@@ -45,1145 +45,1145 @@
 block discarded – undo
45 45
 use OCP\GroupInterface;
46 46
 
47 47
 class Group_LDAP extends BackendUtility implements \OCP\GroupInterface, IGroupLDAP {
48
-	protected $enabled = false;
49
-
50
-	/**
51
-	 * @var string[] $cachedGroupMembers array of users with gid as key
52
-	 */
53
-	protected $cachedGroupMembers;
54
-
55
-	/**
56
-	 * @var string[] $cachedGroupsByMember array of groups with uid as key
57
-	 */
58
-	protected $cachedGroupsByMember;
59
-
60
-	/** @var GroupPluginManager */
61
-	protected $groupPluginManager;
62
-
63
-	public function __construct(Access $access, GroupPluginManager $groupPluginManager) {
64
-		parent::__construct($access);
65
-		$filter = $this->access->connection->ldapGroupFilter;
66
-		$gassoc = $this->access->connection->ldapGroupMemberAssocAttr;
67
-		if(!empty($filter) && !empty($gassoc)) {
68
-			$this->enabled = true;
69
-		}
70
-
71
-		$this->cachedGroupMembers = new CappedMemoryCache();
72
-		$this->cachedGroupsByMember = new CappedMemoryCache();
73
-		$this->groupPluginManager = $groupPluginManager;
74
-	}
75
-
76
-	/**
77
-	 * is user in group?
78
-	 * @param string $uid uid of the user
79
-	 * @param string $gid gid of the group
80
-	 * @return bool
81
-	 *
82
-	 * Checks whether the user is member of a group or not.
83
-	 */
84
-	public function inGroup($uid, $gid) {
85
-		if(!$this->enabled) {
86
-			return false;
87
-		}
88
-		$cacheKey = 'inGroup'.$uid.':'.$gid;
89
-		$inGroup = $this->access->connection->getFromCache($cacheKey);
90
-		if(!is_null($inGroup)) {
91
-			return (bool)$inGroup;
92
-		}
93
-
94
-		$userDN = $this->access->username2dn($uid);
95
-
96
-		if(isset($this->cachedGroupMembers[$gid])) {
97
-			$isInGroup = in_array($userDN, $this->cachedGroupMembers[$gid]);
98
-			return $isInGroup;
99
-		}
100
-
101
-		$cacheKeyMembers = 'inGroup-members:'.$gid;
102
-		$members = $this->access->connection->getFromCache($cacheKeyMembers);
103
-		if(!is_null($members)) {
104
-			$this->cachedGroupMembers[$gid] = $members;
105
-			$isInGroup = in_array($userDN, $members);
106
-			$this->access->connection->writeToCache($cacheKey, $isInGroup);
107
-			return $isInGroup;
108
-		}
109
-
110
-		$groupDN = $this->access->groupname2dn($gid);
111
-		// just in case
112
-		if(!$groupDN || !$userDN) {
113
-			$this->access->connection->writeToCache($cacheKey, false);
114
-			return false;
115
-		}
116
-
117
-		//check primary group first
118
-		if($gid === $this->getUserPrimaryGroup($userDN)) {
119
-			$this->access->connection->writeToCache($cacheKey, true);
120
-			return true;
121
-		}
122
-
123
-		//usually, LDAP attributes are said to be case insensitive. But there are exceptions of course.
124
-		$members = $this->_groupMembers($groupDN);
125
-		$members = array_keys($members); // uids are returned as keys
126
-		if(!is_array($members) || count($members) === 0) {
127
-			$this->access->connection->writeToCache($cacheKey, false);
128
-			return false;
129
-		}
130
-
131
-		//extra work if we don't get back user DNs
132
-		if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
133
-			$dns = array();
134
-			$filterParts = array();
135
-			$bytes = 0;
136
-			foreach($members as $mid) {
137
-				$filter = str_replace('%uid', $mid, $this->access->connection->ldapLoginFilter);
138
-				$filterParts[] = $filter;
139
-				$bytes += strlen($filter);
140
-				if($bytes >= 9000000) {
141
-					// AD has a default input buffer of 10 MB, we do not want
142
-					// to take even the chance to exceed it
143
-					$filter = $this->access->combineFilterWithOr($filterParts);
144
-					$bytes = 0;
145
-					$filterParts = array();
146
-					$users = $this->access->fetchListOfUsers($filter, 'dn', count($filterParts));
147
-					$dns = array_merge($dns, $users);
148
-				}
149
-			}
150
-			if(count($filterParts) > 0) {
151
-				$filter = $this->access->combineFilterWithOr($filterParts);
152
-				$users = $this->access->fetchListOfUsers($filter, 'dn', count($filterParts));
153
-				$dns = array_merge($dns, $users);
154
-			}
155
-			$members = $dns;
156
-		}
157
-
158
-		$isInGroup = in_array($userDN, $members);
159
-		$this->access->connection->writeToCache($cacheKey, $isInGroup);
160
-		$this->access->connection->writeToCache($cacheKeyMembers, $members);
161
-		$this->cachedGroupMembers[$gid] = $members;
162
-
163
-		return $isInGroup;
164
-	}
165
-
166
-	/**
167
-	 * @param string $dnGroup
168
-	 * @return array
169
-	 *
170
-	 * For a group that has user membership defined by an LDAP search url attribute returns the users
171
-	 * that match the search url otherwise returns an empty array.
172
-	 */
173
-	public function getDynamicGroupMembers($dnGroup) {
174
-		$dynamicGroupMemberURL = strtolower($this->access->connection->ldapDynamicGroupMemberURL);
175
-
176
-		if (empty($dynamicGroupMemberURL)) {
177
-			return array();
178
-		}
179
-
180
-		$dynamicMembers = array();
181
-		$memberURLs = $this->access->readAttribute(
182
-			$dnGroup,
183
-			$dynamicGroupMemberURL,
184
-			$this->access->connection->ldapGroupFilter
185
-		);
186
-		if ($memberURLs !== false) {
187
-			// this group has the 'memberURL' attribute so this is a dynamic group
188
-			// example 1: ldap:///cn=users,cn=accounts,dc=dcsubbase,dc=dcbase??one?(o=HeadOffice)
189
-			// example 2: ldap:///cn=users,cn=accounts,dc=dcsubbase,dc=dcbase??one?(&(o=HeadOffice)(uidNumber>=500))
190
-			$pos = strpos($memberURLs[0], '(');
191
-			if ($pos !== false) {
192
-				$memberUrlFilter = substr($memberURLs[0], $pos);
193
-				$foundMembers = $this->access->searchUsers($memberUrlFilter,'dn');
194
-				$dynamicMembers = array();
195
-				foreach($foundMembers as $value) {
196
-					$dynamicMembers[$value['dn'][0]] = 1;
197
-				}
198
-			} else {
199
-				\OCP\Util::writeLog('user_ldap', 'No search filter found on member url '.
200
-					'of group ' . $dnGroup, \OCP\Util::DEBUG);
201
-			}
202
-		}
203
-		return $dynamicMembers;
204
-	}
205
-
206
-	/**
207
-	 * @param string $dnGroup
208
-	 * @param array|null &$seen
209
-	 * @return array|mixed|null
210
-	 * @throws \OC\ServerNotAvailableException
211
-	 */
212
-	private function _groupMembers($dnGroup, &$seen = null) {
213
-		if ($seen === null) {
214
-			$seen = array();
215
-		}
216
-		$allMembers = array();
217
-		if (array_key_exists($dnGroup, $seen)) {
218
-			// avoid loops
219
-			return array();
220
-		}
221
-		// used extensively in cron job, caching makes sense for nested groups
222
-		$cacheKey = '_groupMembers'.$dnGroup;
223
-		$groupMembers = $this->access->connection->getFromCache($cacheKey);
224
-		if($groupMembers !== null) {
225
-			return $groupMembers;
226
-		}
227
-		$seen[$dnGroup] = 1;
228
-		$members = $this->access->readAttribute($dnGroup, $this->access->connection->ldapGroupMemberAssocAttr,
229
-												$this->access->connection->ldapGroupFilter);
230
-		if (is_array($members)) {
231
-			foreach ($members as $member) {
232
-				$allMembers[$member] = 1;
233
-				$nestedGroups = $this->access->connection->ldapNestedGroups;
234
-				if (!empty($nestedGroups)) {
235
-					$subMembers = $this->_groupMembers($member, $seen);
236
-					if ($subMembers) {
237
-						$allMembers += $subMembers;
238
-					}
239
-				}
240
-			}
241
-		}
242
-
243
-		$allMembers += $this->getDynamicGroupMembers($dnGroup);
244
-
245
-		$this->access->connection->writeToCache($cacheKey, $allMembers);
246
-		return $allMembers;
247
-	}
248
-
249
-	/**
250
-	 * @param string $DN
251
-	 * @param array|null &$seen
252
-	 * @return array
253
-	 */
254
-	private function _getGroupDNsFromMemberOf($DN, &$seen = null) {
255
-		if ($seen === null) {
256
-			$seen = array();
257
-		}
258
-		if (array_key_exists($DN, $seen)) {
259
-			// avoid loops
260
-			return array();
261
-		}
262
-		$seen[$DN] = 1;
263
-		$groups = $this->access->readAttribute($DN, 'memberOf');
264
-		if (!is_array($groups)) {
265
-			return array();
266
-		}
267
-		$groups = $this->access->groupsMatchFilter($groups);
268
-		$allGroups =  $groups;
269
-		$nestedGroups = $this->access->connection->ldapNestedGroups;
270
-		if ((int)$nestedGroups === 1) {
271
-			foreach ($groups as $group) {
272
-				$subGroups = $this->_getGroupDNsFromMemberOf($group, $seen);
273
-				$allGroups = array_merge($allGroups, $subGroups);
274
-			}
275
-		}
276
-		return $allGroups;
277
-	}
278
-
279
-	/**
280
-	 * translates a gidNumber into an ownCloud internal name
281
-	 * @param string $gid as given by gidNumber on POSIX LDAP
282
-	 * @param string $dn a DN that belongs to the same domain as the group
283
-	 * @return string|bool
284
-	 */
285
-	public function gidNumber2Name($gid, $dn) {
286
-		$cacheKey = 'gidNumberToName' . $gid;
287
-		$groupName = $this->access->connection->getFromCache($cacheKey);
288
-		if(!is_null($groupName) && isset($groupName)) {
289
-			return $groupName;
290
-		}
291
-
292
-		//we need to get the DN from LDAP
293
-		$filter = $this->access->combineFilterWithAnd([
294
-			$this->access->connection->ldapGroupFilter,
295
-			'objectClass=posixGroup',
296
-			$this->access->connection->ldapGidNumber . '=' . $gid
297
-		]);
298
-		$result = $this->access->searchGroups($filter, array('dn'), 1);
299
-		if(empty($result)) {
300
-			return false;
301
-		}
302
-		$dn = $result[0]['dn'][0];
303
-
304
-		//and now the group name
305
-		//NOTE once we have separate ownCloud group IDs and group names we can
306
-		//directly read the display name attribute instead of the DN
307
-		$name = $this->access->dn2groupname($dn);
308
-
309
-		$this->access->connection->writeToCache($cacheKey, $name);
310
-
311
-		return $name;
312
-	}
313
-
314
-	/**
315
-	 * returns the entry's gidNumber
316
-	 * @param string $dn
317
-	 * @param string $attribute
318
-	 * @return string|bool
319
-	 */
320
-	private function getEntryGidNumber($dn, $attribute) {
321
-		$value = $this->access->readAttribute($dn, $attribute);
322
-		if(is_array($value) && !empty($value)) {
323
-			return $value[0];
324
-		}
325
-		return false;
326
-	}
327
-
328
-	/**
329
-	 * returns the group's primary ID
330
-	 * @param string $dn
331
-	 * @return string|bool
332
-	 */
333
-	public function getGroupGidNumber($dn) {
334
-		return $this->getEntryGidNumber($dn, 'gidNumber');
335
-	}
336
-
337
-	/**
338
-	 * returns the user's gidNumber
339
-	 * @param string $dn
340
-	 * @return string|bool
341
-	 */
342
-	public function getUserGidNumber($dn) {
343
-		$gidNumber = false;
344
-		if($this->access->connection->hasGidNumber) {
345
-			$gidNumber = $this->getEntryGidNumber($dn, $this->access->connection->ldapGidNumber);
346
-			if($gidNumber === false) {
347
-				$this->access->connection->hasGidNumber = false;
348
-			}
349
-		}
350
-		return $gidNumber;
351
-	}
352
-
353
-	/**
354
-	 * returns a filter for a "users has specific gid" search or count operation
355
-	 *
356
-	 * @param string $groupDN
357
-	 * @param string $search
358
-	 * @return string
359
-	 * @throws \Exception
360
-	 */
361
-	private function prepareFilterForUsersHasGidNumber($groupDN, $search = '') {
362
-		$groupID = $this->getGroupGidNumber($groupDN);
363
-		if($groupID === false) {
364
-			throw new \Exception('Not a valid group');
365
-		}
366
-
367
-		$filterParts = [];
368
-		$filterParts[] = $this->access->getFilterForUserCount();
369
-		if ($search !== '') {
370
-			$filterParts[] = $this->access->getFilterPartForUserSearch($search);
371
-		}
372
-		$filterParts[] = $this->access->connection->ldapGidNumber .'=' . $groupID;
373
-
374
-		return $this->access->combineFilterWithAnd($filterParts);
375
-	}
376
-
377
-	/**
378
-	 * returns a list of users that have the given group as gid number
379
-	 *
380
-	 * @param string $groupDN
381
-	 * @param string $search
382
-	 * @param int $limit
383
-	 * @param int $offset
384
-	 * @return string[]
385
-	 */
386
-	public function getUsersInGidNumber($groupDN, $search = '', $limit = -1, $offset = 0) {
387
-		try {
388
-			$filter = $this->prepareFilterForUsersHasGidNumber($groupDN, $search);
389
-			$users = $this->access->fetchListOfUsers(
390
-				$filter,
391
-				[$this->access->connection->ldapUserDisplayName, 'dn'],
392
-				$limit,
393
-				$offset
394
-			);
395
-			return $this->access->nextcloudUserNames($users);
396
-		} catch (\Exception $e) {
397
-			return [];
398
-		}
399
-	}
400
-
401
-	/**
402
-	 * returns the number of users that have the given group as gid number
403
-	 *
404
-	 * @param string $groupDN
405
-	 * @param string $search
406
-	 * @param int $limit
407
-	 * @param int $offset
408
-	 * @return int
409
-	 */
410
-	public function countUsersInGidNumber($groupDN, $search = '', $limit = -1, $offset = 0) {
411
-		try {
412
-			$filter = $this->prepareFilterForUsersHasGidNumber($groupDN, $search);
413
-			$users = $this->access->countUsers($filter, ['dn'], $limit, $offset);
414
-			return (int)$users;
415
-		} catch (\Exception $e) {
416
-			return 0;
417
-		}
418
-	}
419
-
420
-	/**
421
-	 * gets the gidNumber of a user
422
-	 * @param string $dn
423
-	 * @return string
424
-	 */
425
-	public function getUserGroupByGid($dn) {
426
-		$groupID = $this->getUserGidNumber($dn);
427
-		if($groupID !== false) {
428
-			$groupName = $this->gidNumber2Name($groupID, $dn);
429
-			if($groupName !== false) {
430
-				return $groupName;
431
-			}
432
-		}
433
-
434
-		return false;
435
-	}
436
-
437
-	/**
438
-	 * translates a primary group ID into an Nextcloud internal name
439
-	 * @param string $gid as given by primaryGroupID on AD
440
-	 * @param string $dn a DN that belongs to the same domain as the group
441
-	 * @return string|bool
442
-	 */
443
-	public function primaryGroupID2Name($gid, $dn) {
444
-		$cacheKey = 'primaryGroupIDtoName';
445
-		$groupNames = $this->access->connection->getFromCache($cacheKey);
446
-		if(!is_null($groupNames) && isset($groupNames[$gid])) {
447
-			return $groupNames[$gid];
448
-		}
449
-
450
-		$domainObjectSid = $this->access->getSID($dn);
451
-		if($domainObjectSid === false) {
452
-			return false;
453
-		}
454
-
455
-		//we need to get the DN from LDAP
456
-		$filter = $this->access->combineFilterWithAnd(array(
457
-			$this->access->connection->ldapGroupFilter,
458
-			'objectsid=' . $domainObjectSid . '-' . $gid
459
-		));
460
-		$result = $this->access->searchGroups($filter, array('dn'), 1);
461
-		if(empty($result)) {
462
-			return false;
463
-		}
464
-		$dn = $result[0]['dn'][0];
465
-
466
-		//and now the group name
467
-		//NOTE once we have separate Nextcloud group IDs and group names we can
468
-		//directly read the display name attribute instead of the DN
469
-		$name = $this->access->dn2groupname($dn);
470
-
471
-		$this->access->connection->writeToCache($cacheKey, $name);
472
-
473
-		return $name;
474
-	}
475
-
476
-	/**
477
-	 * returns the entry's primary group ID
478
-	 * @param string $dn
479
-	 * @param string $attribute
480
-	 * @return string|bool
481
-	 */
482
-	private function getEntryGroupID($dn, $attribute) {
483
-		$value = $this->access->readAttribute($dn, $attribute);
484
-		if(is_array($value) && !empty($value)) {
485
-			return $value[0];
486
-		}
487
-		return false;
488
-	}
489
-
490
-	/**
491
-	 * returns the group's primary ID
492
-	 * @param string $dn
493
-	 * @return string|bool
494
-	 */
495
-	public function getGroupPrimaryGroupID($dn) {
496
-		return $this->getEntryGroupID($dn, 'primaryGroupToken');
497
-	}
498
-
499
-	/**
500
-	 * returns the user's primary group ID
501
-	 * @param string $dn
502
-	 * @return string|bool
503
-	 */
504
-	public function getUserPrimaryGroupIDs($dn) {
505
-		$primaryGroupID = false;
506
-		if($this->access->connection->hasPrimaryGroups) {
507
-			$primaryGroupID = $this->getEntryGroupID($dn, 'primaryGroupID');
508
-			if($primaryGroupID === false) {
509
-				$this->access->connection->hasPrimaryGroups = false;
510
-			}
511
-		}
512
-		return $primaryGroupID;
513
-	}
514
-
515
-	/**
516
-	 * returns a filter for a "users in primary group" search or count operation
517
-	 *
518
-	 * @param string $groupDN
519
-	 * @param string $search
520
-	 * @return string
521
-	 * @throws \Exception
522
-	 */
523
-	private function prepareFilterForUsersInPrimaryGroup($groupDN, $search = '') {
524
-		$groupID = $this->getGroupPrimaryGroupID($groupDN);
525
-		if($groupID === false) {
526
-			throw new \Exception('Not a valid group');
527
-		}
528
-
529
-		$filterParts = [];
530
-		$filterParts[] = $this->access->getFilterForUserCount();
531
-		if ($search !== '') {
532
-			$filterParts[] = $this->access->getFilterPartForUserSearch($search);
533
-		}
534
-		$filterParts[] = 'primaryGroupID=' . $groupID;
535
-
536
-		return $this->access->combineFilterWithAnd($filterParts);
537
-	}
538
-
539
-	/**
540
-	 * returns a list of users that have the given group as primary group
541
-	 *
542
-	 * @param string $groupDN
543
-	 * @param string $search
544
-	 * @param int $limit
545
-	 * @param int $offset
546
-	 * @return string[]
547
-	 */
548
-	public function getUsersInPrimaryGroup($groupDN, $search = '', $limit = -1, $offset = 0) {
549
-		try {
550
-			$filter = $this->prepareFilterForUsersInPrimaryGroup($groupDN, $search);
551
-			$users = $this->access->fetchListOfUsers(
552
-				$filter,
553
-				array($this->access->connection->ldapUserDisplayName, 'dn'),
554
-				$limit,
555
-				$offset
556
-			);
557
-			return $this->access->nextcloudUserNames($users);
558
-		} catch (\Exception $e) {
559
-			return array();
560
-		}
561
-	}
562
-
563
-	/**
564
-	 * returns the number of users that have the given group as primary group
565
-	 *
566
-	 * @param string $groupDN
567
-	 * @param string $search
568
-	 * @param int $limit
569
-	 * @param int $offset
570
-	 * @return int
571
-	 */
572
-	public function countUsersInPrimaryGroup($groupDN, $search = '', $limit = -1, $offset = 0) {
573
-		try {
574
-			$filter = $this->prepareFilterForUsersInPrimaryGroup($groupDN, $search);
575
-			$users = $this->access->countUsers($filter, array('dn'), $limit, $offset);
576
-			return (int)$users;
577
-		} catch (\Exception $e) {
578
-			return 0;
579
-		}
580
-	}
581
-
582
-	/**
583
-	 * gets the primary group of a user
584
-	 * @param string $dn
585
-	 * @return string
586
-	 */
587
-	public function getUserPrimaryGroup($dn) {
588
-		$groupID = $this->getUserPrimaryGroupIDs($dn);
589
-		if($groupID !== false) {
590
-			$groupName = $this->primaryGroupID2Name($groupID, $dn);
591
-			if($groupName !== false) {
592
-				return $groupName;
593
-			}
594
-		}
595
-
596
-		return false;
597
-	}
598
-
599
-	/**
600
-	 * Get all groups a user belongs to
601
-	 * @param string $uid Name of the user
602
-	 * @return array with group names
603
-	 *
604
-	 * This function fetches all groups a user belongs to. It does not check
605
-	 * if the user exists at all.
606
-	 *
607
-	 * This function includes groups based on dynamic group membership.
608
-	 */
609
-	public function getUserGroups($uid) {
610
-		if(!$this->enabled) {
611
-			return array();
612
-		}
613
-		$cacheKey = 'getUserGroups'.$uid;
614
-		$userGroups = $this->access->connection->getFromCache($cacheKey);
615
-		if(!is_null($userGroups)) {
616
-			return $userGroups;
617
-		}
618
-		$userDN = $this->access->username2dn($uid);
619
-		if(!$userDN) {
620
-			$this->access->connection->writeToCache($cacheKey, array());
621
-			return array();
622
-		}
623
-
624
-		$groups = [];
625
-		$primaryGroup = $this->getUserPrimaryGroup($userDN);
626
-		$gidGroupName = $this->getUserGroupByGid($userDN);
627
-
628
-		$dynamicGroupMemberURL = strtolower($this->access->connection->ldapDynamicGroupMemberURL);
629
-
630
-		if (!empty($dynamicGroupMemberURL)) {
631
-			// look through dynamic groups to add them to the result array if needed
632
-			$groupsToMatch = $this->access->fetchListOfGroups(
633
-				$this->access->connection->ldapGroupFilter,array('dn',$dynamicGroupMemberURL));
634
-			foreach($groupsToMatch as $dynamicGroup) {
635
-				if (!array_key_exists($dynamicGroupMemberURL, $dynamicGroup)) {
636
-					continue;
637
-				}
638
-				$pos = strpos($dynamicGroup[$dynamicGroupMemberURL][0], '(');
639
-				if ($pos !== false) {
640
-					$memberUrlFilter = substr($dynamicGroup[$dynamicGroupMemberURL][0],$pos);
641
-					// apply filter via ldap search to see if this user is in this
642
-					// dynamic group
643
-					$userMatch = $this->access->readAttribute(
644
-						$userDN,
645
-						$this->access->connection->ldapUserDisplayName,
646
-						$memberUrlFilter
647
-					);
648
-					if ($userMatch !== false) {
649
-						// match found so this user is in this group
650
-						$groupName = $this->access->dn2groupname($dynamicGroup['dn'][0]);
651
-						if(is_string($groupName)) {
652
-							// be sure to never return false if the dn could not be
653
-							// resolved to a name, for whatever reason.
654
-							$groups[] = $groupName;
655
-						}
656
-					}
657
-				} else {
658
-					\OCP\Util::writeLog('user_ldap', 'No search filter found on member url '.
659
-						'of group ' . print_r($dynamicGroup, true), \OCP\Util::DEBUG);
660
-				}
661
-			}
662
-		}
663
-
664
-		// if possible, read out membership via memberOf. It's far faster than
665
-		// performing a search, which still is a fallback later.
666
-		// memberof doesn't support memberuid, so skip it here.
667
-		if((int)$this->access->connection->hasMemberOfFilterSupport === 1
668
-			&& (int)$this->access->connection->useMemberOfToDetectMembership === 1
669
-		    && strtolower($this->access->connection->ldapGroupMemberAssocAttr) !== 'memberuid'
670
-		    ) {
671
-			$groupDNs = $this->_getGroupDNsFromMemberOf($userDN);
672
-			if (is_array($groupDNs)) {
673
-				foreach ($groupDNs as $dn) {
674
-					$groupName = $this->access->dn2groupname($dn);
675
-					if(is_string($groupName)) {
676
-						// be sure to never return false if the dn could not be
677
-						// resolved to a name, for whatever reason.
678
-						$groups[] = $groupName;
679
-					}
680
-				}
681
-			}
682
-
683
-			if($primaryGroup !== false) {
684
-				$groups[] = $primaryGroup;
685
-			}
686
-			if($gidGroupName !== false) {
687
-				$groups[] = $gidGroupName;
688
-			}
689
-			$this->access->connection->writeToCache($cacheKey, $groups);
690
-			return $groups;
691
-		}
692
-
693
-		//uniqueMember takes DN, memberuid the uid, so we need to distinguish
694
-		if((strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'uniquemember')
695
-			|| (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'member')
696
-		) {
697
-			$uid = $userDN;
698
-		} else if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
699
-			$result = $this->access->readAttribute($userDN, 'uid');
700
-			if ($result === false) {
701
-				\OCP\Util::writeLog('user_ldap', 'No uid attribute found for DN ' . $userDN . ' on '.
702
-					$this->access->connection->ldapHost, \OCP\Util::DEBUG);
703
-			}
704
-			$uid = $result[0];
705
-		} else {
706
-			// just in case
707
-			$uid = $userDN;
708
-		}
709
-
710
-		if(isset($this->cachedGroupsByMember[$uid])) {
711
-			$groups = array_merge($groups, $this->cachedGroupsByMember[$uid]);
712
-		} else {
713
-			$groupsByMember = array_values($this->getGroupsByMember($uid));
714
-			$groupsByMember = $this->access->nextcloudGroupNames($groupsByMember);
715
-			$this->cachedGroupsByMember[$uid] = $groupsByMember;
716
-			$groups = array_merge($groups, $groupsByMember);
717
-		}
718
-
719
-		if($primaryGroup !== false) {
720
-			$groups[] = $primaryGroup;
721
-		}
722
-		if($gidGroupName !== false) {
723
-			$groups[] = $gidGroupName;
724
-		}
725
-
726
-		$groups = array_unique($groups, SORT_LOCALE_STRING);
727
-		$this->access->connection->writeToCache($cacheKey, $groups);
728
-
729
-		return $groups;
730
-	}
731
-
732
-	/**
733
-	 * @param string $dn
734
-	 * @param array|null &$seen
735
-	 * @return array
736
-	 */
737
-	private function getGroupsByMember($dn, &$seen = null) {
738
-		if ($seen === null) {
739
-			$seen = array();
740
-		}
741
-		$allGroups = array();
742
-		if (array_key_exists($dn, $seen)) {
743
-			// avoid loops
744
-			return array();
745
-		}
746
-		$seen[$dn] = true;
747
-		$filter = $this->access->combineFilterWithAnd(array(
748
-			$this->access->connection->ldapGroupFilter,
749
-			$this->access->connection->ldapGroupMemberAssocAttr.'='.$dn
750
-		));
751
-		$groups = $this->access->fetchListOfGroups($filter,
752
-			array($this->access->connection->ldapGroupDisplayName, 'dn'));
753
-		if (is_array($groups)) {
754
-			foreach ($groups as $groupobj) {
755
-				$groupDN = $groupobj['dn'][0];
756
-				$allGroups[$groupDN] = $groupobj;
757
-				$nestedGroups = $this->access->connection->ldapNestedGroups;
758
-				if (!empty($nestedGroups)) {
759
-					$supergroups = $this->getGroupsByMember($groupDN, $seen);
760
-					if (is_array($supergroups) && (count($supergroups)>0)) {
761
-						$allGroups = array_merge($allGroups, $supergroups);
762
-					}
763
-				}
764
-			}
765
-		}
766
-		return $allGroups;
767
-	}
768
-
769
-	/**
770
-	 * get a list of all users in a group
771
-	 *
772
-	 * @param string $gid
773
-	 * @param string $search
774
-	 * @param int $limit
775
-	 * @param int $offset
776
-	 * @return array with user ids
777
-	 */
778
-	public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
779
-		if(!$this->enabled) {
780
-			return array();
781
-		}
782
-		if(!$this->groupExists($gid)) {
783
-			return array();
784
-		}
785
-		$search = $this->access->escapeFilterPart($search, true);
786
-		$cacheKey = 'usersInGroup-'.$gid.'-'.$search.'-'.$limit.'-'.$offset;
787
-		// check for cache of the exact query
788
-		$groupUsers = $this->access->connection->getFromCache($cacheKey);
789
-		if(!is_null($groupUsers)) {
790
-			return $groupUsers;
791
-		}
792
-
793
-		// check for cache of the query without limit and offset
794
-		$groupUsers = $this->access->connection->getFromCache('usersInGroup-'.$gid.'-'.$search);
795
-		if(!is_null($groupUsers)) {
796
-			$groupUsers = array_slice($groupUsers, $offset, $limit);
797
-			$this->access->connection->writeToCache($cacheKey, $groupUsers);
798
-			return $groupUsers;
799
-		}
800
-
801
-		if($limit === -1) {
802
-			$limit = null;
803
-		}
804
-		$groupDN = $this->access->groupname2dn($gid);
805
-		if(!$groupDN) {
806
-			// group couldn't be found, return empty resultset
807
-			$this->access->connection->writeToCache($cacheKey, array());
808
-			return array();
809
-		}
810
-
811
-		$primaryUsers = $this->getUsersInPrimaryGroup($groupDN, $search, $limit, $offset);
812
-		$posixGroupUsers = $this->getUsersInGidNumber($groupDN, $search, $limit, $offset);
813
-		$members = array_keys($this->_groupMembers($groupDN));
814
-		if(!$members && empty($posixGroupUsers) && empty($primaryUsers)) {
815
-			//in case users could not be retrieved, return empty result set
816
-			$this->access->connection->writeToCache($cacheKey, []);
817
-			return [];
818
-		}
819
-
820
-		$groupUsers = array();
821
-		$isMemberUid = (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid');
822
-		$attrs = $this->access->userManager->getAttributes(true);
823
-		foreach($members as $member) {
824
-			if($isMemberUid) {
825
-				//we got uids, need to get their DNs to 'translate' them to user names
826
-				$filter = $this->access->combineFilterWithAnd(array(
827
-					str_replace('%uid', $member, $this->access->connection->ldapLoginFilter),
828
-					$this->access->getFilterPartForUserSearch($search)
829
-				));
830
-				$ldap_users = $this->access->fetchListOfUsers($filter, $attrs, 1);
831
-				if(count($ldap_users) < 1) {
832
-					continue;
833
-				}
834
-				$groupUsers[] = $this->access->dn2username($ldap_users[0]['dn'][0]);
835
-			} else {
836
-				//we got DNs, check if we need to filter by search or we can give back all of them
837
-				if ($search !== '') {
838
-					if(!$this->access->readAttribute($member,
839
-						$this->access->connection->ldapUserDisplayName,
840
-						$this->access->getFilterPartForUserSearch($search))) {
841
-						continue;
842
-					}
843
-				}
844
-				// dn2username will also check if the users belong to the allowed base
845
-				if($ocname = $this->access->dn2username($member)) {
846
-					$groupUsers[] = $ocname;
847
-				}
848
-			}
849
-		}
850
-
851
-		$groupUsers = array_unique(array_merge($groupUsers, $primaryUsers, $posixGroupUsers));
852
-		natsort($groupUsers);
853
-		$this->access->connection->writeToCache('usersInGroup-'.$gid.'-'.$search, $groupUsers);
854
-		$groupUsers = array_slice($groupUsers, $offset, $limit);
855
-
856
-		$this->access->connection->writeToCache($cacheKey, $groupUsers);
857
-
858
-		return $groupUsers;
859
-	}
860
-
861
-	/**
862
-	 * returns the number of users in a group, who match the search term
863
-	 * @param string $gid the internal group name
864
-	 * @param string $search optional, a search string
865
-	 * @return int|bool
866
-	 */
867
-	public function countUsersInGroup($gid, $search = '') {
868
-		if ($this->groupPluginManager->implementsActions(GroupInterface::COUNT_USERS)) {
869
-			return $this->groupPluginManager->countUsersInGroup($gid, $search);
870
-		}
871
-
872
-		$cacheKey = 'countUsersInGroup-'.$gid.'-'.$search;
873
-		if(!$this->enabled || !$this->groupExists($gid)) {
874
-			return false;
875
-		}
876
-		$groupUsers = $this->access->connection->getFromCache($cacheKey);
877
-		if(!is_null($groupUsers)) {
878
-			return $groupUsers;
879
-		}
880
-
881
-		$groupDN = $this->access->groupname2dn($gid);
882
-		if(!$groupDN) {
883
-			// group couldn't be found, return empty result set
884
-			$this->access->connection->writeToCache($cacheKey, false);
885
-			return false;
886
-		}
887
-
888
-		$members = array_keys($this->_groupMembers($groupDN));
889
-		$primaryUserCount = $this->countUsersInPrimaryGroup($groupDN, '');
890
-		if(!$members && $primaryUserCount === 0) {
891
-			//in case users could not be retrieved, return empty result set
892
-			$this->access->connection->writeToCache($cacheKey, false);
893
-			return false;
894
-		}
895
-
896
-		if ($search === '') {
897
-			$groupUsers = count($members) + $primaryUserCount;
898
-			$this->access->connection->writeToCache($cacheKey, $groupUsers);
899
-			return $groupUsers;
900
-		}
901
-		$search = $this->access->escapeFilterPart($search, true);
902
-		$isMemberUid =
903
-			(strtolower($this->access->connection->ldapGroupMemberAssocAttr)
904
-			=== 'memberuid');
905
-
906
-		//we need to apply the search filter
907
-		//alternatives that need to be checked:
908
-		//a) get all users by search filter and array_intersect them
909
-		//b) a, but only when less than 1k 10k ?k users like it is
910
-		//c) put all DNs|uids in a LDAP filter, combine with the search string
911
-		//   and let it count.
912
-		//For now this is not important, because the only use of this method
913
-		//does not supply a search string
914
-		$groupUsers = array();
915
-		foreach($members as $member) {
916
-			if($isMemberUid) {
917
-				//we got uids, need to get their DNs to 'translate' them to user names
918
-				$filter = $this->access->combineFilterWithAnd(array(
919
-					str_replace('%uid', $member, $this->access->connection->ldapLoginFilter),
920
-					$this->access->getFilterPartForUserSearch($search)
921
-				));
922
-				$ldap_users = $this->access->fetchListOfUsers($filter, 'dn', 1);
923
-				if(count($ldap_users) < 1) {
924
-					continue;
925
-				}
926
-				$groupUsers[] = $this->access->dn2username($ldap_users[0]);
927
-			} else {
928
-				//we need to apply the search filter now
929
-				if(!$this->access->readAttribute($member,
930
-					$this->access->connection->ldapUserDisplayName,
931
-					$this->access->getFilterPartForUserSearch($search))) {
932
-					continue;
933
-				}
934
-				// dn2username will also check if the users belong to the allowed base
935
-				if($ocname = $this->access->dn2username($member)) {
936
-					$groupUsers[] = $ocname;
937
-				}
938
-			}
939
-		}
940
-
941
-		//and get users that have the group as primary
942
-		$primaryUsers = $this->countUsersInPrimaryGroup($groupDN, $search);
943
-
944
-		return count($groupUsers) + $primaryUsers;
945
-	}
946
-
947
-	/**
948
-	 * get a list of all groups
949
-	 *
950
-	 * @param string $search
951
-	 * @param $limit
952
-	 * @param int $offset
953
-	 * @return array with group names
954
-	 *
955
-	 * Returns a list with all groups (used by getGroups)
956
-	 */
957
-	protected function getGroupsChunk($search = '', $limit = -1, $offset = 0) {
958
-		if(!$this->enabled) {
959
-			return array();
960
-		}
961
-		$cacheKey = 'getGroups-'.$search.'-'.$limit.'-'.$offset;
962
-
963
-		//Check cache before driving unnecessary searches
964
-		\OCP\Util::writeLog('user_ldap', 'getGroups '.$cacheKey, \OCP\Util::DEBUG);
965
-		$ldap_groups = $this->access->connection->getFromCache($cacheKey);
966
-		if(!is_null($ldap_groups)) {
967
-			return $ldap_groups;
968
-		}
969
-
970
-		// if we'd pass -1 to LDAP search, we'd end up in a Protocol
971
-		// error. With a limit of 0, we get 0 results. So we pass null.
972
-		if($limit <= 0) {
973
-			$limit = null;
974
-		}
975
-		$filter = $this->access->combineFilterWithAnd(array(
976
-			$this->access->connection->ldapGroupFilter,
977
-			$this->access->getFilterPartForGroupSearch($search)
978
-		));
979
-		\OCP\Util::writeLog('user_ldap', 'getGroups Filter '.$filter, \OCP\Util::DEBUG);
980
-		$ldap_groups = $this->access->fetchListOfGroups($filter,
981
-				array($this->access->connection->ldapGroupDisplayName, 'dn'),
982
-				$limit,
983
-				$offset);
984
-		$ldap_groups = $this->access->nextcloudGroupNames($ldap_groups);
985
-
986
-		$this->access->connection->writeToCache($cacheKey, $ldap_groups);
987
-		return $ldap_groups;
988
-	}
989
-
990
-	/**
991
-	 * get a list of all groups using a paged search
992
-	 *
993
-	 * @param string $search
994
-	 * @param int $limit
995
-	 * @param int $offset
996
-	 * @return array with group names
997
-	 *
998
-	 * Returns a list with all groups
999
-	 * Uses a paged search if available to override a
1000
-	 * server side search limit.
1001
-	 * (active directory has a limit of 1000 by default)
1002
-	 */
1003
-	public function getGroups($search = '', $limit = -1, $offset = 0) {
1004
-		if(!$this->enabled) {
1005
-			return array();
1006
-		}
1007
-		$search = $this->access->escapeFilterPart($search, true);
1008
-		$pagingSize = (int)$this->access->connection->ldapPagingSize;
1009
-		if (!$this->access->connection->hasPagedResultSupport || $pagingSize <= 0) {
1010
-			return $this->getGroupsChunk($search, $limit, $offset);
1011
-		}
1012
-		$maxGroups = 100000; // limit max results (just for safety reasons)
1013
-		if ($limit > -1) {
1014
-		   $overallLimit = min($limit + $offset, $maxGroups);
1015
-		} else {
1016
-		   $overallLimit = $maxGroups;
1017
-		}
1018
-		$chunkOffset = $offset;
1019
-		$allGroups = array();
1020
-		while ($chunkOffset < $overallLimit) {
1021
-			$chunkLimit = min($pagingSize, $overallLimit - $chunkOffset);
1022
-			$ldapGroups = $this->getGroupsChunk($search, $chunkLimit, $chunkOffset);
1023
-			$nread = count($ldapGroups);
1024
-			\OCP\Util::writeLog('user_ldap', 'getGroups('.$search.'): read '.$nread.' at offset '.$chunkOffset.' (limit: '.$chunkLimit.')', \OCP\Util::DEBUG);
1025
-			if ($nread) {
1026
-				$allGroups = array_merge($allGroups, $ldapGroups);
1027
-				$chunkOffset += $nread;
1028
-			}
1029
-			if ($nread < $chunkLimit) {
1030
-				break;
1031
-			}
1032
-		}
1033
-		return $allGroups;
1034
-	}
1035
-
1036
-	/**
1037
-	 * @param string $group
1038
-	 * @return bool
1039
-	 */
1040
-	public function groupMatchesFilter($group) {
1041
-		return (strripos($group, $this->groupSearch) !== false);
1042
-	}
1043
-
1044
-	/**
1045
-	 * check if a group exists
1046
-	 * @param string $gid
1047
-	 * @return bool
1048
-	 */
1049
-	public function groupExists($gid) {
1050
-		$groupExists = $this->access->connection->getFromCache('groupExists'.$gid);
1051
-		if(!is_null($groupExists)) {
1052
-			return (bool)$groupExists;
1053
-		}
1054
-
1055
-		//getting dn, if false the group does not exist. If dn, it may be mapped
1056
-		//only, requires more checking.
1057
-		$dn = $this->access->groupname2dn($gid);
1058
-		if(!$dn) {
1059
-			$this->access->connection->writeToCache('groupExists'.$gid, false);
1060
-			return false;
1061
-		}
1062
-
1063
-		//if group really still exists, we will be able to read its objectclass
1064
-		if(!is_array($this->access->readAttribute($dn, ''))) {
1065
-			$this->access->connection->writeToCache('groupExists'.$gid, false);
1066
-			return false;
1067
-		}
1068
-
1069
-		$this->access->connection->writeToCache('groupExists'.$gid, true);
1070
-		return true;
1071
-	}
1072
-
1073
-	/**
1074
-	* Check if backend implements actions
1075
-	* @param int $actions bitwise-or'ed actions
1076
-	* @return boolean
1077
-	*
1078
-	* Returns the supported actions as int to be
1079
-	* compared with GroupInterface::CREATE_GROUP etc.
1080
-	*/
1081
-	public function implementsActions($actions) {
1082
-		return (bool)((GroupInterface::COUNT_USERS |
1083
-				$this->groupPluginManager->getImplementedActions()) & $actions);
1084
-	}
1085
-
1086
-	/**
1087
-	 * Return access for LDAP interaction.
1088
-	 * @return Access instance of Access for LDAP interaction
1089
-	 */
1090
-	public function getLDAPAccess($gid) {
1091
-		return $this->access;
1092
-	}
1093
-
1094
-	/**
1095
-	 * create a group
1096
-	 * @param string $gid
1097
-	 * @return bool
1098
-	 * @throws \Exception
1099
-	 */
1100
-	public function createGroup($gid) {
1101
-		if ($this->groupPluginManager->implementsActions(GroupInterface::CREATE_GROUP)) {
1102
-			if ($dn = $this->groupPluginManager->createGroup($gid)) {
1103
-				//updates group mapping
1104
-				$this->access->dn2ocname($dn, $gid, false);
1105
-				$this->access->connection->writeToCache("groupExists".$gid, true);
1106
-			}
1107
-			return $dn != null;
1108
-		}
1109
-		throw new \Exception('Could not create group in LDAP backend.');
1110
-	}
1111
-
1112
-	/**
1113
-	 * delete a group
1114
-	 * @param string $gid gid of the group to delete
1115
-	 * @return bool
1116
-	 * @throws \Exception
1117
-	 */
1118
-	public function deleteGroup($gid) {
1119
-		if ($this->groupPluginManager->implementsActions(GroupInterface::DELETE_GROUP)) {
1120
-			if ($ret = $this->groupPluginManager->deleteGroup($gid)) {
1121
-				#delete group in nextcloud internal db
1122
-				$this->access->getGroupMapper()->unmap($gid);
1123
-				$this->access->connection->writeToCache("groupExists".$gid, false);
1124
-			}
1125
-			return $ret;
1126
-		}
1127
-		throw new \Exception('Could not delete group in LDAP backend.');
1128
-	}
1129
-
1130
-	/**
1131
-	 * Add a user to a group
1132
-	 * @param string $uid Name of the user to add to group
1133
-	 * @param string $gid Name of the group in which add the user
1134
-	 * @return bool
1135
-	 * @throws \Exception
1136
-	 */
1137
-	public function addToGroup($uid, $gid) {
1138
-		if ($this->groupPluginManager->implementsActions(GroupInterface::ADD_TO_GROUP)) {
1139
-			if ($ret = $this->groupPluginManager->addToGroup($uid, $gid)) {
1140
-				$this->access->connection->clearCache();
1141
-			}
1142
-			return $ret;
1143
-		}
1144
-		throw new \Exception('Could not add user to group in LDAP backend.');
1145
-	}
1146
-
1147
-	/**
1148
-	 * Removes a user from a group
1149
-	 * @param string $uid Name of the user to remove from group
1150
-	 * @param string $gid Name of the group from which remove the user
1151
-	 * @return bool
1152
-	 * @throws \Exception
1153
-	 */
1154
-	public function removeFromGroup($uid, $gid) {
1155
-		if ($this->groupPluginManager->implementsActions(GroupInterface::REMOVE_FROM_GROUP)) {
1156
-			if ($ret = $this->groupPluginManager->removeFromGroup($uid, $gid)) {
1157
-				$this->access->connection->clearCache();
1158
-			}
1159
-			return $ret;
1160
-		}
1161
-		throw new \Exception('Could not remove user from group in LDAP backend.');
1162
-	}
1163
-
1164
-	/**
1165
-	 * Gets group details
1166
-	 * @param string $gid Name of the group
1167
-	 * @return array | false
1168
-	 * @throws \Exception
1169
-	 */
1170
-	public function getGroupDetails($gid) {
1171
-		if ($this->groupPluginManager->implementsActions(GroupInterface::GROUP_DETAILS)) {
1172
-			return $this->groupPluginManager->getGroupDetails($gid);
1173
-		}
1174
-		throw new \Exception('Could not get group details in LDAP backend.');
1175
-	}
1176
-
1177
-	/**
1178
-	 * Return LDAP connection resource from a cloned connection.
1179
-	 * The cloned connection needs to be closed manually.
1180
-	 * of the current access.
1181
-	 * @param string $gid
1182
-	 * @return resource of the LDAP connection
1183
-	 */
1184
-	public function getNewLDAPConnection($gid) {
1185
-		$connection = clone $this->access->getConnection();
1186
-		return $connection->getConnectionResource();
1187
-	}
48
+    protected $enabled = false;
49
+
50
+    /**
51
+     * @var string[] $cachedGroupMembers array of users with gid as key
52
+     */
53
+    protected $cachedGroupMembers;
54
+
55
+    /**
56
+     * @var string[] $cachedGroupsByMember array of groups with uid as key
57
+     */
58
+    protected $cachedGroupsByMember;
59
+
60
+    /** @var GroupPluginManager */
61
+    protected $groupPluginManager;
62
+
63
+    public function __construct(Access $access, GroupPluginManager $groupPluginManager) {
64
+        parent::__construct($access);
65
+        $filter = $this->access->connection->ldapGroupFilter;
66
+        $gassoc = $this->access->connection->ldapGroupMemberAssocAttr;
67
+        if(!empty($filter) && !empty($gassoc)) {
68
+            $this->enabled = true;
69
+        }
70
+
71
+        $this->cachedGroupMembers = new CappedMemoryCache();
72
+        $this->cachedGroupsByMember = new CappedMemoryCache();
73
+        $this->groupPluginManager = $groupPluginManager;
74
+    }
75
+
76
+    /**
77
+     * is user in group?
78
+     * @param string $uid uid of the user
79
+     * @param string $gid gid of the group
80
+     * @return bool
81
+     *
82
+     * Checks whether the user is member of a group or not.
83
+     */
84
+    public function inGroup($uid, $gid) {
85
+        if(!$this->enabled) {
86
+            return false;
87
+        }
88
+        $cacheKey = 'inGroup'.$uid.':'.$gid;
89
+        $inGroup = $this->access->connection->getFromCache($cacheKey);
90
+        if(!is_null($inGroup)) {
91
+            return (bool)$inGroup;
92
+        }
93
+
94
+        $userDN = $this->access->username2dn($uid);
95
+
96
+        if(isset($this->cachedGroupMembers[$gid])) {
97
+            $isInGroup = in_array($userDN, $this->cachedGroupMembers[$gid]);
98
+            return $isInGroup;
99
+        }
100
+
101
+        $cacheKeyMembers = 'inGroup-members:'.$gid;
102
+        $members = $this->access->connection->getFromCache($cacheKeyMembers);
103
+        if(!is_null($members)) {
104
+            $this->cachedGroupMembers[$gid] = $members;
105
+            $isInGroup = in_array($userDN, $members);
106
+            $this->access->connection->writeToCache($cacheKey, $isInGroup);
107
+            return $isInGroup;
108
+        }
109
+
110
+        $groupDN = $this->access->groupname2dn($gid);
111
+        // just in case
112
+        if(!$groupDN || !$userDN) {
113
+            $this->access->connection->writeToCache($cacheKey, false);
114
+            return false;
115
+        }
116
+
117
+        //check primary group first
118
+        if($gid === $this->getUserPrimaryGroup($userDN)) {
119
+            $this->access->connection->writeToCache($cacheKey, true);
120
+            return true;
121
+        }
122
+
123
+        //usually, LDAP attributes are said to be case insensitive. But there are exceptions of course.
124
+        $members = $this->_groupMembers($groupDN);
125
+        $members = array_keys($members); // uids are returned as keys
126
+        if(!is_array($members) || count($members) === 0) {
127
+            $this->access->connection->writeToCache($cacheKey, false);
128
+            return false;
129
+        }
130
+
131
+        //extra work if we don't get back user DNs
132
+        if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
133
+            $dns = array();
134
+            $filterParts = array();
135
+            $bytes = 0;
136
+            foreach($members as $mid) {
137
+                $filter = str_replace('%uid', $mid, $this->access->connection->ldapLoginFilter);
138
+                $filterParts[] = $filter;
139
+                $bytes += strlen($filter);
140
+                if($bytes >= 9000000) {
141
+                    // AD has a default input buffer of 10 MB, we do not want
142
+                    // to take even the chance to exceed it
143
+                    $filter = $this->access->combineFilterWithOr($filterParts);
144
+                    $bytes = 0;
145
+                    $filterParts = array();
146
+                    $users = $this->access->fetchListOfUsers($filter, 'dn', count($filterParts));
147
+                    $dns = array_merge($dns, $users);
148
+                }
149
+            }
150
+            if(count($filterParts) > 0) {
151
+                $filter = $this->access->combineFilterWithOr($filterParts);
152
+                $users = $this->access->fetchListOfUsers($filter, 'dn', count($filterParts));
153
+                $dns = array_merge($dns, $users);
154
+            }
155
+            $members = $dns;
156
+        }
157
+
158
+        $isInGroup = in_array($userDN, $members);
159
+        $this->access->connection->writeToCache($cacheKey, $isInGroup);
160
+        $this->access->connection->writeToCache($cacheKeyMembers, $members);
161
+        $this->cachedGroupMembers[$gid] = $members;
162
+
163
+        return $isInGroup;
164
+    }
165
+
166
+    /**
167
+     * @param string $dnGroup
168
+     * @return array
169
+     *
170
+     * For a group that has user membership defined by an LDAP search url attribute returns the users
171
+     * that match the search url otherwise returns an empty array.
172
+     */
173
+    public function getDynamicGroupMembers($dnGroup) {
174
+        $dynamicGroupMemberURL = strtolower($this->access->connection->ldapDynamicGroupMemberURL);
175
+
176
+        if (empty($dynamicGroupMemberURL)) {
177
+            return array();
178
+        }
179
+
180
+        $dynamicMembers = array();
181
+        $memberURLs = $this->access->readAttribute(
182
+            $dnGroup,
183
+            $dynamicGroupMemberURL,
184
+            $this->access->connection->ldapGroupFilter
185
+        );
186
+        if ($memberURLs !== false) {
187
+            // this group has the 'memberURL' attribute so this is a dynamic group
188
+            // example 1: ldap:///cn=users,cn=accounts,dc=dcsubbase,dc=dcbase??one?(o=HeadOffice)
189
+            // example 2: ldap:///cn=users,cn=accounts,dc=dcsubbase,dc=dcbase??one?(&(o=HeadOffice)(uidNumber>=500))
190
+            $pos = strpos($memberURLs[0], '(');
191
+            if ($pos !== false) {
192
+                $memberUrlFilter = substr($memberURLs[0], $pos);
193
+                $foundMembers = $this->access->searchUsers($memberUrlFilter,'dn');
194
+                $dynamicMembers = array();
195
+                foreach($foundMembers as $value) {
196
+                    $dynamicMembers[$value['dn'][0]] = 1;
197
+                }
198
+            } else {
199
+                \OCP\Util::writeLog('user_ldap', 'No search filter found on member url '.
200
+                    'of group ' . $dnGroup, \OCP\Util::DEBUG);
201
+            }
202
+        }
203
+        return $dynamicMembers;
204
+    }
205
+
206
+    /**
207
+     * @param string $dnGroup
208
+     * @param array|null &$seen
209
+     * @return array|mixed|null
210
+     * @throws \OC\ServerNotAvailableException
211
+     */
212
+    private function _groupMembers($dnGroup, &$seen = null) {
213
+        if ($seen === null) {
214
+            $seen = array();
215
+        }
216
+        $allMembers = array();
217
+        if (array_key_exists($dnGroup, $seen)) {
218
+            // avoid loops
219
+            return array();
220
+        }
221
+        // used extensively in cron job, caching makes sense for nested groups
222
+        $cacheKey = '_groupMembers'.$dnGroup;
223
+        $groupMembers = $this->access->connection->getFromCache($cacheKey);
224
+        if($groupMembers !== null) {
225
+            return $groupMembers;
226
+        }
227
+        $seen[$dnGroup] = 1;
228
+        $members = $this->access->readAttribute($dnGroup, $this->access->connection->ldapGroupMemberAssocAttr,
229
+                                                $this->access->connection->ldapGroupFilter);
230
+        if (is_array($members)) {
231
+            foreach ($members as $member) {
232
+                $allMembers[$member] = 1;
233
+                $nestedGroups = $this->access->connection->ldapNestedGroups;
234
+                if (!empty($nestedGroups)) {
235
+                    $subMembers = $this->_groupMembers($member, $seen);
236
+                    if ($subMembers) {
237
+                        $allMembers += $subMembers;
238
+                    }
239
+                }
240
+            }
241
+        }
242
+
243
+        $allMembers += $this->getDynamicGroupMembers($dnGroup);
244
+
245
+        $this->access->connection->writeToCache($cacheKey, $allMembers);
246
+        return $allMembers;
247
+    }
248
+
249
+    /**
250
+     * @param string $DN
251
+     * @param array|null &$seen
252
+     * @return array
253
+     */
254
+    private function _getGroupDNsFromMemberOf($DN, &$seen = null) {
255
+        if ($seen === null) {
256
+            $seen = array();
257
+        }
258
+        if (array_key_exists($DN, $seen)) {
259
+            // avoid loops
260
+            return array();
261
+        }
262
+        $seen[$DN] = 1;
263
+        $groups = $this->access->readAttribute($DN, 'memberOf');
264
+        if (!is_array($groups)) {
265
+            return array();
266
+        }
267
+        $groups = $this->access->groupsMatchFilter($groups);
268
+        $allGroups =  $groups;
269
+        $nestedGroups = $this->access->connection->ldapNestedGroups;
270
+        if ((int)$nestedGroups === 1) {
271
+            foreach ($groups as $group) {
272
+                $subGroups = $this->_getGroupDNsFromMemberOf($group, $seen);
273
+                $allGroups = array_merge($allGroups, $subGroups);
274
+            }
275
+        }
276
+        return $allGroups;
277
+    }
278
+
279
+    /**
280
+     * translates a gidNumber into an ownCloud internal name
281
+     * @param string $gid as given by gidNumber on POSIX LDAP
282
+     * @param string $dn a DN that belongs to the same domain as the group
283
+     * @return string|bool
284
+     */
285
+    public function gidNumber2Name($gid, $dn) {
286
+        $cacheKey = 'gidNumberToName' . $gid;
287
+        $groupName = $this->access->connection->getFromCache($cacheKey);
288
+        if(!is_null($groupName) && isset($groupName)) {
289
+            return $groupName;
290
+        }
291
+
292
+        //we need to get the DN from LDAP
293
+        $filter = $this->access->combineFilterWithAnd([
294
+            $this->access->connection->ldapGroupFilter,
295
+            'objectClass=posixGroup',
296
+            $this->access->connection->ldapGidNumber . '=' . $gid
297
+        ]);
298
+        $result = $this->access->searchGroups($filter, array('dn'), 1);
299
+        if(empty($result)) {
300
+            return false;
301
+        }
302
+        $dn = $result[0]['dn'][0];
303
+
304
+        //and now the group name
305
+        //NOTE once we have separate ownCloud group IDs and group names we can
306
+        //directly read the display name attribute instead of the DN
307
+        $name = $this->access->dn2groupname($dn);
308
+
309
+        $this->access->connection->writeToCache($cacheKey, $name);
310
+
311
+        return $name;
312
+    }
313
+
314
+    /**
315
+     * returns the entry's gidNumber
316
+     * @param string $dn
317
+     * @param string $attribute
318
+     * @return string|bool
319
+     */
320
+    private function getEntryGidNumber($dn, $attribute) {
321
+        $value = $this->access->readAttribute($dn, $attribute);
322
+        if(is_array($value) && !empty($value)) {
323
+            return $value[0];
324
+        }
325
+        return false;
326
+    }
327
+
328
+    /**
329
+     * returns the group's primary ID
330
+     * @param string $dn
331
+     * @return string|bool
332
+     */
333
+    public function getGroupGidNumber($dn) {
334
+        return $this->getEntryGidNumber($dn, 'gidNumber');
335
+    }
336
+
337
+    /**
338
+     * returns the user's gidNumber
339
+     * @param string $dn
340
+     * @return string|bool
341
+     */
342
+    public function getUserGidNumber($dn) {
343
+        $gidNumber = false;
344
+        if($this->access->connection->hasGidNumber) {
345
+            $gidNumber = $this->getEntryGidNumber($dn, $this->access->connection->ldapGidNumber);
346
+            if($gidNumber === false) {
347
+                $this->access->connection->hasGidNumber = false;
348
+            }
349
+        }
350
+        return $gidNumber;
351
+    }
352
+
353
+    /**
354
+     * returns a filter for a "users has specific gid" search or count operation
355
+     *
356
+     * @param string $groupDN
357
+     * @param string $search
358
+     * @return string
359
+     * @throws \Exception
360
+     */
361
+    private function prepareFilterForUsersHasGidNumber($groupDN, $search = '') {
362
+        $groupID = $this->getGroupGidNumber($groupDN);
363
+        if($groupID === false) {
364
+            throw new \Exception('Not a valid group');
365
+        }
366
+
367
+        $filterParts = [];
368
+        $filterParts[] = $this->access->getFilterForUserCount();
369
+        if ($search !== '') {
370
+            $filterParts[] = $this->access->getFilterPartForUserSearch($search);
371
+        }
372
+        $filterParts[] = $this->access->connection->ldapGidNumber .'=' . $groupID;
373
+
374
+        return $this->access->combineFilterWithAnd($filterParts);
375
+    }
376
+
377
+    /**
378
+     * returns a list of users that have the given group as gid number
379
+     *
380
+     * @param string $groupDN
381
+     * @param string $search
382
+     * @param int $limit
383
+     * @param int $offset
384
+     * @return string[]
385
+     */
386
+    public function getUsersInGidNumber($groupDN, $search = '', $limit = -1, $offset = 0) {
387
+        try {
388
+            $filter = $this->prepareFilterForUsersHasGidNumber($groupDN, $search);
389
+            $users = $this->access->fetchListOfUsers(
390
+                $filter,
391
+                [$this->access->connection->ldapUserDisplayName, 'dn'],
392
+                $limit,
393
+                $offset
394
+            );
395
+            return $this->access->nextcloudUserNames($users);
396
+        } catch (\Exception $e) {
397
+            return [];
398
+        }
399
+    }
400
+
401
+    /**
402
+     * returns the number of users that have the given group as gid number
403
+     *
404
+     * @param string $groupDN
405
+     * @param string $search
406
+     * @param int $limit
407
+     * @param int $offset
408
+     * @return int
409
+     */
410
+    public function countUsersInGidNumber($groupDN, $search = '', $limit = -1, $offset = 0) {
411
+        try {
412
+            $filter = $this->prepareFilterForUsersHasGidNumber($groupDN, $search);
413
+            $users = $this->access->countUsers($filter, ['dn'], $limit, $offset);
414
+            return (int)$users;
415
+        } catch (\Exception $e) {
416
+            return 0;
417
+        }
418
+    }
419
+
420
+    /**
421
+     * gets the gidNumber of a user
422
+     * @param string $dn
423
+     * @return string
424
+     */
425
+    public function getUserGroupByGid($dn) {
426
+        $groupID = $this->getUserGidNumber($dn);
427
+        if($groupID !== false) {
428
+            $groupName = $this->gidNumber2Name($groupID, $dn);
429
+            if($groupName !== false) {
430
+                return $groupName;
431
+            }
432
+        }
433
+
434
+        return false;
435
+    }
436
+
437
+    /**
438
+     * translates a primary group ID into an Nextcloud internal name
439
+     * @param string $gid as given by primaryGroupID on AD
440
+     * @param string $dn a DN that belongs to the same domain as the group
441
+     * @return string|bool
442
+     */
443
+    public function primaryGroupID2Name($gid, $dn) {
444
+        $cacheKey = 'primaryGroupIDtoName';
445
+        $groupNames = $this->access->connection->getFromCache($cacheKey);
446
+        if(!is_null($groupNames) && isset($groupNames[$gid])) {
447
+            return $groupNames[$gid];
448
+        }
449
+
450
+        $domainObjectSid = $this->access->getSID($dn);
451
+        if($domainObjectSid === false) {
452
+            return false;
453
+        }
454
+
455
+        //we need to get the DN from LDAP
456
+        $filter = $this->access->combineFilterWithAnd(array(
457
+            $this->access->connection->ldapGroupFilter,
458
+            'objectsid=' . $domainObjectSid . '-' . $gid
459
+        ));
460
+        $result = $this->access->searchGroups($filter, array('dn'), 1);
461
+        if(empty($result)) {
462
+            return false;
463
+        }
464
+        $dn = $result[0]['dn'][0];
465
+
466
+        //and now the group name
467
+        //NOTE once we have separate Nextcloud group IDs and group names we can
468
+        //directly read the display name attribute instead of the DN
469
+        $name = $this->access->dn2groupname($dn);
470
+
471
+        $this->access->connection->writeToCache($cacheKey, $name);
472
+
473
+        return $name;
474
+    }
475
+
476
+    /**
477
+     * returns the entry's primary group ID
478
+     * @param string $dn
479
+     * @param string $attribute
480
+     * @return string|bool
481
+     */
482
+    private function getEntryGroupID($dn, $attribute) {
483
+        $value = $this->access->readAttribute($dn, $attribute);
484
+        if(is_array($value) && !empty($value)) {
485
+            return $value[0];
486
+        }
487
+        return false;
488
+    }
489
+
490
+    /**
491
+     * returns the group's primary ID
492
+     * @param string $dn
493
+     * @return string|bool
494
+     */
495
+    public function getGroupPrimaryGroupID($dn) {
496
+        return $this->getEntryGroupID($dn, 'primaryGroupToken');
497
+    }
498
+
499
+    /**
500
+     * returns the user's primary group ID
501
+     * @param string $dn
502
+     * @return string|bool
503
+     */
504
+    public function getUserPrimaryGroupIDs($dn) {
505
+        $primaryGroupID = false;
506
+        if($this->access->connection->hasPrimaryGroups) {
507
+            $primaryGroupID = $this->getEntryGroupID($dn, 'primaryGroupID');
508
+            if($primaryGroupID === false) {
509
+                $this->access->connection->hasPrimaryGroups = false;
510
+            }
511
+        }
512
+        return $primaryGroupID;
513
+    }
514
+
515
+    /**
516
+     * returns a filter for a "users in primary group" search or count operation
517
+     *
518
+     * @param string $groupDN
519
+     * @param string $search
520
+     * @return string
521
+     * @throws \Exception
522
+     */
523
+    private function prepareFilterForUsersInPrimaryGroup($groupDN, $search = '') {
524
+        $groupID = $this->getGroupPrimaryGroupID($groupDN);
525
+        if($groupID === false) {
526
+            throw new \Exception('Not a valid group');
527
+        }
528
+
529
+        $filterParts = [];
530
+        $filterParts[] = $this->access->getFilterForUserCount();
531
+        if ($search !== '') {
532
+            $filterParts[] = $this->access->getFilterPartForUserSearch($search);
533
+        }
534
+        $filterParts[] = 'primaryGroupID=' . $groupID;
535
+
536
+        return $this->access->combineFilterWithAnd($filterParts);
537
+    }
538
+
539
+    /**
540
+     * returns a list of users that have the given group as primary group
541
+     *
542
+     * @param string $groupDN
543
+     * @param string $search
544
+     * @param int $limit
545
+     * @param int $offset
546
+     * @return string[]
547
+     */
548
+    public function getUsersInPrimaryGroup($groupDN, $search = '', $limit = -1, $offset = 0) {
549
+        try {
550
+            $filter = $this->prepareFilterForUsersInPrimaryGroup($groupDN, $search);
551
+            $users = $this->access->fetchListOfUsers(
552
+                $filter,
553
+                array($this->access->connection->ldapUserDisplayName, 'dn'),
554
+                $limit,
555
+                $offset
556
+            );
557
+            return $this->access->nextcloudUserNames($users);
558
+        } catch (\Exception $e) {
559
+            return array();
560
+        }
561
+    }
562
+
563
+    /**
564
+     * returns the number of users that have the given group as primary group
565
+     *
566
+     * @param string $groupDN
567
+     * @param string $search
568
+     * @param int $limit
569
+     * @param int $offset
570
+     * @return int
571
+     */
572
+    public function countUsersInPrimaryGroup($groupDN, $search = '', $limit = -1, $offset = 0) {
573
+        try {
574
+            $filter = $this->prepareFilterForUsersInPrimaryGroup($groupDN, $search);
575
+            $users = $this->access->countUsers($filter, array('dn'), $limit, $offset);
576
+            return (int)$users;
577
+        } catch (\Exception $e) {
578
+            return 0;
579
+        }
580
+    }
581
+
582
+    /**
583
+     * gets the primary group of a user
584
+     * @param string $dn
585
+     * @return string
586
+     */
587
+    public function getUserPrimaryGroup($dn) {
588
+        $groupID = $this->getUserPrimaryGroupIDs($dn);
589
+        if($groupID !== false) {
590
+            $groupName = $this->primaryGroupID2Name($groupID, $dn);
591
+            if($groupName !== false) {
592
+                return $groupName;
593
+            }
594
+        }
595
+
596
+        return false;
597
+    }
598
+
599
+    /**
600
+     * Get all groups a user belongs to
601
+     * @param string $uid Name of the user
602
+     * @return array with group names
603
+     *
604
+     * This function fetches all groups a user belongs to. It does not check
605
+     * if the user exists at all.
606
+     *
607
+     * This function includes groups based on dynamic group membership.
608
+     */
609
+    public function getUserGroups($uid) {
610
+        if(!$this->enabled) {
611
+            return array();
612
+        }
613
+        $cacheKey = 'getUserGroups'.$uid;
614
+        $userGroups = $this->access->connection->getFromCache($cacheKey);
615
+        if(!is_null($userGroups)) {
616
+            return $userGroups;
617
+        }
618
+        $userDN = $this->access->username2dn($uid);
619
+        if(!$userDN) {
620
+            $this->access->connection->writeToCache($cacheKey, array());
621
+            return array();
622
+        }
623
+
624
+        $groups = [];
625
+        $primaryGroup = $this->getUserPrimaryGroup($userDN);
626
+        $gidGroupName = $this->getUserGroupByGid($userDN);
627
+
628
+        $dynamicGroupMemberURL = strtolower($this->access->connection->ldapDynamicGroupMemberURL);
629
+
630
+        if (!empty($dynamicGroupMemberURL)) {
631
+            // look through dynamic groups to add them to the result array if needed
632
+            $groupsToMatch = $this->access->fetchListOfGroups(
633
+                $this->access->connection->ldapGroupFilter,array('dn',$dynamicGroupMemberURL));
634
+            foreach($groupsToMatch as $dynamicGroup) {
635
+                if (!array_key_exists($dynamicGroupMemberURL, $dynamicGroup)) {
636
+                    continue;
637
+                }
638
+                $pos = strpos($dynamicGroup[$dynamicGroupMemberURL][0], '(');
639
+                if ($pos !== false) {
640
+                    $memberUrlFilter = substr($dynamicGroup[$dynamicGroupMemberURL][0],$pos);
641
+                    // apply filter via ldap search to see if this user is in this
642
+                    // dynamic group
643
+                    $userMatch = $this->access->readAttribute(
644
+                        $userDN,
645
+                        $this->access->connection->ldapUserDisplayName,
646
+                        $memberUrlFilter
647
+                    );
648
+                    if ($userMatch !== false) {
649
+                        // match found so this user is in this group
650
+                        $groupName = $this->access->dn2groupname($dynamicGroup['dn'][0]);
651
+                        if(is_string($groupName)) {
652
+                            // be sure to never return false if the dn could not be
653
+                            // resolved to a name, for whatever reason.
654
+                            $groups[] = $groupName;
655
+                        }
656
+                    }
657
+                } else {
658
+                    \OCP\Util::writeLog('user_ldap', 'No search filter found on member url '.
659
+                        'of group ' . print_r($dynamicGroup, true), \OCP\Util::DEBUG);
660
+                }
661
+            }
662
+        }
663
+
664
+        // if possible, read out membership via memberOf. It's far faster than
665
+        // performing a search, which still is a fallback later.
666
+        // memberof doesn't support memberuid, so skip it here.
667
+        if((int)$this->access->connection->hasMemberOfFilterSupport === 1
668
+            && (int)$this->access->connection->useMemberOfToDetectMembership === 1
669
+            && strtolower($this->access->connection->ldapGroupMemberAssocAttr) !== 'memberuid'
670
+            ) {
671
+            $groupDNs = $this->_getGroupDNsFromMemberOf($userDN);
672
+            if (is_array($groupDNs)) {
673
+                foreach ($groupDNs as $dn) {
674
+                    $groupName = $this->access->dn2groupname($dn);
675
+                    if(is_string($groupName)) {
676
+                        // be sure to never return false if the dn could not be
677
+                        // resolved to a name, for whatever reason.
678
+                        $groups[] = $groupName;
679
+                    }
680
+                }
681
+            }
682
+
683
+            if($primaryGroup !== false) {
684
+                $groups[] = $primaryGroup;
685
+            }
686
+            if($gidGroupName !== false) {
687
+                $groups[] = $gidGroupName;
688
+            }
689
+            $this->access->connection->writeToCache($cacheKey, $groups);
690
+            return $groups;
691
+        }
692
+
693
+        //uniqueMember takes DN, memberuid the uid, so we need to distinguish
694
+        if((strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'uniquemember')
695
+            || (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'member')
696
+        ) {
697
+            $uid = $userDN;
698
+        } else if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
699
+            $result = $this->access->readAttribute($userDN, 'uid');
700
+            if ($result === false) {
701
+                \OCP\Util::writeLog('user_ldap', 'No uid attribute found for DN ' . $userDN . ' on '.
702
+                    $this->access->connection->ldapHost, \OCP\Util::DEBUG);
703
+            }
704
+            $uid = $result[0];
705
+        } else {
706
+            // just in case
707
+            $uid = $userDN;
708
+        }
709
+
710
+        if(isset($this->cachedGroupsByMember[$uid])) {
711
+            $groups = array_merge($groups, $this->cachedGroupsByMember[$uid]);
712
+        } else {
713
+            $groupsByMember = array_values($this->getGroupsByMember($uid));
714
+            $groupsByMember = $this->access->nextcloudGroupNames($groupsByMember);
715
+            $this->cachedGroupsByMember[$uid] = $groupsByMember;
716
+            $groups = array_merge($groups, $groupsByMember);
717
+        }
718
+
719
+        if($primaryGroup !== false) {
720
+            $groups[] = $primaryGroup;
721
+        }
722
+        if($gidGroupName !== false) {
723
+            $groups[] = $gidGroupName;
724
+        }
725
+
726
+        $groups = array_unique($groups, SORT_LOCALE_STRING);
727
+        $this->access->connection->writeToCache($cacheKey, $groups);
728
+
729
+        return $groups;
730
+    }
731
+
732
+    /**
733
+     * @param string $dn
734
+     * @param array|null &$seen
735
+     * @return array
736
+     */
737
+    private function getGroupsByMember($dn, &$seen = null) {
738
+        if ($seen === null) {
739
+            $seen = array();
740
+        }
741
+        $allGroups = array();
742
+        if (array_key_exists($dn, $seen)) {
743
+            // avoid loops
744
+            return array();
745
+        }
746
+        $seen[$dn] = true;
747
+        $filter = $this->access->combineFilterWithAnd(array(
748
+            $this->access->connection->ldapGroupFilter,
749
+            $this->access->connection->ldapGroupMemberAssocAttr.'='.$dn
750
+        ));
751
+        $groups = $this->access->fetchListOfGroups($filter,
752
+            array($this->access->connection->ldapGroupDisplayName, 'dn'));
753
+        if (is_array($groups)) {
754
+            foreach ($groups as $groupobj) {
755
+                $groupDN = $groupobj['dn'][0];
756
+                $allGroups[$groupDN] = $groupobj;
757
+                $nestedGroups = $this->access->connection->ldapNestedGroups;
758
+                if (!empty($nestedGroups)) {
759
+                    $supergroups = $this->getGroupsByMember($groupDN, $seen);
760
+                    if (is_array($supergroups) && (count($supergroups)>0)) {
761
+                        $allGroups = array_merge($allGroups, $supergroups);
762
+                    }
763
+                }
764
+            }
765
+        }
766
+        return $allGroups;
767
+    }
768
+
769
+    /**
770
+     * get a list of all users in a group
771
+     *
772
+     * @param string $gid
773
+     * @param string $search
774
+     * @param int $limit
775
+     * @param int $offset
776
+     * @return array with user ids
777
+     */
778
+    public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
779
+        if(!$this->enabled) {
780
+            return array();
781
+        }
782
+        if(!$this->groupExists($gid)) {
783
+            return array();
784
+        }
785
+        $search = $this->access->escapeFilterPart($search, true);
786
+        $cacheKey = 'usersInGroup-'.$gid.'-'.$search.'-'.$limit.'-'.$offset;
787
+        // check for cache of the exact query
788
+        $groupUsers = $this->access->connection->getFromCache($cacheKey);
789
+        if(!is_null($groupUsers)) {
790
+            return $groupUsers;
791
+        }
792
+
793
+        // check for cache of the query without limit and offset
794
+        $groupUsers = $this->access->connection->getFromCache('usersInGroup-'.$gid.'-'.$search);
795
+        if(!is_null($groupUsers)) {
796
+            $groupUsers = array_slice($groupUsers, $offset, $limit);
797
+            $this->access->connection->writeToCache($cacheKey, $groupUsers);
798
+            return $groupUsers;
799
+        }
800
+
801
+        if($limit === -1) {
802
+            $limit = null;
803
+        }
804
+        $groupDN = $this->access->groupname2dn($gid);
805
+        if(!$groupDN) {
806
+            // group couldn't be found, return empty resultset
807
+            $this->access->connection->writeToCache($cacheKey, array());
808
+            return array();
809
+        }
810
+
811
+        $primaryUsers = $this->getUsersInPrimaryGroup($groupDN, $search, $limit, $offset);
812
+        $posixGroupUsers = $this->getUsersInGidNumber($groupDN, $search, $limit, $offset);
813
+        $members = array_keys($this->_groupMembers($groupDN));
814
+        if(!$members && empty($posixGroupUsers) && empty($primaryUsers)) {
815
+            //in case users could not be retrieved, return empty result set
816
+            $this->access->connection->writeToCache($cacheKey, []);
817
+            return [];
818
+        }
819
+
820
+        $groupUsers = array();
821
+        $isMemberUid = (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid');
822
+        $attrs = $this->access->userManager->getAttributes(true);
823
+        foreach($members as $member) {
824
+            if($isMemberUid) {
825
+                //we got uids, need to get their DNs to 'translate' them to user names
826
+                $filter = $this->access->combineFilterWithAnd(array(
827
+                    str_replace('%uid', $member, $this->access->connection->ldapLoginFilter),
828
+                    $this->access->getFilterPartForUserSearch($search)
829
+                ));
830
+                $ldap_users = $this->access->fetchListOfUsers($filter, $attrs, 1);
831
+                if(count($ldap_users) < 1) {
832
+                    continue;
833
+                }
834
+                $groupUsers[] = $this->access->dn2username($ldap_users[0]['dn'][0]);
835
+            } else {
836
+                //we got DNs, check if we need to filter by search or we can give back all of them
837
+                if ($search !== '') {
838
+                    if(!$this->access->readAttribute($member,
839
+                        $this->access->connection->ldapUserDisplayName,
840
+                        $this->access->getFilterPartForUserSearch($search))) {
841
+                        continue;
842
+                    }
843
+                }
844
+                // dn2username will also check if the users belong to the allowed base
845
+                if($ocname = $this->access->dn2username($member)) {
846
+                    $groupUsers[] = $ocname;
847
+                }
848
+            }
849
+        }
850
+
851
+        $groupUsers = array_unique(array_merge($groupUsers, $primaryUsers, $posixGroupUsers));
852
+        natsort($groupUsers);
853
+        $this->access->connection->writeToCache('usersInGroup-'.$gid.'-'.$search, $groupUsers);
854
+        $groupUsers = array_slice($groupUsers, $offset, $limit);
855
+
856
+        $this->access->connection->writeToCache($cacheKey, $groupUsers);
857
+
858
+        return $groupUsers;
859
+    }
860
+
861
+    /**
862
+     * returns the number of users in a group, who match the search term
863
+     * @param string $gid the internal group name
864
+     * @param string $search optional, a search string
865
+     * @return int|bool
866
+     */
867
+    public function countUsersInGroup($gid, $search = '') {
868
+        if ($this->groupPluginManager->implementsActions(GroupInterface::COUNT_USERS)) {
869
+            return $this->groupPluginManager->countUsersInGroup($gid, $search);
870
+        }
871
+
872
+        $cacheKey = 'countUsersInGroup-'.$gid.'-'.$search;
873
+        if(!$this->enabled || !$this->groupExists($gid)) {
874
+            return false;
875
+        }
876
+        $groupUsers = $this->access->connection->getFromCache($cacheKey);
877
+        if(!is_null($groupUsers)) {
878
+            return $groupUsers;
879
+        }
880
+
881
+        $groupDN = $this->access->groupname2dn($gid);
882
+        if(!$groupDN) {
883
+            // group couldn't be found, return empty result set
884
+            $this->access->connection->writeToCache($cacheKey, false);
885
+            return false;
886
+        }
887
+
888
+        $members = array_keys($this->_groupMembers($groupDN));
889
+        $primaryUserCount = $this->countUsersInPrimaryGroup($groupDN, '');
890
+        if(!$members && $primaryUserCount === 0) {
891
+            //in case users could not be retrieved, return empty result set
892
+            $this->access->connection->writeToCache($cacheKey, false);
893
+            return false;
894
+        }
895
+
896
+        if ($search === '') {
897
+            $groupUsers = count($members) + $primaryUserCount;
898
+            $this->access->connection->writeToCache($cacheKey, $groupUsers);
899
+            return $groupUsers;
900
+        }
901
+        $search = $this->access->escapeFilterPart($search, true);
902
+        $isMemberUid =
903
+            (strtolower($this->access->connection->ldapGroupMemberAssocAttr)
904
+            === 'memberuid');
905
+
906
+        //we need to apply the search filter
907
+        //alternatives that need to be checked:
908
+        //a) get all users by search filter and array_intersect them
909
+        //b) a, but only when less than 1k 10k ?k users like it is
910
+        //c) put all DNs|uids in a LDAP filter, combine with the search string
911
+        //   and let it count.
912
+        //For now this is not important, because the only use of this method
913
+        //does not supply a search string
914
+        $groupUsers = array();
915
+        foreach($members as $member) {
916
+            if($isMemberUid) {
917
+                //we got uids, need to get their DNs to 'translate' them to user names
918
+                $filter = $this->access->combineFilterWithAnd(array(
919
+                    str_replace('%uid', $member, $this->access->connection->ldapLoginFilter),
920
+                    $this->access->getFilterPartForUserSearch($search)
921
+                ));
922
+                $ldap_users = $this->access->fetchListOfUsers($filter, 'dn', 1);
923
+                if(count($ldap_users) < 1) {
924
+                    continue;
925
+                }
926
+                $groupUsers[] = $this->access->dn2username($ldap_users[0]);
927
+            } else {
928
+                //we need to apply the search filter now
929
+                if(!$this->access->readAttribute($member,
930
+                    $this->access->connection->ldapUserDisplayName,
931
+                    $this->access->getFilterPartForUserSearch($search))) {
932
+                    continue;
933
+                }
934
+                // dn2username will also check if the users belong to the allowed base
935
+                if($ocname = $this->access->dn2username($member)) {
936
+                    $groupUsers[] = $ocname;
937
+                }
938
+            }
939
+        }
940
+
941
+        //and get users that have the group as primary
942
+        $primaryUsers = $this->countUsersInPrimaryGroup($groupDN, $search);
943
+
944
+        return count($groupUsers) + $primaryUsers;
945
+    }
946
+
947
+    /**
948
+     * get a list of all groups
949
+     *
950
+     * @param string $search
951
+     * @param $limit
952
+     * @param int $offset
953
+     * @return array with group names
954
+     *
955
+     * Returns a list with all groups (used by getGroups)
956
+     */
957
+    protected function getGroupsChunk($search = '', $limit = -1, $offset = 0) {
958
+        if(!$this->enabled) {
959
+            return array();
960
+        }
961
+        $cacheKey = 'getGroups-'.$search.'-'.$limit.'-'.$offset;
962
+
963
+        //Check cache before driving unnecessary searches
964
+        \OCP\Util::writeLog('user_ldap', 'getGroups '.$cacheKey, \OCP\Util::DEBUG);
965
+        $ldap_groups = $this->access->connection->getFromCache($cacheKey);
966
+        if(!is_null($ldap_groups)) {
967
+            return $ldap_groups;
968
+        }
969
+
970
+        // if we'd pass -1 to LDAP search, we'd end up in a Protocol
971
+        // error. With a limit of 0, we get 0 results. So we pass null.
972
+        if($limit <= 0) {
973
+            $limit = null;
974
+        }
975
+        $filter = $this->access->combineFilterWithAnd(array(
976
+            $this->access->connection->ldapGroupFilter,
977
+            $this->access->getFilterPartForGroupSearch($search)
978
+        ));
979
+        \OCP\Util::writeLog('user_ldap', 'getGroups Filter '.$filter, \OCP\Util::DEBUG);
980
+        $ldap_groups = $this->access->fetchListOfGroups($filter,
981
+                array($this->access->connection->ldapGroupDisplayName, 'dn'),
982
+                $limit,
983
+                $offset);
984
+        $ldap_groups = $this->access->nextcloudGroupNames($ldap_groups);
985
+
986
+        $this->access->connection->writeToCache($cacheKey, $ldap_groups);
987
+        return $ldap_groups;
988
+    }
989
+
990
+    /**
991
+     * get a list of all groups using a paged search
992
+     *
993
+     * @param string $search
994
+     * @param int $limit
995
+     * @param int $offset
996
+     * @return array with group names
997
+     *
998
+     * Returns a list with all groups
999
+     * Uses a paged search if available to override a
1000
+     * server side search limit.
1001
+     * (active directory has a limit of 1000 by default)
1002
+     */
1003
+    public function getGroups($search = '', $limit = -1, $offset = 0) {
1004
+        if(!$this->enabled) {
1005
+            return array();
1006
+        }
1007
+        $search = $this->access->escapeFilterPart($search, true);
1008
+        $pagingSize = (int)$this->access->connection->ldapPagingSize;
1009
+        if (!$this->access->connection->hasPagedResultSupport || $pagingSize <= 0) {
1010
+            return $this->getGroupsChunk($search, $limit, $offset);
1011
+        }
1012
+        $maxGroups = 100000; // limit max results (just for safety reasons)
1013
+        if ($limit > -1) {
1014
+            $overallLimit = min($limit + $offset, $maxGroups);
1015
+        } else {
1016
+            $overallLimit = $maxGroups;
1017
+        }
1018
+        $chunkOffset = $offset;
1019
+        $allGroups = array();
1020
+        while ($chunkOffset < $overallLimit) {
1021
+            $chunkLimit = min($pagingSize, $overallLimit - $chunkOffset);
1022
+            $ldapGroups = $this->getGroupsChunk($search, $chunkLimit, $chunkOffset);
1023
+            $nread = count($ldapGroups);
1024
+            \OCP\Util::writeLog('user_ldap', 'getGroups('.$search.'): read '.$nread.' at offset '.$chunkOffset.' (limit: '.$chunkLimit.')', \OCP\Util::DEBUG);
1025
+            if ($nread) {
1026
+                $allGroups = array_merge($allGroups, $ldapGroups);
1027
+                $chunkOffset += $nread;
1028
+            }
1029
+            if ($nread < $chunkLimit) {
1030
+                break;
1031
+            }
1032
+        }
1033
+        return $allGroups;
1034
+    }
1035
+
1036
+    /**
1037
+     * @param string $group
1038
+     * @return bool
1039
+     */
1040
+    public function groupMatchesFilter($group) {
1041
+        return (strripos($group, $this->groupSearch) !== false);
1042
+    }
1043
+
1044
+    /**
1045
+     * check if a group exists
1046
+     * @param string $gid
1047
+     * @return bool
1048
+     */
1049
+    public function groupExists($gid) {
1050
+        $groupExists = $this->access->connection->getFromCache('groupExists'.$gid);
1051
+        if(!is_null($groupExists)) {
1052
+            return (bool)$groupExists;
1053
+        }
1054
+
1055
+        //getting dn, if false the group does not exist. If dn, it may be mapped
1056
+        //only, requires more checking.
1057
+        $dn = $this->access->groupname2dn($gid);
1058
+        if(!$dn) {
1059
+            $this->access->connection->writeToCache('groupExists'.$gid, false);
1060
+            return false;
1061
+        }
1062
+
1063
+        //if group really still exists, we will be able to read its objectclass
1064
+        if(!is_array($this->access->readAttribute($dn, ''))) {
1065
+            $this->access->connection->writeToCache('groupExists'.$gid, false);
1066
+            return false;
1067
+        }
1068
+
1069
+        $this->access->connection->writeToCache('groupExists'.$gid, true);
1070
+        return true;
1071
+    }
1072
+
1073
+    /**
1074
+     * Check if backend implements actions
1075
+     * @param int $actions bitwise-or'ed actions
1076
+     * @return boolean
1077
+     *
1078
+     * Returns the supported actions as int to be
1079
+     * compared with GroupInterface::CREATE_GROUP etc.
1080
+     */
1081
+    public function implementsActions($actions) {
1082
+        return (bool)((GroupInterface::COUNT_USERS |
1083
+                $this->groupPluginManager->getImplementedActions()) & $actions);
1084
+    }
1085
+
1086
+    /**
1087
+     * Return access for LDAP interaction.
1088
+     * @return Access instance of Access for LDAP interaction
1089
+     */
1090
+    public function getLDAPAccess($gid) {
1091
+        return $this->access;
1092
+    }
1093
+
1094
+    /**
1095
+     * create a group
1096
+     * @param string $gid
1097
+     * @return bool
1098
+     * @throws \Exception
1099
+     */
1100
+    public function createGroup($gid) {
1101
+        if ($this->groupPluginManager->implementsActions(GroupInterface::CREATE_GROUP)) {
1102
+            if ($dn = $this->groupPluginManager->createGroup($gid)) {
1103
+                //updates group mapping
1104
+                $this->access->dn2ocname($dn, $gid, false);
1105
+                $this->access->connection->writeToCache("groupExists".$gid, true);
1106
+            }
1107
+            return $dn != null;
1108
+        }
1109
+        throw new \Exception('Could not create group in LDAP backend.');
1110
+    }
1111
+
1112
+    /**
1113
+     * delete a group
1114
+     * @param string $gid gid of the group to delete
1115
+     * @return bool
1116
+     * @throws \Exception
1117
+     */
1118
+    public function deleteGroup($gid) {
1119
+        if ($this->groupPluginManager->implementsActions(GroupInterface::DELETE_GROUP)) {
1120
+            if ($ret = $this->groupPluginManager->deleteGroup($gid)) {
1121
+                #delete group in nextcloud internal db
1122
+                $this->access->getGroupMapper()->unmap($gid);
1123
+                $this->access->connection->writeToCache("groupExists".$gid, false);
1124
+            }
1125
+            return $ret;
1126
+        }
1127
+        throw new \Exception('Could not delete group in LDAP backend.');
1128
+    }
1129
+
1130
+    /**
1131
+     * Add a user to a group
1132
+     * @param string $uid Name of the user to add to group
1133
+     * @param string $gid Name of the group in which add the user
1134
+     * @return bool
1135
+     * @throws \Exception
1136
+     */
1137
+    public function addToGroup($uid, $gid) {
1138
+        if ($this->groupPluginManager->implementsActions(GroupInterface::ADD_TO_GROUP)) {
1139
+            if ($ret = $this->groupPluginManager->addToGroup($uid, $gid)) {
1140
+                $this->access->connection->clearCache();
1141
+            }
1142
+            return $ret;
1143
+        }
1144
+        throw new \Exception('Could not add user to group in LDAP backend.');
1145
+    }
1146
+
1147
+    /**
1148
+     * Removes a user from a group
1149
+     * @param string $uid Name of the user to remove from group
1150
+     * @param string $gid Name of the group from which remove the user
1151
+     * @return bool
1152
+     * @throws \Exception
1153
+     */
1154
+    public function removeFromGroup($uid, $gid) {
1155
+        if ($this->groupPluginManager->implementsActions(GroupInterface::REMOVE_FROM_GROUP)) {
1156
+            if ($ret = $this->groupPluginManager->removeFromGroup($uid, $gid)) {
1157
+                $this->access->connection->clearCache();
1158
+            }
1159
+            return $ret;
1160
+        }
1161
+        throw new \Exception('Could not remove user from group in LDAP backend.');
1162
+    }
1163
+
1164
+    /**
1165
+     * Gets group details
1166
+     * @param string $gid Name of the group
1167
+     * @return array | false
1168
+     * @throws \Exception
1169
+     */
1170
+    public function getGroupDetails($gid) {
1171
+        if ($this->groupPluginManager->implementsActions(GroupInterface::GROUP_DETAILS)) {
1172
+            return $this->groupPluginManager->getGroupDetails($gid);
1173
+        }
1174
+        throw new \Exception('Could not get group details in LDAP backend.');
1175
+    }
1176
+
1177
+    /**
1178
+     * Return LDAP connection resource from a cloned connection.
1179
+     * The cloned connection needs to be closed manually.
1180
+     * of the current access.
1181
+     * @param string $gid
1182
+     * @return resource of the LDAP connection
1183
+     */
1184
+    public function getNewLDAPConnection($gid) {
1185
+        $connection = clone $this->access->getConnection();
1186
+        return $connection->getConnectionResource();
1187
+    }
1188 1188
 
1189 1189
 }
Please login to merge, or discard this patch.
Spacing   +94 added lines, -94 removed lines patch added patch discarded remove patch
@@ -64,7 +64,7 @@  discard block
 block discarded – undo
64 64
 		parent::__construct($access);
65 65
 		$filter = $this->access->connection->ldapGroupFilter;
66 66
 		$gassoc = $this->access->connection->ldapGroupMemberAssocAttr;
67
-		if(!empty($filter) && !empty($gassoc)) {
67
+		if (!empty($filter) && !empty($gassoc)) {
68 68
 			$this->enabled = true;
69 69
 		}
70 70
 
@@ -82,25 +82,25 @@  discard block
 block discarded – undo
82 82
 	 * Checks whether the user is member of a group or not.
83 83
 	 */
84 84
 	public function inGroup($uid, $gid) {
85
-		if(!$this->enabled) {
85
+		if (!$this->enabled) {
86 86
 			return false;
87 87
 		}
88 88
 		$cacheKey = 'inGroup'.$uid.':'.$gid;
89 89
 		$inGroup = $this->access->connection->getFromCache($cacheKey);
90
-		if(!is_null($inGroup)) {
91
-			return (bool)$inGroup;
90
+		if (!is_null($inGroup)) {
91
+			return (bool) $inGroup;
92 92
 		}
93 93
 
94 94
 		$userDN = $this->access->username2dn($uid);
95 95
 
96
-		if(isset($this->cachedGroupMembers[$gid])) {
96
+		if (isset($this->cachedGroupMembers[$gid])) {
97 97
 			$isInGroup = in_array($userDN, $this->cachedGroupMembers[$gid]);
98 98
 			return $isInGroup;
99 99
 		}
100 100
 
101 101
 		$cacheKeyMembers = 'inGroup-members:'.$gid;
102 102
 		$members = $this->access->connection->getFromCache($cacheKeyMembers);
103
-		if(!is_null($members)) {
103
+		if (!is_null($members)) {
104 104
 			$this->cachedGroupMembers[$gid] = $members;
105 105
 			$isInGroup = in_array($userDN, $members);
106 106
 			$this->access->connection->writeToCache($cacheKey, $isInGroup);
@@ -109,13 +109,13 @@  discard block
 block discarded – undo
109 109
 
110 110
 		$groupDN = $this->access->groupname2dn($gid);
111 111
 		// just in case
112
-		if(!$groupDN || !$userDN) {
112
+		if (!$groupDN || !$userDN) {
113 113
 			$this->access->connection->writeToCache($cacheKey, false);
114 114
 			return false;
115 115
 		}
116 116
 
117 117
 		//check primary group first
118
-		if($gid === $this->getUserPrimaryGroup($userDN)) {
118
+		if ($gid === $this->getUserPrimaryGroup($userDN)) {
119 119
 			$this->access->connection->writeToCache($cacheKey, true);
120 120
 			return true;
121 121
 		}
@@ -123,21 +123,21 @@  discard block
 block discarded – undo
123 123
 		//usually, LDAP attributes are said to be case insensitive. But there are exceptions of course.
124 124
 		$members = $this->_groupMembers($groupDN);
125 125
 		$members = array_keys($members); // uids are returned as keys
126
-		if(!is_array($members) || count($members) === 0) {
126
+		if (!is_array($members) || count($members) === 0) {
127 127
 			$this->access->connection->writeToCache($cacheKey, false);
128 128
 			return false;
129 129
 		}
130 130
 
131 131
 		//extra work if we don't get back user DNs
132
-		if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
132
+		if (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
133 133
 			$dns = array();
134 134
 			$filterParts = array();
135 135
 			$bytes = 0;
136
-			foreach($members as $mid) {
136
+			foreach ($members as $mid) {
137 137
 				$filter = str_replace('%uid', $mid, $this->access->connection->ldapLoginFilter);
138 138
 				$filterParts[] = $filter;
139 139
 				$bytes += strlen($filter);
140
-				if($bytes >= 9000000) {
140
+				if ($bytes >= 9000000) {
141 141
 					// AD has a default input buffer of 10 MB, we do not want
142 142
 					// to take even the chance to exceed it
143 143
 					$filter = $this->access->combineFilterWithOr($filterParts);
@@ -147,7 +147,7 @@  discard block
 block discarded – undo
147 147
 					$dns = array_merge($dns, $users);
148 148
 				}
149 149
 			}
150
-			if(count($filterParts) > 0) {
150
+			if (count($filterParts) > 0) {
151 151
 				$filter = $this->access->combineFilterWithOr($filterParts);
152 152
 				$users = $this->access->fetchListOfUsers($filter, 'dn', count($filterParts));
153 153
 				$dns = array_merge($dns, $users);
@@ -190,14 +190,14 @@  discard block
 block discarded – undo
190 190
 			$pos = strpos($memberURLs[0], '(');
191 191
 			if ($pos !== false) {
192 192
 				$memberUrlFilter = substr($memberURLs[0], $pos);
193
-				$foundMembers = $this->access->searchUsers($memberUrlFilter,'dn');
193
+				$foundMembers = $this->access->searchUsers($memberUrlFilter, 'dn');
194 194
 				$dynamicMembers = array();
195
-				foreach($foundMembers as $value) {
195
+				foreach ($foundMembers as $value) {
196 196
 					$dynamicMembers[$value['dn'][0]] = 1;
197 197
 				}
198 198
 			} else {
199 199
 				\OCP\Util::writeLog('user_ldap', 'No search filter found on member url '.
200
-					'of group ' . $dnGroup, \OCP\Util::DEBUG);
200
+					'of group '.$dnGroup, \OCP\Util::DEBUG);
201 201
 			}
202 202
 		}
203 203
 		return $dynamicMembers;
@@ -221,7 +221,7 @@  discard block
 block discarded – undo
221 221
 		// used extensively in cron job, caching makes sense for nested groups
222 222
 		$cacheKey = '_groupMembers'.$dnGroup;
223 223
 		$groupMembers = $this->access->connection->getFromCache($cacheKey);
224
-		if($groupMembers !== null) {
224
+		if ($groupMembers !== null) {
225 225
 			return $groupMembers;
226 226
 		}
227 227
 		$seen[$dnGroup] = 1;
@@ -265,9 +265,9 @@  discard block
 block discarded – undo
265 265
 			return array();
266 266
 		}
267 267
 		$groups = $this->access->groupsMatchFilter($groups);
268
-		$allGroups =  $groups;
268
+		$allGroups = $groups;
269 269
 		$nestedGroups = $this->access->connection->ldapNestedGroups;
270
-		if ((int)$nestedGroups === 1) {
270
+		if ((int) $nestedGroups === 1) {
271 271
 			foreach ($groups as $group) {
272 272
 				$subGroups = $this->_getGroupDNsFromMemberOf($group, $seen);
273 273
 				$allGroups = array_merge($allGroups, $subGroups);
@@ -283,9 +283,9 @@  discard block
 block discarded – undo
283 283
 	 * @return string|bool
284 284
 	 */
285 285
 	public function gidNumber2Name($gid, $dn) {
286
-		$cacheKey = 'gidNumberToName' . $gid;
286
+		$cacheKey = 'gidNumberToName'.$gid;
287 287
 		$groupName = $this->access->connection->getFromCache($cacheKey);
288
-		if(!is_null($groupName) && isset($groupName)) {
288
+		if (!is_null($groupName) && isset($groupName)) {
289 289
 			return $groupName;
290 290
 		}
291 291
 
@@ -293,10 +293,10 @@  discard block
 block discarded – undo
293 293
 		$filter = $this->access->combineFilterWithAnd([
294 294
 			$this->access->connection->ldapGroupFilter,
295 295
 			'objectClass=posixGroup',
296
-			$this->access->connection->ldapGidNumber . '=' . $gid
296
+			$this->access->connection->ldapGidNumber.'='.$gid
297 297
 		]);
298 298
 		$result = $this->access->searchGroups($filter, array('dn'), 1);
299
-		if(empty($result)) {
299
+		if (empty($result)) {
300 300
 			return false;
301 301
 		}
302 302
 		$dn = $result[0]['dn'][0];
@@ -319,7 +319,7 @@  discard block
 block discarded – undo
319 319
 	 */
320 320
 	private function getEntryGidNumber($dn, $attribute) {
321 321
 		$value = $this->access->readAttribute($dn, $attribute);
322
-		if(is_array($value) && !empty($value)) {
322
+		if (is_array($value) && !empty($value)) {
323 323
 			return $value[0];
324 324
 		}
325 325
 		return false;
@@ -341,9 +341,9 @@  discard block
 block discarded – undo
341 341
 	 */
342 342
 	public function getUserGidNumber($dn) {
343 343
 		$gidNumber = false;
344
-		if($this->access->connection->hasGidNumber) {
344
+		if ($this->access->connection->hasGidNumber) {
345 345
 			$gidNumber = $this->getEntryGidNumber($dn, $this->access->connection->ldapGidNumber);
346
-			if($gidNumber === false) {
346
+			if ($gidNumber === false) {
347 347
 				$this->access->connection->hasGidNumber = false;
348 348
 			}
349 349
 		}
@@ -360,7 +360,7 @@  discard block
 block discarded – undo
360 360
 	 */
361 361
 	private function prepareFilterForUsersHasGidNumber($groupDN, $search = '') {
362 362
 		$groupID = $this->getGroupGidNumber($groupDN);
363
-		if($groupID === false) {
363
+		if ($groupID === false) {
364 364
 			throw new \Exception('Not a valid group');
365 365
 		}
366 366
 
@@ -369,7 +369,7 @@  discard block
 block discarded – undo
369 369
 		if ($search !== '') {
370 370
 			$filterParts[] = $this->access->getFilterPartForUserSearch($search);
371 371
 		}
372
-		$filterParts[] = $this->access->connection->ldapGidNumber .'=' . $groupID;
372
+		$filterParts[] = $this->access->connection->ldapGidNumber.'='.$groupID;
373 373
 
374 374
 		return $this->access->combineFilterWithAnd($filterParts);
375 375
 	}
@@ -411,7 +411,7 @@  discard block
 block discarded – undo
411 411
 		try {
412 412
 			$filter = $this->prepareFilterForUsersHasGidNumber($groupDN, $search);
413 413
 			$users = $this->access->countUsers($filter, ['dn'], $limit, $offset);
414
-			return (int)$users;
414
+			return (int) $users;
415 415
 		} catch (\Exception $e) {
416 416
 			return 0;
417 417
 		}
@@ -424,9 +424,9 @@  discard block
 block discarded – undo
424 424
 	 */
425 425
 	public function getUserGroupByGid($dn) {
426 426
 		$groupID = $this->getUserGidNumber($dn);
427
-		if($groupID !== false) {
427
+		if ($groupID !== false) {
428 428
 			$groupName = $this->gidNumber2Name($groupID, $dn);
429
-			if($groupName !== false) {
429
+			if ($groupName !== false) {
430 430
 				return $groupName;
431 431
 			}
432 432
 		}
@@ -443,22 +443,22 @@  discard block
 block discarded – undo
443 443
 	public function primaryGroupID2Name($gid, $dn) {
444 444
 		$cacheKey = 'primaryGroupIDtoName';
445 445
 		$groupNames = $this->access->connection->getFromCache($cacheKey);
446
-		if(!is_null($groupNames) && isset($groupNames[$gid])) {
446
+		if (!is_null($groupNames) && isset($groupNames[$gid])) {
447 447
 			return $groupNames[$gid];
448 448
 		}
449 449
 
450 450
 		$domainObjectSid = $this->access->getSID($dn);
451
-		if($domainObjectSid === false) {
451
+		if ($domainObjectSid === false) {
452 452
 			return false;
453 453
 		}
454 454
 
455 455
 		//we need to get the DN from LDAP
456 456
 		$filter = $this->access->combineFilterWithAnd(array(
457 457
 			$this->access->connection->ldapGroupFilter,
458
-			'objectsid=' . $domainObjectSid . '-' . $gid
458
+			'objectsid='.$domainObjectSid.'-'.$gid
459 459
 		));
460 460
 		$result = $this->access->searchGroups($filter, array('dn'), 1);
461
-		if(empty($result)) {
461
+		if (empty($result)) {
462 462
 			return false;
463 463
 		}
464 464
 		$dn = $result[0]['dn'][0];
@@ -481,7 +481,7 @@  discard block
 block discarded – undo
481 481
 	 */
482 482
 	private function getEntryGroupID($dn, $attribute) {
483 483
 		$value = $this->access->readAttribute($dn, $attribute);
484
-		if(is_array($value) && !empty($value)) {
484
+		if (is_array($value) && !empty($value)) {
485 485
 			return $value[0];
486 486
 		}
487 487
 		return false;
@@ -503,9 +503,9 @@  discard block
 block discarded – undo
503 503
 	 */
504 504
 	public function getUserPrimaryGroupIDs($dn) {
505 505
 		$primaryGroupID = false;
506
-		if($this->access->connection->hasPrimaryGroups) {
506
+		if ($this->access->connection->hasPrimaryGroups) {
507 507
 			$primaryGroupID = $this->getEntryGroupID($dn, 'primaryGroupID');
508
-			if($primaryGroupID === false) {
508
+			if ($primaryGroupID === false) {
509 509
 				$this->access->connection->hasPrimaryGroups = false;
510 510
 			}
511 511
 		}
@@ -522,7 +522,7 @@  discard block
 block discarded – undo
522 522
 	 */
523 523
 	private function prepareFilterForUsersInPrimaryGroup($groupDN, $search = '') {
524 524
 		$groupID = $this->getGroupPrimaryGroupID($groupDN);
525
-		if($groupID === false) {
525
+		if ($groupID === false) {
526 526
 			throw new \Exception('Not a valid group');
527 527
 		}
528 528
 
@@ -531,7 +531,7 @@  discard block
 block discarded – undo
531 531
 		if ($search !== '') {
532 532
 			$filterParts[] = $this->access->getFilterPartForUserSearch($search);
533 533
 		}
534
-		$filterParts[] = 'primaryGroupID=' . $groupID;
534
+		$filterParts[] = 'primaryGroupID='.$groupID;
535 535
 
536 536
 		return $this->access->combineFilterWithAnd($filterParts);
537 537
 	}
@@ -573,7 +573,7 @@  discard block
 block discarded – undo
573 573
 		try {
574 574
 			$filter = $this->prepareFilterForUsersInPrimaryGroup($groupDN, $search);
575 575
 			$users = $this->access->countUsers($filter, array('dn'), $limit, $offset);
576
-			return (int)$users;
576
+			return (int) $users;
577 577
 		} catch (\Exception $e) {
578 578
 			return 0;
579 579
 		}
@@ -586,9 +586,9 @@  discard block
 block discarded – undo
586 586
 	 */
587 587
 	public function getUserPrimaryGroup($dn) {
588 588
 		$groupID = $this->getUserPrimaryGroupIDs($dn);
589
-		if($groupID !== false) {
589
+		if ($groupID !== false) {
590 590
 			$groupName = $this->primaryGroupID2Name($groupID, $dn);
591
-			if($groupName !== false) {
591
+			if ($groupName !== false) {
592 592
 				return $groupName;
593 593
 			}
594 594
 		}
@@ -607,16 +607,16 @@  discard block
 block discarded – undo
607 607
 	 * This function includes groups based on dynamic group membership.
608 608
 	 */
609 609
 	public function getUserGroups($uid) {
610
-		if(!$this->enabled) {
610
+		if (!$this->enabled) {
611 611
 			return array();
612 612
 		}
613 613
 		$cacheKey = 'getUserGroups'.$uid;
614 614
 		$userGroups = $this->access->connection->getFromCache($cacheKey);
615
-		if(!is_null($userGroups)) {
615
+		if (!is_null($userGroups)) {
616 616
 			return $userGroups;
617 617
 		}
618 618
 		$userDN = $this->access->username2dn($uid);
619
-		if(!$userDN) {
619
+		if (!$userDN) {
620 620
 			$this->access->connection->writeToCache($cacheKey, array());
621 621
 			return array();
622 622
 		}
@@ -630,14 +630,14 @@  discard block
 block discarded – undo
630 630
 		if (!empty($dynamicGroupMemberURL)) {
631 631
 			// look through dynamic groups to add them to the result array if needed
632 632
 			$groupsToMatch = $this->access->fetchListOfGroups(
633
-				$this->access->connection->ldapGroupFilter,array('dn',$dynamicGroupMemberURL));
634
-			foreach($groupsToMatch as $dynamicGroup) {
633
+				$this->access->connection->ldapGroupFilter, array('dn', $dynamicGroupMemberURL));
634
+			foreach ($groupsToMatch as $dynamicGroup) {
635 635
 				if (!array_key_exists($dynamicGroupMemberURL, $dynamicGroup)) {
636 636
 					continue;
637 637
 				}
638 638
 				$pos = strpos($dynamicGroup[$dynamicGroupMemberURL][0], '(');
639 639
 				if ($pos !== false) {
640
-					$memberUrlFilter = substr($dynamicGroup[$dynamicGroupMemberURL][0],$pos);
640
+					$memberUrlFilter = substr($dynamicGroup[$dynamicGroupMemberURL][0], $pos);
641 641
 					// apply filter via ldap search to see if this user is in this
642 642
 					// dynamic group
643 643
 					$userMatch = $this->access->readAttribute(
@@ -648,7 +648,7 @@  discard block
 block discarded – undo
648 648
 					if ($userMatch !== false) {
649 649
 						// match found so this user is in this group
650 650
 						$groupName = $this->access->dn2groupname($dynamicGroup['dn'][0]);
651
-						if(is_string($groupName)) {
651
+						if (is_string($groupName)) {
652 652
 							// be sure to never return false if the dn could not be
653 653
 							// resolved to a name, for whatever reason.
654 654
 							$groups[] = $groupName;
@@ -656,7 +656,7 @@  discard block
 block discarded – undo
656 656
 					}
657 657
 				} else {
658 658
 					\OCP\Util::writeLog('user_ldap', 'No search filter found on member url '.
659
-						'of group ' . print_r($dynamicGroup, true), \OCP\Util::DEBUG);
659
+						'of group '.print_r($dynamicGroup, true), \OCP\Util::DEBUG);
660 660
 				}
661 661
 			}
662 662
 		}
@@ -664,15 +664,15 @@  discard block
 block discarded – undo
664 664
 		// if possible, read out membership via memberOf. It's far faster than
665 665
 		// performing a search, which still is a fallback later.
666 666
 		// memberof doesn't support memberuid, so skip it here.
667
-		if((int)$this->access->connection->hasMemberOfFilterSupport === 1
668
-			&& (int)$this->access->connection->useMemberOfToDetectMembership === 1
667
+		if ((int) $this->access->connection->hasMemberOfFilterSupport === 1
668
+			&& (int) $this->access->connection->useMemberOfToDetectMembership === 1
669 669
 		    && strtolower($this->access->connection->ldapGroupMemberAssocAttr) !== 'memberuid'
670 670
 		    ) {
671 671
 			$groupDNs = $this->_getGroupDNsFromMemberOf($userDN);
672 672
 			if (is_array($groupDNs)) {
673 673
 				foreach ($groupDNs as $dn) {
674 674
 					$groupName = $this->access->dn2groupname($dn);
675
-					if(is_string($groupName)) {
675
+					if (is_string($groupName)) {
676 676
 						// be sure to never return false if the dn could not be
677 677
 						// resolved to a name, for whatever reason.
678 678
 						$groups[] = $groupName;
@@ -680,10 +680,10 @@  discard block
 block discarded – undo
680 680
 				}
681 681
 			}
682 682
 
683
-			if($primaryGroup !== false) {
683
+			if ($primaryGroup !== false) {
684 684
 				$groups[] = $primaryGroup;
685 685
 			}
686
-			if($gidGroupName !== false) {
686
+			if ($gidGroupName !== false) {
687 687
 				$groups[] = $gidGroupName;
688 688
 			}
689 689
 			$this->access->connection->writeToCache($cacheKey, $groups);
@@ -691,14 +691,14 @@  discard block
 block discarded – undo
691 691
 		}
692 692
 
693 693
 		//uniqueMember takes DN, memberuid the uid, so we need to distinguish
694
-		if((strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'uniquemember')
694
+		if ((strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'uniquemember')
695 695
 			|| (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'member')
696 696
 		) {
697 697
 			$uid = $userDN;
698
-		} else if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
698
+		} else if (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
699 699
 			$result = $this->access->readAttribute($userDN, 'uid');
700 700
 			if ($result === false) {
701
-				\OCP\Util::writeLog('user_ldap', 'No uid attribute found for DN ' . $userDN . ' on '.
701
+				\OCP\Util::writeLog('user_ldap', 'No uid attribute found for DN '.$userDN.' on '.
702 702
 					$this->access->connection->ldapHost, \OCP\Util::DEBUG);
703 703
 			}
704 704
 			$uid = $result[0];
@@ -707,7 +707,7 @@  discard block
 block discarded – undo
707 707
 			$uid = $userDN;
708 708
 		}
709 709
 
710
-		if(isset($this->cachedGroupsByMember[$uid])) {
710
+		if (isset($this->cachedGroupsByMember[$uid])) {
711 711
 			$groups = array_merge($groups, $this->cachedGroupsByMember[$uid]);
712 712
 		} else {
713 713
 			$groupsByMember = array_values($this->getGroupsByMember($uid));
@@ -716,10 +716,10 @@  discard block
 block discarded – undo
716 716
 			$groups = array_merge($groups, $groupsByMember);
717 717
 		}
718 718
 
719
-		if($primaryGroup !== false) {
719
+		if ($primaryGroup !== false) {
720 720
 			$groups[] = $primaryGroup;
721 721
 		}
722
-		if($gidGroupName !== false) {
722
+		if ($gidGroupName !== false) {
723 723
 			$groups[] = $gidGroupName;
724 724
 		}
725 725
 
@@ -757,7 +757,7 @@  discard block
 block discarded – undo
757 757
 				$nestedGroups = $this->access->connection->ldapNestedGroups;
758 758
 				if (!empty($nestedGroups)) {
759 759
 					$supergroups = $this->getGroupsByMember($groupDN, $seen);
760
-					if (is_array($supergroups) && (count($supergroups)>0)) {
760
+					if (is_array($supergroups) && (count($supergroups) > 0)) {
761 761
 						$allGroups = array_merge($allGroups, $supergroups);
762 762
 					}
763 763
 				}
@@ -776,33 +776,33 @@  discard block
 block discarded – undo
776 776
 	 * @return array with user ids
777 777
 	 */
778 778
 	public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
779
-		if(!$this->enabled) {
779
+		if (!$this->enabled) {
780 780
 			return array();
781 781
 		}
782
-		if(!$this->groupExists($gid)) {
782
+		if (!$this->groupExists($gid)) {
783 783
 			return array();
784 784
 		}
785 785
 		$search = $this->access->escapeFilterPart($search, true);
786 786
 		$cacheKey = 'usersInGroup-'.$gid.'-'.$search.'-'.$limit.'-'.$offset;
787 787
 		// check for cache of the exact query
788 788
 		$groupUsers = $this->access->connection->getFromCache($cacheKey);
789
-		if(!is_null($groupUsers)) {
789
+		if (!is_null($groupUsers)) {
790 790
 			return $groupUsers;
791 791
 		}
792 792
 
793 793
 		// check for cache of the query without limit and offset
794 794
 		$groupUsers = $this->access->connection->getFromCache('usersInGroup-'.$gid.'-'.$search);
795
-		if(!is_null($groupUsers)) {
795
+		if (!is_null($groupUsers)) {
796 796
 			$groupUsers = array_slice($groupUsers, $offset, $limit);
797 797
 			$this->access->connection->writeToCache($cacheKey, $groupUsers);
798 798
 			return $groupUsers;
799 799
 		}
800 800
 
801
-		if($limit === -1) {
801
+		if ($limit === -1) {
802 802
 			$limit = null;
803 803
 		}
804 804
 		$groupDN = $this->access->groupname2dn($gid);
805
-		if(!$groupDN) {
805
+		if (!$groupDN) {
806 806
 			// group couldn't be found, return empty resultset
807 807
 			$this->access->connection->writeToCache($cacheKey, array());
808 808
 			return array();
@@ -811,7 +811,7 @@  discard block
 block discarded – undo
811 811
 		$primaryUsers = $this->getUsersInPrimaryGroup($groupDN, $search, $limit, $offset);
812 812
 		$posixGroupUsers = $this->getUsersInGidNumber($groupDN, $search, $limit, $offset);
813 813
 		$members = array_keys($this->_groupMembers($groupDN));
814
-		if(!$members && empty($posixGroupUsers) && empty($primaryUsers)) {
814
+		if (!$members && empty($posixGroupUsers) && empty($primaryUsers)) {
815 815
 			//in case users could not be retrieved, return empty result set
816 816
 			$this->access->connection->writeToCache($cacheKey, []);
817 817
 			return [];
@@ -820,29 +820,29 @@  discard block
 block discarded – undo
820 820
 		$groupUsers = array();
821 821
 		$isMemberUid = (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid');
822 822
 		$attrs = $this->access->userManager->getAttributes(true);
823
-		foreach($members as $member) {
824
-			if($isMemberUid) {
823
+		foreach ($members as $member) {
824
+			if ($isMemberUid) {
825 825
 				//we got uids, need to get their DNs to 'translate' them to user names
826 826
 				$filter = $this->access->combineFilterWithAnd(array(
827 827
 					str_replace('%uid', $member, $this->access->connection->ldapLoginFilter),
828 828
 					$this->access->getFilterPartForUserSearch($search)
829 829
 				));
830 830
 				$ldap_users = $this->access->fetchListOfUsers($filter, $attrs, 1);
831
-				if(count($ldap_users) < 1) {
831
+				if (count($ldap_users) < 1) {
832 832
 					continue;
833 833
 				}
834 834
 				$groupUsers[] = $this->access->dn2username($ldap_users[0]['dn'][0]);
835 835
 			} else {
836 836
 				//we got DNs, check if we need to filter by search or we can give back all of them
837 837
 				if ($search !== '') {
838
-					if(!$this->access->readAttribute($member,
838
+					if (!$this->access->readAttribute($member,
839 839
 						$this->access->connection->ldapUserDisplayName,
840 840
 						$this->access->getFilterPartForUserSearch($search))) {
841 841
 						continue;
842 842
 					}
843 843
 				}
844 844
 				// dn2username will also check if the users belong to the allowed base
845
-				if($ocname = $this->access->dn2username($member)) {
845
+				if ($ocname = $this->access->dn2username($member)) {
846 846
 					$groupUsers[] = $ocname;
847 847
 				}
848 848
 			}
@@ -870,16 +870,16 @@  discard block
 block discarded – undo
870 870
 		}
871 871
 
872 872
 		$cacheKey = 'countUsersInGroup-'.$gid.'-'.$search;
873
-		if(!$this->enabled || !$this->groupExists($gid)) {
873
+		if (!$this->enabled || !$this->groupExists($gid)) {
874 874
 			return false;
875 875
 		}
876 876
 		$groupUsers = $this->access->connection->getFromCache($cacheKey);
877
-		if(!is_null($groupUsers)) {
877
+		if (!is_null($groupUsers)) {
878 878
 			return $groupUsers;
879 879
 		}
880 880
 
881 881
 		$groupDN = $this->access->groupname2dn($gid);
882
-		if(!$groupDN) {
882
+		if (!$groupDN) {
883 883
 			// group couldn't be found, return empty result set
884 884
 			$this->access->connection->writeToCache($cacheKey, false);
885 885
 			return false;
@@ -887,7 +887,7 @@  discard block
 block discarded – undo
887 887
 
888 888
 		$members = array_keys($this->_groupMembers($groupDN));
889 889
 		$primaryUserCount = $this->countUsersInPrimaryGroup($groupDN, '');
890
-		if(!$members && $primaryUserCount === 0) {
890
+		if (!$members && $primaryUserCount === 0) {
891 891
 			//in case users could not be retrieved, return empty result set
892 892
 			$this->access->connection->writeToCache($cacheKey, false);
893 893
 			return false;
@@ -912,27 +912,27 @@  discard block
 block discarded – undo
912 912
 		//For now this is not important, because the only use of this method
913 913
 		//does not supply a search string
914 914
 		$groupUsers = array();
915
-		foreach($members as $member) {
916
-			if($isMemberUid) {
915
+		foreach ($members as $member) {
916
+			if ($isMemberUid) {
917 917
 				//we got uids, need to get their DNs to 'translate' them to user names
918 918
 				$filter = $this->access->combineFilterWithAnd(array(
919 919
 					str_replace('%uid', $member, $this->access->connection->ldapLoginFilter),
920 920
 					$this->access->getFilterPartForUserSearch($search)
921 921
 				));
922 922
 				$ldap_users = $this->access->fetchListOfUsers($filter, 'dn', 1);
923
-				if(count($ldap_users) < 1) {
923
+				if (count($ldap_users) < 1) {
924 924
 					continue;
925 925
 				}
926 926
 				$groupUsers[] = $this->access->dn2username($ldap_users[0]);
927 927
 			} else {
928 928
 				//we need to apply the search filter now
929
-				if(!$this->access->readAttribute($member,
929
+				if (!$this->access->readAttribute($member,
930 930
 					$this->access->connection->ldapUserDisplayName,
931 931
 					$this->access->getFilterPartForUserSearch($search))) {
932 932
 					continue;
933 933
 				}
934 934
 				// dn2username will also check if the users belong to the allowed base
935
-				if($ocname = $this->access->dn2username($member)) {
935
+				if ($ocname = $this->access->dn2username($member)) {
936 936
 					$groupUsers[] = $ocname;
937 937
 				}
938 938
 			}
@@ -955,7 +955,7 @@  discard block
 block discarded – undo
955 955
 	 * Returns a list with all groups (used by getGroups)
956 956
 	 */
957 957
 	protected function getGroupsChunk($search = '', $limit = -1, $offset = 0) {
958
-		if(!$this->enabled) {
958
+		if (!$this->enabled) {
959 959
 			return array();
960 960
 		}
961 961
 		$cacheKey = 'getGroups-'.$search.'-'.$limit.'-'.$offset;
@@ -963,13 +963,13 @@  discard block
 block discarded – undo
963 963
 		//Check cache before driving unnecessary searches
964 964
 		\OCP\Util::writeLog('user_ldap', 'getGroups '.$cacheKey, \OCP\Util::DEBUG);
965 965
 		$ldap_groups = $this->access->connection->getFromCache($cacheKey);
966
-		if(!is_null($ldap_groups)) {
966
+		if (!is_null($ldap_groups)) {
967 967
 			return $ldap_groups;
968 968
 		}
969 969
 
970 970
 		// if we'd pass -1 to LDAP search, we'd end up in a Protocol
971 971
 		// error. With a limit of 0, we get 0 results. So we pass null.
972
-		if($limit <= 0) {
972
+		if ($limit <= 0) {
973 973
 			$limit = null;
974 974
 		}
975 975
 		$filter = $this->access->combineFilterWithAnd(array(
@@ -1001,11 +1001,11 @@  discard block
 block discarded – undo
1001 1001
 	 * (active directory has a limit of 1000 by default)
1002 1002
 	 */
1003 1003
 	public function getGroups($search = '', $limit = -1, $offset = 0) {
1004
-		if(!$this->enabled) {
1004
+		if (!$this->enabled) {
1005 1005
 			return array();
1006 1006
 		}
1007 1007
 		$search = $this->access->escapeFilterPart($search, true);
1008
-		$pagingSize = (int)$this->access->connection->ldapPagingSize;
1008
+		$pagingSize = (int) $this->access->connection->ldapPagingSize;
1009 1009
 		if (!$this->access->connection->hasPagedResultSupport || $pagingSize <= 0) {
1010 1010
 			return $this->getGroupsChunk($search, $limit, $offset);
1011 1011
 		}
@@ -1048,20 +1048,20 @@  discard block
 block discarded – undo
1048 1048
 	 */
1049 1049
 	public function groupExists($gid) {
1050 1050
 		$groupExists = $this->access->connection->getFromCache('groupExists'.$gid);
1051
-		if(!is_null($groupExists)) {
1052
-			return (bool)$groupExists;
1051
+		if (!is_null($groupExists)) {
1052
+			return (bool) $groupExists;
1053 1053
 		}
1054 1054
 
1055 1055
 		//getting dn, if false the group does not exist. If dn, it may be mapped
1056 1056
 		//only, requires more checking.
1057 1057
 		$dn = $this->access->groupname2dn($gid);
1058
-		if(!$dn) {
1058
+		if (!$dn) {
1059 1059
 			$this->access->connection->writeToCache('groupExists'.$gid, false);
1060 1060
 			return false;
1061 1061
 		}
1062 1062
 
1063 1063
 		//if group really still exists, we will be able to read its objectclass
1064
-		if(!is_array($this->access->readAttribute($dn, ''))) {
1064
+		if (!is_array($this->access->readAttribute($dn, ''))) {
1065 1065
 			$this->access->connection->writeToCache('groupExists'.$gid, false);
1066 1066
 			return false;
1067 1067
 		}
@@ -1079,7 +1079,7 @@  discard block
 block discarded – undo
1079 1079
 	* compared with GroupInterface::CREATE_GROUP etc.
1080 1080
 	*/
1081 1081
 	public function implementsActions($actions) {
1082
-		return (bool)((GroupInterface::COUNT_USERS |
1082
+		return (bool) ((GroupInterface::COUNT_USERS |
1083 1083
 				$this->groupPluginManager->getImplementedActions()) & $actions);
1084 1084
 	}
1085 1085
 
Please login to merge, or discard this patch.