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