Completed
Push — master ( 5cb16a...ada3b1 )
by John
23:13
created
lib/private/Group/Database.php 2 patches
Indentation   +564 added lines, -564 removed lines patch added patch discarded remove patch
@@ -30,568 +30,568 @@
 block discarded – undo
30 30
  * Class for group management in a SQL Database (e.g. MySQL, SQLite)
31 31
  */
32 32
 class Database extends ABackend implements
33
-	IAddToGroupBackend,
34
-	ICountDisabledInGroup,
35
-	ICountUsersBackend,
36
-	ICreateNamedGroupBackend,
37
-	IDeleteGroupBackend,
38
-	IGetDisplayNameBackend,
39
-	IGroupDetailsBackend,
40
-	IRemoveFromGroupBackend,
41
-	ISetDisplayNameBackend,
42
-	ISearchableGroupBackend,
43
-	IBatchMethodsBackend,
44
-	INamedBackend {
45
-	/** @var array<string, array{gid: string, displayname: string}> */
46
-	private $groupCache = [];
47
-
48
-	/**
49
-	 * \OC\Group\Database constructor.
50
-	 *
51
-	 * @param IDBConnection|null $dbConn
52
-	 */
53
-	public function __construct(
54
-		private ?IDBConnection $dbConn = null,
55
-	) {
56
-	}
57
-
58
-	/**
59
-	 * FIXME: This function should not be required!
60
-	 */
61
-	private function fixDI() {
62
-		if ($this->dbConn === null) {
63
-			$this->dbConn = \OC::$server->getDatabaseConnection();
64
-		}
65
-	}
66
-
67
-	public function createGroup(string $name): ?string {
68
-		$this->fixDI();
69
-
70
-		$gid = $this->computeGid($name);
71
-		try {
72
-			// Add group
73
-			$builder = $this->dbConn->getQueryBuilder();
74
-			$builder->insert('groups')
75
-				->setValue('gid', $builder->createNamedParameter($gid))
76
-				->setValue('displayname', $builder->createNamedParameter($name))
77
-				->executeStatement();
78
-		} catch (Exception $e) {
79
-			if ($e->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
80
-				return null;
81
-			} else {
82
-				throw $e;
83
-			}
84
-		}
85
-
86
-		// Add to cache
87
-		$this->groupCache[$gid] = [
88
-			'gid' => $gid,
89
-			'displayname' => $name
90
-		];
91
-
92
-		return $gid;
93
-	}
94
-
95
-	/**
96
-	 * delete a group
97
-	 * @param string $gid gid of the group to delete
98
-	 * @return bool
99
-	 *
100
-	 * Deletes a group and removes it from the group_user-table
101
-	 */
102
-	public function deleteGroup(string $gid): bool {
103
-		$this->fixDI();
104
-
105
-		// Delete the group
106
-		$qb = $this->dbConn->getQueryBuilder();
107
-		$qb->delete('groups')
108
-			->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
109
-			->executeStatement();
110
-
111
-		// Delete the group-user relation
112
-		$qb = $this->dbConn->getQueryBuilder();
113
-		$qb->delete('group_user')
114
-			->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
115
-			->executeStatement();
116
-
117
-		// Delete the group-groupadmin relation
118
-		$qb = $this->dbConn->getQueryBuilder();
119
-		$qb->delete('group_admin')
120
-			->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
121
-			->executeStatement();
122
-
123
-		// Delete from cache
124
-		unset($this->groupCache[$gid]);
125
-
126
-		return true;
127
-	}
128
-
129
-	/**
130
-	 * is user in group?
131
-	 * @param string $uid uid of the user
132
-	 * @param string $gid gid of the group
133
-	 * @return bool
134
-	 *
135
-	 * Checks whether the user is member of a group or not.
136
-	 */
137
-	public function inGroup($uid, $gid) {
138
-		$this->fixDI();
139
-
140
-		// check
141
-		$qb = $this->dbConn->getQueryBuilder();
142
-		$cursor = $qb->select('uid')
143
-			->from('group_user')
144
-			->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
145
-			->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
146
-			->executeQuery();
147
-
148
-		$result = $cursor->fetch();
149
-		$cursor->closeCursor();
150
-
151
-		return $result ? true : false;
152
-	}
153
-
154
-	/**
155
-	 * Add a user to a group
156
-	 * @param string $uid Name of the user to add to group
157
-	 * @param string $gid Name of the group in which add the user
158
-	 * @return bool
159
-	 *
160
-	 * Adds a user to a group.
161
-	 */
162
-	public function addToGroup(string $uid, string $gid): bool {
163
-		$this->fixDI();
164
-
165
-		// No duplicate entries!
166
-		if (!$this->inGroup($uid, $gid)) {
167
-			$qb = $this->dbConn->getQueryBuilder();
168
-			$qb->insert('group_user')
169
-				->setValue('uid', $qb->createNamedParameter($uid))
170
-				->setValue('gid', $qb->createNamedParameter($gid))
171
-				->executeStatement();
172
-			return true;
173
-		} else {
174
-			return false;
175
-		}
176
-	}
177
-
178
-	/**
179
-	 * Removes a user from a group
180
-	 * @param string $uid Name of the user to remove from group
181
-	 * @param string $gid Name of the group from which remove the user
182
-	 * @return bool
183
-	 *
184
-	 * removes the user from a group.
185
-	 */
186
-	public function removeFromGroup(string $uid, string $gid): bool {
187
-		$this->fixDI();
188
-
189
-		$qb = $this->dbConn->getQueryBuilder();
190
-		$qb->delete('group_user')
191
-			->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
192
-			->andWhere($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
193
-			->executeStatement();
194
-
195
-		return true;
196
-	}
197
-
198
-	/**
199
-	 * Get all groups a user belongs to
200
-	 * @param string $uid Name of the user
201
-	 * @return list<string> an array of group names
202
-	 *
203
-	 * This function fetches all groups a user belongs to. It does not check
204
-	 * if the user exists at all.
205
-	 */
206
-	public function getUserGroups($uid) {
207
-		//guests has empty or null $uid
208
-		if ($uid === null || $uid === '') {
209
-			return [];
210
-		}
211
-
212
-		$this->fixDI();
213
-
214
-		// No magic!
215
-		$qb = $this->dbConn->getQueryBuilder();
216
-		$cursor = $qb->select('gu.gid', 'g.displayname')
217
-			->from('group_user', 'gu')
218
-			->leftJoin('gu', 'groups', 'g', $qb->expr()->eq('gu.gid', 'g.gid'))
219
-			->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
220
-			->executeQuery();
221
-
222
-		$groups = [];
223
-		while ($row = $cursor->fetch()) {
224
-			$groups[] = $row['gid'];
225
-			$this->groupCache[$row['gid']] = [
226
-				'gid' => $row['gid'],
227
-				'displayname' => $row['displayname'],
228
-			];
229
-		}
230
-		$cursor->closeCursor();
231
-
232
-		return $groups;
233
-	}
234
-
235
-	/**
236
-	 * get a list of all groups
237
-	 * @param string $search
238
-	 * @param int $limit
239
-	 * @param int $offset
240
-	 * @return array an array of group names
241
-	 *
242
-	 * Returns a list with all groups
243
-	 */
244
-	public function getGroups(string $search = '', int $limit = -1, int $offset = 0) {
245
-		$this->fixDI();
246
-
247
-		$query = $this->dbConn->getQueryBuilder();
248
-		$query->select('gid', 'displayname')
249
-			->from('groups')
250
-			->orderBy('gid', 'ASC');
251
-
252
-		if ($search !== '') {
253
-			$query->where($query->expr()->iLike('gid', $query->createNamedParameter(
254
-				'%' . $this->dbConn->escapeLikeParameter($search) . '%'
255
-			)));
256
-			$query->orWhere($query->expr()->iLike('displayname', $query->createNamedParameter(
257
-				'%' . $this->dbConn->escapeLikeParameter($search) . '%'
258
-			)));
259
-		}
260
-
261
-		if ($limit > 0) {
262
-			$query->setMaxResults($limit);
263
-		}
264
-		if ($offset > 0) {
265
-			$query->setFirstResult($offset);
266
-		}
267
-		$result = $query->executeQuery();
268
-
269
-		$groups = [];
270
-		while ($row = $result->fetch()) {
271
-			$this->groupCache[$row['gid']] = [
272
-				'displayname' => $row['displayname'],
273
-				'gid' => $row['gid'],
274
-			];
275
-			$groups[] = $row['gid'];
276
-		}
277
-		$result->closeCursor();
278
-
279
-		return $groups;
280
-	}
281
-
282
-	/**
283
-	 * check if a group exists
284
-	 * @param string $gid
285
-	 * @return bool
286
-	 */
287
-	public function groupExists($gid) {
288
-		$this->fixDI();
289
-
290
-		// Check cache first
291
-		if (isset($this->groupCache[$gid])) {
292
-			return true;
293
-		}
294
-
295
-		$qb = $this->dbConn->getQueryBuilder();
296
-		$cursor = $qb->select('gid', 'displayname')
297
-			->from('groups')
298
-			->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
299
-			->executeQuery();
300
-		$result = $cursor->fetch();
301
-		$cursor->closeCursor();
302
-
303
-		if ($result !== false) {
304
-			$this->groupCache[$gid] = [
305
-				'gid' => $gid,
306
-				'displayname' => $result['displayname'],
307
-			];
308
-			return true;
309
-		}
310
-		return false;
311
-	}
312
-
313
-	/**
314
-	 * {@inheritdoc}
315
-	 */
316
-	public function groupsExists(array $gids): array {
317
-		$notFoundGids = [];
318
-		$existingGroups = [];
319
-
320
-		// In case the data is already locally accessible, not need to do SQL query
321
-		// or do a SQL query but with a smaller in clause
322
-		foreach ($gids as $gid) {
323
-			if (isset($this->groupCache[$gid])) {
324
-				$existingGroups[] = $gid;
325
-			} else {
326
-				$notFoundGids[] = $gid;
327
-			}
328
-		}
329
-
330
-		$qb = $this->dbConn->getQueryBuilder();
331
-		$qb->select('gid', 'displayname')
332
-			->from('groups')
333
-			->where($qb->expr()->in('gid', $qb->createParameter('ids')));
334
-		foreach (array_chunk($notFoundGids, 1000) as $chunk) {
335
-			$qb->setParameter('ids', $chunk, IQueryBuilder::PARAM_STR_ARRAY);
336
-			$result = $qb->executeQuery();
337
-			while ($row = $result->fetch()) {
338
-				$this->groupCache[(string)$row['gid']] = [
339
-					'displayname' => (string)$row['displayname'],
340
-					'gid' => (string)$row['gid'],
341
-				];
342
-				$existingGroups[] = (string)$row['gid'];
343
-			}
344
-			$result->closeCursor();
345
-		}
346
-
347
-		return $existingGroups;
348
-	}
349
-
350
-	/**
351
-	 * Get a list of all users in a group
352
-	 * @param string $gid
353
-	 * @param string $search
354
-	 * @param int $limit
355
-	 * @param int $offset
356
-	 * @return array<int,string> an array of user ids
357
-	 */
358
-	public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0): array {
359
-		return array_values(array_map(fn ($user) => $user->getUid(), $this->searchInGroup($gid, $search, $limit, $offset)));
360
-	}
361
-
362
-	public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array {
363
-		$this->fixDI();
364
-
365
-		$query = $this->dbConn->getQueryBuilder();
366
-		$query->select('g.uid', 'dn.value AS displayname');
367
-
368
-		$query->from('group_user', 'g')
369
-			->where($query->expr()->eq('gid', $query->createNamedParameter($gid)))
370
-			->orderBy('g.uid', 'ASC');
371
-
372
-		// Join displayname and email from oc_accounts_data
373
-		$query->leftJoin('g', 'accounts_data', 'dn',
374
-			$query->expr()->andX(
375
-				$query->expr()->eq('dn.uid', 'g.uid'),
376
-				$query->expr()->eq('dn.name', $query->expr()->literal('displayname'))
377
-			)
378
-		);
379
-
380
-		$query->leftJoin('g', 'accounts_data', 'em',
381
-			$query->expr()->andX(
382
-				$query->expr()->eq('em.uid', 'g.uid'),
383
-				$query->expr()->eq('em.name', $query->expr()->literal('email'))
384
-			)
385
-		);
386
-
387
-		if ($search !== '') {
388
-			// sqlite doesn't like re-using a single named parameter here
389
-			$searchParam1 = $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%');
390
-			$searchParam2 = $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%');
391
-			$searchParam3 = $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%');
392
-
393
-			$query->andWhere(
394
-				$query->expr()->orX(
395
-					$query->expr()->ilike('g.uid', $searchParam1),
396
-					$query->expr()->ilike('dn.value', $searchParam2),
397
-					$query->expr()->ilike('em.value', $searchParam3)
398
-				)
399
-			)
400
-				->orderBy('g.uid', 'ASC');
401
-		}
402
-
403
-
404
-		if ($limit !== -1) {
405
-			$query->setMaxResults($limit);
406
-		}
407
-		if ($offset !== 0) {
408
-			$query->setFirstResult($offset);
409
-		}
410
-
411
-		$result = $query->executeQuery();
412
-
413
-		$users = [];
414
-		$userManager = \OCP\Server::get(IUserManager::class);
415
-		while ($row = $result->fetch()) {
416
-			$users[$row['uid']] = new LazyUser($row['uid'], $userManager, $row['displayname'] ?? null);
417
-		}
418
-		$result->closeCursor();
419
-
420
-		return $users;
421
-	}
422
-
423
-	/**
424
-	 * get the number of all users matching the search string in a group
425
-	 * @param string $gid
426
-	 * @param string $search
427
-	 * @return int
428
-	 */
429
-	public function countUsersInGroup(string $gid, string $search = ''): int {
430
-		$this->fixDI();
431
-
432
-		$query = $this->dbConn->getQueryBuilder();
433
-		$query->select($query->func()->count('*', 'num_users'))
434
-			->from('group_user')
435
-			->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
436
-
437
-		if ($search !== '') {
438
-			$query->andWhere($query->expr()->like('uid', $query->createNamedParameter(
439
-				'%' . $this->dbConn->escapeLikeParameter($search) . '%'
440
-			)));
441
-		}
442
-
443
-		$result = $query->executeQuery();
444
-		$count = $result->fetchOne();
445
-		$result->closeCursor();
446
-
447
-		if ($count !== false) {
448
-			$count = (int)$count;
449
-		} else {
450
-			$count = 0;
451
-		}
452
-
453
-		return $count;
454
-	}
455
-
456
-	/**
457
-	 * get the number of disabled users in a group
458
-	 *
459
-	 * @param string $search
460
-	 *
461
-	 * @return int
462
-	 */
463
-	public function countDisabledInGroup(string $gid): int {
464
-		$this->fixDI();
465
-
466
-		$query = $this->dbConn->getQueryBuilder();
467
-		$query->select($query->createFunction('COUNT(DISTINCT ' . $query->getColumnName('uid') . ')'))
468
-			->from('preferences', 'p')
469
-			->innerJoin('p', 'group_user', 'g', $query->expr()->eq('p.userid', 'g.uid'))
470
-			->where($query->expr()->eq('appid', $query->createNamedParameter('core')))
471
-			->andWhere($query->expr()->eq('configkey', $query->createNamedParameter('enabled')))
472
-			->andWhere($query->expr()->eq('configvalue', $query->createNamedParameter('false'), IQueryBuilder::PARAM_STR))
473
-			->andWhere($query->expr()->eq('gid', $query->createNamedParameter($gid), IQueryBuilder::PARAM_STR));
474
-
475
-		$result = $query->executeQuery();
476
-		$count = $result->fetchOne();
477
-		$result->closeCursor();
478
-
479
-		if ($count !== false) {
480
-			$count = (int)$count;
481
-		} else {
482
-			$count = 0;
483
-		}
484
-
485
-		return $count;
486
-	}
487
-
488
-	public function getDisplayName(string $gid): string {
489
-		if (isset($this->groupCache[$gid])) {
490
-			$displayName = $this->groupCache[$gid]['displayname'];
491
-
492
-			if (isset($displayName) && trim($displayName) !== '') {
493
-				return $displayName;
494
-			}
495
-		}
496
-
497
-		$this->fixDI();
498
-
499
-		$query = $this->dbConn->getQueryBuilder();
500
-		$query->select('displayname')
501
-			->from('groups')
502
-			->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
503
-
504
-		$result = $query->executeQuery();
505
-		$displayName = $result->fetchOne();
506
-		$result->closeCursor();
507
-
508
-		return (string)$displayName;
509
-	}
510
-
511
-	public function getGroupDetails(string $gid): array {
512
-		$displayName = $this->getDisplayName($gid);
513
-		if ($displayName !== '') {
514
-			return ['displayName' => $displayName];
515
-		}
516
-
517
-		return [];
518
-	}
519
-
520
-	/**
521
-	 * {@inheritdoc}
522
-	 */
523
-	public function getGroupsDetails(array $gids): array {
524
-		$notFoundGids = [];
525
-		$details = [];
526
-
527
-		$this->fixDI();
528
-
529
-		// In case the data is already locally accessible, not need to do SQL query
530
-		// or do a SQL query but with a smaller in clause
531
-		foreach ($gids as $gid) {
532
-			if (isset($this->groupCache[$gid])) {
533
-				$details[$gid] = ['displayName' => $this->groupCache[$gid]['displayname']];
534
-			} else {
535
-				$notFoundGids[] = $gid;
536
-			}
537
-		}
538
-
539
-		foreach (array_chunk($notFoundGids, 1000) as $chunk) {
540
-			$query = $this->dbConn->getQueryBuilder();
541
-			$query->select('gid', 'displayname')
542
-				->from('groups')
543
-				->where($query->expr()->in('gid', $query->createNamedParameter($chunk, IQueryBuilder::PARAM_STR_ARRAY)));
544
-
545
-			$result = $query->executeQuery();
546
-			while ($row = $result->fetch()) {
547
-				$details[(string)$row['gid']] = ['displayName' => (string)$row['displayname']];
548
-				$this->groupCache[(string)$row['gid']] = [
549
-					'displayname' => (string)$row['displayname'],
550
-					'gid' => (string)$row['gid'],
551
-				];
552
-			}
553
-			$result->closeCursor();
554
-		}
555
-
556
-		return $details;
557
-	}
558
-
559
-	public function setDisplayName(string $gid, string $displayName): bool {
560
-		if (!$this->groupExists($gid)) {
561
-			return false;
562
-		}
563
-
564
-		$this->fixDI();
565
-
566
-		$displayName = trim($displayName);
567
-		if ($displayName === '') {
568
-			$displayName = $gid;
569
-		}
570
-
571
-		$query = $this->dbConn->getQueryBuilder();
572
-		$query->update('groups')
573
-			->set('displayname', $query->createNamedParameter($displayName))
574
-			->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
575
-		$query->executeStatement();
576
-
577
-		return true;
578
-	}
579
-
580
-	/**
581
-	 * Backend name to be shown in group management
582
-	 * @return string the name of the backend to be shown
583
-	 * @since 21.0.0
584
-	 */
585
-	public function getBackendName(): string {
586
-		return 'Database';
587
-	}
588
-
589
-	/**
590
-	 * Compute group ID from display name (GIDs are limited to 64 characters in database)
591
-	 */
592
-	private function computeGid(string $displayName): string {
593
-		return mb_strlen($displayName) > 64
594
-			? hash('sha256', $displayName)
595
-			: $displayName;
596
-	}
33
+    IAddToGroupBackend,
34
+    ICountDisabledInGroup,
35
+    ICountUsersBackend,
36
+    ICreateNamedGroupBackend,
37
+    IDeleteGroupBackend,
38
+    IGetDisplayNameBackend,
39
+    IGroupDetailsBackend,
40
+    IRemoveFromGroupBackend,
41
+    ISetDisplayNameBackend,
42
+    ISearchableGroupBackend,
43
+    IBatchMethodsBackend,
44
+    INamedBackend {
45
+    /** @var array<string, array{gid: string, displayname: string}> */
46
+    private $groupCache = [];
47
+
48
+    /**
49
+     * \OC\Group\Database constructor.
50
+     *
51
+     * @param IDBConnection|null $dbConn
52
+     */
53
+    public function __construct(
54
+        private ?IDBConnection $dbConn = null,
55
+    ) {
56
+    }
57
+
58
+    /**
59
+     * FIXME: This function should not be required!
60
+     */
61
+    private function fixDI() {
62
+        if ($this->dbConn === null) {
63
+            $this->dbConn = \OC::$server->getDatabaseConnection();
64
+        }
65
+    }
66
+
67
+    public function createGroup(string $name): ?string {
68
+        $this->fixDI();
69
+
70
+        $gid = $this->computeGid($name);
71
+        try {
72
+            // Add group
73
+            $builder = $this->dbConn->getQueryBuilder();
74
+            $builder->insert('groups')
75
+                ->setValue('gid', $builder->createNamedParameter($gid))
76
+                ->setValue('displayname', $builder->createNamedParameter($name))
77
+                ->executeStatement();
78
+        } catch (Exception $e) {
79
+            if ($e->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
80
+                return null;
81
+            } else {
82
+                throw $e;
83
+            }
84
+        }
85
+
86
+        // Add to cache
87
+        $this->groupCache[$gid] = [
88
+            'gid' => $gid,
89
+            'displayname' => $name
90
+        ];
91
+
92
+        return $gid;
93
+    }
94
+
95
+    /**
96
+     * delete a group
97
+     * @param string $gid gid of the group to delete
98
+     * @return bool
99
+     *
100
+     * Deletes a group and removes it from the group_user-table
101
+     */
102
+    public function deleteGroup(string $gid): bool {
103
+        $this->fixDI();
104
+
105
+        // Delete the group
106
+        $qb = $this->dbConn->getQueryBuilder();
107
+        $qb->delete('groups')
108
+            ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
109
+            ->executeStatement();
110
+
111
+        // Delete the group-user relation
112
+        $qb = $this->dbConn->getQueryBuilder();
113
+        $qb->delete('group_user')
114
+            ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
115
+            ->executeStatement();
116
+
117
+        // Delete the group-groupadmin relation
118
+        $qb = $this->dbConn->getQueryBuilder();
119
+        $qb->delete('group_admin')
120
+            ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
121
+            ->executeStatement();
122
+
123
+        // Delete from cache
124
+        unset($this->groupCache[$gid]);
125
+
126
+        return true;
127
+    }
128
+
129
+    /**
130
+     * is user in group?
131
+     * @param string $uid uid of the user
132
+     * @param string $gid gid of the group
133
+     * @return bool
134
+     *
135
+     * Checks whether the user is member of a group or not.
136
+     */
137
+    public function inGroup($uid, $gid) {
138
+        $this->fixDI();
139
+
140
+        // check
141
+        $qb = $this->dbConn->getQueryBuilder();
142
+        $cursor = $qb->select('uid')
143
+            ->from('group_user')
144
+            ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
145
+            ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
146
+            ->executeQuery();
147
+
148
+        $result = $cursor->fetch();
149
+        $cursor->closeCursor();
150
+
151
+        return $result ? true : false;
152
+    }
153
+
154
+    /**
155
+     * Add a user to a group
156
+     * @param string $uid Name of the user to add to group
157
+     * @param string $gid Name of the group in which add the user
158
+     * @return bool
159
+     *
160
+     * Adds a user to a group.
161
+     */
162
+    public function addToGroup(string $uid, string $gid): bool {
163
+        $this->fixDI();
164
+
165
+        // No duplicate entries!
166
+        if (!$this->inGroup($uid, $gid)) {
167
+            $qb = $this->dbConn->getQueryBuilder();
168
+            $qb->insert('group_user')
169
+                ->setValue('uid', $qb->createNamedParameter($uid))
170
+                ->setValue('gid', $qb->createNamedParameter($gid))
171
+                ->executeStatement();
172
+            return true;
173
+        } else {
174
+            return false;
175
+        }
176
+    }
177
+
178
+    /**
179
+     * Removes a user from a group
180
+     * @param string $uid Name of the user to remove from group
181
+     * @param string $gid Name of the group from which remove the user
182
+     * @return bool
183
+     *
184
+     * removes the user from a group.
185
+     */
186
+    public function removeFromGroup(string $uid, string $gid): bool {
187
+        $this->fixDI();
188
+
189
+        $qb = $this->dbConn->getQueryBuilder();
190
+        $qb->delete('group_user')
191
+            ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
192
+            ->andWhere($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
193
+            ->executeStatement();
194
+
195
+        return true;
196
+    }
197
+
198
+    /**
199
+     * Get all groups a user belongs to
200
+     * @param string $uid Name of the user
201
+     * @return list<string> an array of group names
202
+     *
203
+     * This function fetches all groups a user belongs to. It does not check
204
+     * if the user exists at all.
205
+     */
206
+    public function getUserGroups($uid) {
207
+        //guests has empty or null $uid
208
+        if ($uid === null || $uid === '') {
209
+            return [];
210
+        }
211
+
212
+        $this->fixDI();
213
+
214
+        // No magic!
215
+        $qb = $this->dbConn->getQueryBuilder();
216
+        $cursor = $qb->select('gu.gid', 'g.displayname')
217
+            ->from('group_user', 'gu')
218
+            ->leftJoin('gu', 'groups', 'g', $qb->expr()->eq('gu.gid', 'g.gid'))
219
+            ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
220
+            ->executeQuery();
221
+
222
+        $groups = [];
223
+        while ($row = $cursor->fetch()) {
224
+            $groups[] = $row['gid'];
225
+            $this->groupCache[$row['gid']] = [
226
+                'gid' => $row['gid'],
227
+                'displayname' => $row['displayname'],
228
+            ];
229
+        }
230
+        $cursor->closeCursor();
231
+
232
+        return $groups;
233
+    }
234
+
235
+    /**
236
+     * get a list of all groups
237
+     * @param string $search
238
+     * @param int $limit
239
+     * @param int $offset
240
+     * @return array an array of group names
241
+     *
242
+     * Returns a list with all groups
243
+     */
244
+    public function getGroups(string $search = '', int $limit = -1, int $offset = 0) {
245
+        $this->fixDI();
246
+
247
+        $query = $this->dbConn->getQueryBuilder();
248
+        $query->select('gid', 'displayname')
249
+            ->from('groups')
250
+            ->orderBy('gid', 'ASC');
251
+
252
+        if ($search !== '') {
253
+            $query->where($query->expr()->iLike('gid', $query->createNamedParameter(
254
+                '%' . $this->dbConn->escapeLikeParameter($search) . '%'
255
+            )));
256
+            $query->orWhere($query->expr()->iLike('displayname', $query->createNamedParameter(
257
+                '%' . $this->dbConn->escapeLikeParameter($search) . '%'
258
+            )));
259
+        }
260
+
261
+        if ($limit > 0) {
262
+            $query->setMaxResults($limit);
263
+        }
264
+        if ($offset > 0) {
265
+            $query->setFirstResult($offset);
266
+        }
267
+        $result = $query->executeQuery();
268
+
269
+        $groups = [];
270
+        while ($row = $result->fetch()) {
271
+            $this->groupCache[$row['gid']] = [
272
+                'displayname' => $row['displayname'],
273
+                'gid' => $row['gid'],
274
+            ];
275
+            $groups[] = $row['gid'];
276
+        }
277
+        $result->closeCursor();
278
+
279
+        return $groups;
280
+    }
281
+
282
+    /**
283
+     * check if a group exists
284
+     * @param string $gid
285
+     * @return bool
286
+     */
287
+    public function groupExists($gid) {
288
+        $this->fixDI();
289
+
290
+        // Check cache first
291
+        if (isset($this->groupCache[$gid])) {
292
+            return true;
293
+        }
294
+
295
+        $qb = $this->dbConn->getQueryBuilder();
296
+        $cursor = $qb->select('gid', 'displayname')
297
+            ->from('groups')
298
+            ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
299
+            ->executeQuery();
300
+        $result = $cursor->fetch();
301
+        $cursor->closeCursor();
302
+
303
+        if ($result !== false) {
304
+            $this->groupCache[$gid] = [
305
+                'gid' => $gid,
306
+                'displayname' => $result['displayname'],
307
+            ];
308
+            return true;
309
+        }
310
+        return false;
311
+    }
312
+
313
+    /**
314
+     * {@inheritdoc}
315
+     */
316
+    public function groupsExists(array $gids): array {
317
+        $notFoundGids = [];
318
+        $existingGroups = [];
319
+
320
+        // In case the data is already locally accessible, not need to do SQL query
321
+        // or do a SQL query but with a smaller in clause
322
+        foreach ($gids as $gid) {
323
+            if (isset($this->groupCache[$gid])) {
324
+                $existingGroups[] = $gid;
325
+            } else {
326
+                $notFoundGids[] = $gid;
327
+            }
328
+        }
329
+
330
+        $qb = $this->dbConn->getQueryBuilder();
331
+        $qb->select('gid', 'displayname')
332
+            ->from('groups')
333
+            ->where($qb->expr()->in('gid', $qb->createParameter('ids')));
334
+        foreach (array_chunk($notFoundGids, 1000) as $chunk) {
335
+            $qb->setParameter('ids', $chunk, IQueryBuilder::PARAM_STR_ARRAY);
336
+            $result = $qb->executeQuery();
337
+            while ($row = $result->fetch()) {
338
+                $this->groupCache[(string)$row['gid']] = [
339
+                    'displayname' => (string)$row['displayname'],
340
+                    'gid' => (string)$row['gid'],
341
+                ];
342
+                $existingGroups[] = (string)$row['gid'];
343
+            }
344
+            $result->closeCursor();
345
+        }
346
+
347
+        return $existingGroups;
348
+    }
349
+
350
+    /**
351
+     * Get a list of all users in a group
352
+     * @param string $gid
353
+     * @param string $search
354
+     * @param int $limit
355
+     * @param int $offset
356
+     * @return array<int,string> an array of user ids
357
+     */
358
+    public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0): array {
359
+        return array_values(array_map(fn ($user) => $user->getUid(), $this->searchInGroup($gid, $search, $limit, $offset)));
360
+    }
361
+
362
+    public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array {
363
+        $this->fixDI();
364
+
365
+        $query = $this->dbConn->getQueryBuilder();
366
+        $query->select('g.uid', 'dn.value AS displayname');
367
+
368
+        $query->from('group_user', 'g')
369
+            ->where($query->expr()->eq('gid', $query->createNamedParameter($gid)))
370
+            ->orderBy('g.uid', 'ASC');
371
+
372
+        // Join displayname and email from oc_accounts_data
373
+        $query->leftJoin('g', 'accounts_data', 'dn',
374
+            $query->expr()->andX(
375
+                $query->expr()->eq('dn.uid', 'g.uid'),
376
+                $query->expr()->eq('dn.name', $query->expr()->literal('displayname'))
377
+            )
378
+        );
379
+
380
+        $query->leftJoin('g', 'accounts_data', 'em',
381
+            $query->expr()->andX(
382
+                $query->expr()->eq('em.uid', 'g.uid'),
383
+                $query->expr()->eq('em.name', $query->expr()->literal('email'))
384
+            )
385
+        );
386
+
387
+        if ($search !== '') {
388
+            // sqlite doesn't like re-using a single named parameter here
389
+            $searchParam1 = $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%');
390
+            $searchParam2 = $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%');
391
+            $searchParam3 = $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%');
392
+
393
+            $query->andWhere(
394
+                $query->expr()->orX(
395
+                    $query->expr()->ilike('g.uid', $searchParam1),
396
+                    $query->expr()->ilike('dn.value', $searchParam2),
397
+                    $query->expr()->ilike('em.value', $searchParam3)
398
+                )
399
+            )
400
+                ->orderBy('g.uid', 'ASC');
401
+        }
402
+
403
+
404
+        if ($limit !== -1) {
405
+            $query->setMaxResults($limit);
406
+        }
407
+        if ($offset !== 0) {
408
+            $query->setFirstResult($offset);
409
+        }
410
+
411
+        $result = $query->executeQuery();
412
+
413
+        $users = [];
414
+        $userManager = \OCP\Server::get(IUserManager::class);
415
+        while ($row = $result->fetch()) {
416
+            $users[$row['uid']] = new LazyUser($row['uid'], $userManager, $row['displayname'] ?? null);
417
+        }
418
+        $result->closeCursor();
419
+
420
+        return $users;
421
+    }
422
+
423
+    /**
424
+     * get the number of all users matching the search string in a group
425
+     * @param string $gid
426
+     * @param string $search
427
+     * @return int
428
+     */
429
+    public function countUsersInGroup(string $gid, string $search = ''): int {
430
+        $this->fixDI();
431
+
432
+        $query = $this->dbConn->getQueryBuilder();
433
+        $query->select($query->func()->count('*', 'num_users'))
434
+            ->from('group_user')
435
+            ->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
436
+
437
+        if ($search !== '') {
438
+            $query->andWhere($query->expr()->like('uid', $query->createNamedParameter(
439
+                '%' . $this->dbConn->escapeLikeParameter($search) . '%'
440
+            )));
441
+        }
442
+
443
+        $result = $query->executeQuery();
444
+        $count = $result->fetchOne();
445
+        $result->closeCursor();
446
+
447
+        if ($count !== false) {
448
+            $count = (int)$count;
449
+        } else {
450
+            $count = 0;
451
+        }
452
+
453
+        return $count;
454
+    }
455
+
456
+    /**
457
+     * get the number of disabled users in a group
458
+     *
459
+     * @param string $search
460
+     *
461
+     * @return int
462
+     */
463
+    public function countDisabledInGroup(string $gid): int {
464
+        $this->fixDI();
465
+
466
+        $query = $this->dbConn->getQueryBuilder();
467
+        $query->select($query->createFunction('COUNT(DISTINCT ' . $query->getColumnName('uid') . ')'))
468
+            ->from('preferences', 'p')
469
+            ->innerJoin('p', 'group_user', 'g', $query->expr()->eq('p.userid', 'g.uid'))
470
+            ->where($query->expr()->eq('appid', $query->createNamedParameter('core')))
471
+            ->andWhere($query->expr()->eq('configkey', $query->createNamedParameter('enabled')))
472
+            ->andWhere($query->expr()->eq('configvalue', $query->createNamedParameter('false'), IQueryBuilder::PARAM_STR))
473
+            ->andWhere($query->expr()->eq('gid', $query->createNamedParameter($gid), IQueryBuilder::PARAM_STR));
474
+
475
+        $result = $query->executeQuery();
476
+        $count = $result->fetchOne();
477
+        $result->closeCursor();
478
+
479
+        if ($count !== false) {
480
+            $count = (int)$count;
481
+        } else {
482
+            $count = 0;
483
+        }
484
+
485
+        return $count;
486
+    }
487
+
488
+    public function getDisplayName(string $gid): string {
489
+        if (isset($this->groupCache[$gid])) {
490
+            $displayName = $this->groupCache[$gid]['displayname'];
491
+
492
+            if (isset($displayName) && trim($displayName) !== '') {
493
+                return $displayName;
494
+            }
495
+        }
496
+
497
+        $this->fixDI();
498
+
499
+        $query = $this->dbConn->getQueryBuilder();
500
+        $query->select('displayname')
501
+            ->from('groups')
502
+            ->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
503
+
504
+        $result = $query->executeQuery();
505
+        $displayName = $result->fetchOne();
506
+        $result->closeCursor();
507
+
508
+        return (string)$displayName;
509
+    }
510
+
511
+    public function getGroupDetails(string $gid): array {
512
+        $displayName = $this->getDisplayName($gid);
513
+        if ($displayName !== '') {
514
+            return ['displayName' => $displayName];
515
+        }
516
+
517
+        return [];
518
+    }
519
+
520
+    /**
521
+     * {@inheritdoc}
522
+     */
523
+    public function getGroupsDetails(array $gids): array {
524
+        $notFoundGids = [];
525
+        $details = [];
526
+
527
+        $this->fixDI();
528
+
529
+        // In case the data is already locally accessible, not need to do SQL query
530
+        // or do a SQL query but with a smaller in clause
531
+        foreach ($gids as $gid) {
532
+            if (isset($this->groupCache[$gid])) {
533
+                $details[$gid] = ['displayName' => $this->groupCache[$gid]['displayname']];
534
+            } else {
535
+                $notFoundGids[] = $gid;
536
+            }
537
+        }
538
+
539
+        foreach (array_chunk($notFoundGids, 1000) as $chunk) {
540
+            $query = $this->dbConn->getQueryBuilder();
541
+            $query->select('gid', 'displayname')
542
+                ->from('groups')
543
+                ->where($query->expr()->in('gid', $query->createNamedParameter($chunk, IQueryBuilder::PARAM_STR_ARRAY)));
544
+
545
+            $result = $query->executeQuery();
546
+            while ($row = $result->fetch()) {
547
+                $details[(string)$row['gid']] = ['displayName' => (string)$row['displayname']];
548
+                $this->groupCache[(string)$row['gid']] = [
549
+                    'displayname' => (string)$row['displayname'],
550
+                    'gid' => (string)$row['gid'],
551
+                ];
552
+            }
553
+            $result->closeCursor();
554
+        }
555
+
556
+        return $details;
557
+    }
558
+
559
+    public function setDisplayName(string $gid, string $displayName): bool {
560
+        if (!$this->groupExists($gid)) {
561
+            return false;
562
+        }
563
+
564
+        $this->fixDI();
565
+
566
+        $displayName = trim($displayName);
567
+        if ($displayName === '') {
568
+            $displayName = $gid;
569
+        }
570
+
571
+        $query = $this->dbConn->getQueryBuilder();
572
+        $query->update('groups')
573
+            ->set('displayname', $query->createNamedParameter($displayName))
574
+            ->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
575
+        $query->executeStatement();
576
+
577
+        return true;
578
+    }
579
+
580
+    /**
581
+     * Backend name to be shown in group management
582
+     * @return string the name of the backend to be shown
583
+     * @since 21.0.0
584
+     */
585
+    public function getBackendName(): string {
586
+        return 'Database';
587
+    }
588
+
589
+    /**
590
+     * Compute group ID from display name (GIDs are limited to 64 characters in database)
591
+     */
592
+    private function computeGid(string $displayName): string {
593
+        return mb_strlen($displayName) > 64
594
+            ? hash('sha256', $displayName)
595
+            : $displayName;
596
+    }
597 597
 }
Please login to merge, or discard this patch.
Spacing   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -251,10 +251,10 @@  discard block
 block discarded – undo
251 251
 
252 252
 		if ($search !== '') {
253 253
 			$query->where($query->expr()->iLike('gid', $query->createNamedParameter(
254
-				'%' . $this->dbConn->escapeLikeParameter($search) . '%'
254
+				'%'.$this->dbConn->escapeLikeParameter($search).'%'
255 255
 			)));
256 256
 			$query->orWhere($query->expr()->iLike('displayname', $query->createNamedParameter(
257
-				'%' . $this->dbConn->escapeLikeParameter($search) . '%'
257
+				'%'.$this->dbConn->escapeLikeParameter($search).'%'
258 258
 			)));
259 259
 		}
260 260
 
@@ -335,11 +335,11 @@  discard block
 block discarded – undo
335 335
 			$qb->setParameter('ids', $chunk, IQueryBuilder::PARAM_STR_ARRAY);
336 336
 			$result = $qb->executeQuery();
337 337
 			while ($row = $result->fetch()) {
338
-				$this->groupCache[(string)$row['gid']] = [
339
-					'displayname' => (string)$row['displayname'],
340
-					'gid' => (string)$row['gid'],
338
+				$this->groupCache[(string) $row['gid']] = [
339
+					'displayname' => (string) $row['displayname'],
340
+					'gid' => (string) $row['gid'],
341 341
 				];
342
-				$existingGroups[] = (string)$row['gid'];
342
+				$existingGroups[] = (string) $row['gid'];
343 343
 			}
344 344
 			$result->closeCursor();
345 345
 		}
@@ -386,9 +386,9 @@  discard block
 block discarded – undo
386 386
 
387 387
 		if ($search !== '') {
388 388
 			// sqlite doesn't like re-using a single named parameter here
389
-			$searchParam1 = $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%');
390
-			$searchParam2 = $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%');
391
-			$searchParam3 = $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%');
389
+			$searchParam1 = $query->createNamedParameter('%'.$this->dbConn->escapeLikeParameter($search).'%');
390
+			$searchParam2 = $query->createNamedParameter('%'.$this->dbConn->escapeLikeParameter($search).'%');
391
+			$searchParam3 = $query->createNamedParameter('%'.$this->dbConn->escapeLikeParameter($search).'%');
392 392
 
393 393
 			$query->andWhere(
394 394
 				$query->expr()->orX(
@@ -436,7 +436,7 @@  discard block
 block discarded – undo
436 436
 
437 437
 		if ($search !== '') {
438 438
 			$query->andWhere($query->expr()->like('uid', $query->createNamedParameter(
439
-				'%' . $this->dbConn->escapeLikeParameter($search) . '%'
439
+				'%'.$this->dbConn->escapeLikeParameter($search).'%'
440 440
 			)));
441 441
 		}
442 442
 
@@ -445,7 +445,7 @@  discard block
 block discarded – undo
445 445
 		$result->closeCursor();
446 446
 
447 447
 		if ($count !== false) {
448
-			$count = (int)$count;
448
+			$count = (int) $count;
449 449
 		} else {
450 450
 			$count = 0;
451 451
 		}
@@ -464,7 +464,7 @@  discard block
 block discarded – undo
464 464
 		$this->fixDI();
465 465
 
466 466
 		$query = $this->dbConn->getQueryBuilder();
467
-		$query->select($query->createFunction('COUNT(DISTINCT ' . $query->getColumnName('uid') . ')'))
467
+		$query->select($query->createFunction('COUNT(DISTINCT '.$query->getColumnName('uid').')'))
468 468
 			->from('preferences', 'p')
469 469
 			->innerJoin('p', 'group_user', 'g', $query->expr()->eq('p.userid', 'g.uid'))
470 470
 			->where($query->expr()->eq('appid', $query->createNamedParameter('core')))
@@ -477,7 +477,7 @@  discard block
 block discarded – undo
477 477
 		$result->closeCursor();
478 478
 
479 479
 		if ($count !== false) {
480
-			$count = (int)$count;
480
+			$count = (int) $count;
481 481
 		} else {
482 482
 			$count = 0;
483 483
 		}
@@ -505,7 +505,7 @@  discard block
 block discarded – undo
505 505
 		$displayName = $result->fetchOne();
506 506
 		$result->closeCursor();
507 507
 
508
-		return (string)$displayName;
508
+		return (string) $displayName;
509 509
 	}
510 510
 
511 511
 	public function getGroupDetails(string $gid): array {
@@ -544,10 +544,10 @@  discard block
 block discarded – undo
544 544
 
545 545
 			$result = $query->executeQuery();
546 546
 			while ($row = $result->fetch()) {
547
-				$details[(string)$row['gid']] = ['displayName' => (string)$row['displayname']];
548
-				$this->groupCache[(string)$row['gid']] = [
549
-					'displayname' => (string)$row['displayname'],
550
-					'gid' => (string)$row['gid'],
547
+				$details[(string) $row['gid']] = ['displayName' => (string) $row['displayname']];
548
+				$this->groupCache[(string) $row['gid']] = [
549
+					'displayname' => (string) $row['displayname'],
550
+					'gid' => (string) $row['gid'],
551 551
 				];
552 552
 			}
553 553
 			$result->closeCursor();
Please login to merge, or discard this patch.