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