Completed
Push — master ( fb7244...8410d6 )
by Blizzz
67:26 queued 26:45
created
tests/lib/User/ManagerTest.php 2 patches
Indentation   +775 added lines, -775 removed lines patch added patch discarded remove patch
@@ -28,779 +28,779 @@
 block discarded – undo
28 28
  * @package Test\User
29 29
  */
30 30
 class ManagerTest extends TestCase {
31
-	/** @var IConfig */
32
-	private $config;
33
-	/** @var IEventDispatcher */
34
-	private $eventDispatcher;
35
-	/** @var ICacheFactory */
36
-	private $cacheFactory;
37
-	/** @var ICache */
38
-	private $cache;
39
-	/** @var LoggerInterface */
40
-	private $logger;
41
-
42
-	protected function setUp(): void {
43
-		parent::setUp();
44
-
45
-		$this->config = $this->createMock(IConfig::class);
46
-		$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
47
-		$this->cacheFactory = $this->createMock(ICacheFactory::class);
48
-		$this->cache = $this->createMock(ICache::class);
49
-		$this->logger = $this->createMock(LoggerInterface::class);
50
-
51
-		$this->cacheFactory->method('createDistributed')
52
-			->willReturn($this->cache);
53
-	}
54
-
55
-	public function testGetBackends(): void {
56
-		$userDummyBackend = $this->createMock(\Test\Util\User\Dummy::class);
57
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
58
-		$manager->registerBackend($userDummyBackend);
59
-		$this->assertEquals([$userDummyBackend], $manager->getBackends());
60
-		$dummyDatabaseBackend = $this->createMock(Database::class);
61
-		$manager->registerBackend($dummyDatabaseBackend);
62
-		$this->assertEquals([$userDummyBackend, $dummyDatabaseBackend], $manager->getBackends());
63
-	}
64
-
65
-
66
-	public function testUserExistsSingleBackendExists(): void {
67
-		/**
68
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
69
-		 */
70
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
71
-		$backend->expects($this->once())
72
-			->method('userExists')
73
-			->with($this->equalTo('foo'))
74
-			->willReturn(true);
75
-
76
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
77
-		$manager->registerBackend($backend);
78
-
79
-		$this->assertTrue($manager->userExists('foo'));
80
-	}
81
-
82
-	public function testUserExistsTooLong(): void {
83
-		/** @var \Test\Util\User\Dummy|MockObject $backend */
84
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
85
-		$backend->expects($this->never())
86
-			->method('userExists')
87
-			->with($this->equalTo('foo'))
88
-			->willReturn(true);
89
-
90
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
91
-		$manager->registerBackend($backend);
92
-
93
-		$this->assertFalse($manager->userExists('foo' . str_repeat('a', 62)));
94
-	}
95
-
96
-	public function testUserExistsSingleBackendNotExists(): void {
97
-		/**
98
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
99
-		 */
100
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
101
-		$backend->expects($this->once())
102
-			->method('userExists')
103
-			->with($this->equalTo('foo'))
104
-			->willReturn(false);
105
-
106
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
107
-		$manager->registerBackend($backend);
108
-
109
-		$this->assertFalse($manager->userExists('foo'));
110
-	}
111
-
112
-	public function testUserExistsNoBackends(): void {
113
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
114
-
115
-		$this->assertFalse($manager->userExists('foo'));
116
-	}
117
-
118
-	public function testUserExistsTwoBackendsSecondExists(): void {
119
-		/**
120
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend1
121
-		 */
122
-		$backend1 = $this->createMock(\Test\Util\User\Dummy::class);
123
-		$backend1->expects($this->once())
124
-			->method('userExists')
125
-			->with($this->equalTo('foo'))
126
-			->willReturn(false);
127
-
128
-		/**
129
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend2
130
-		 */
131
-		$backend2 = $this->createMock(\Test\Util\User\Dummy::class);
132
-		$backend2->expects($this->once())
133
-			->method('userExists')
134
-			->with($this->equalTo('foo'))
135
-			->willReturn(true);
136
-
137
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
138
-		$manager->registerBackend($backend1);
139
-		$manager->registerBackend($backend2);
140
-
141
-		$this->assertTrue($manager->userExists('foo'));
142
-	}
143
-
144
-	public function testUserExistsTwoBackendsFirstExists(): void {
145
-		/**
146
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend1
147
-		 */
148
-		$backend1 = $this->createMock(\Test\Util\User\Dummy::class);
149
-		$backend1->expects($this->once())
150
-			->method('userExists')
151
-			->with($this->equalTo('foo'))
152
-			->willReturn(true);
153
-
154
-		/**
155
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend2
156
-		 */
157
-		$backend2 = $this->createMock(\Test\Util\User\Dummy::class);
158
-		$backend2->expects($this->never())
159
-			->method('userExists');
160
-
161
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
162
-		$manager->registerBackend($backend1);
163
-		$manager->registerBackend($backend2);
164
-
165
-		$this->assertTrue($manager->userExists('foo'));
166
-	}
167
-
168
-	public function testCheckPassword(): void {
169
-		/**
170
-		 * @var \OC\User\Backend | \PHPUnit\Framework\MockObject\MockObject $backend
171
-		 */
172
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
173
-		$backend->expects($this->once())
174
-			->method('checkPassword')
175
-			->with($this->equalTo('foo'), $this->equalTo('bar'))
176
-			->willReturn(true);
177
-
178
-		$backend->expects($this->any())
179
-			->method('implementsActions')
180
-			->willReturnCallback(function ($actions) {
181
-				if ($actions === \OC\USER\BACKEND::CHECK_PASSWORD) {
182
-					return true;
183
-				} else {
184
-					return false;
185
-				}
186
-			});
187
-
188
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
189
-		$manager->registerBackend($backend);
190
-
191
-		$user = $manager->checkPassword('foo', 'bar');
192
-		$this->assertTrue($user instanceof \OC\User\User);
193
-	}
194
-
195
-	public function testCheckPasswordNotSupported(): void {
196
-		/**
197
-		 * @var \OC\User\Backend | \PHPUnit\Framework\MockObject\MockObject $backend
198
-		 */
199
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
200
-		$backend->expects($this->never())
201
-			->method('checkPassword');
202
-
203
-		$backend->expects($this->any())
204
-			->method('implementsActions')
205
-			->willReturn(false);
206
-
207
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
208
-		$manager->registerBackend($backend);
209
-
210
-		$this->assertFalse($manager->checkPassword('foo', 'bar'));
211
-	}
212
-
213
-	public function testGetOneBackendExists(): void {
214
-		/**
215
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
216
-		 */
217
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
218
-		$backend->expects($this->once())
219
-			->method('userExists')
220
-			->with($this->equalTo('foo'))
221
-			->willReturn(true);
222
-		$backend->expects($this->never())
223
-			->method('loginName2UserName');
224
-
225
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
226
-		$manager->registerBackend($backend);
227
-
228
-		$this->assertEquals('foo', $manager->get('foo')->getUID());
229
-	}
230
-
231
-	public function testGetOneBackendNotExists(): void {
232
-		/**
233
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
234
-		 */
235
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
236
-		$backend->expects($this->once())
237
-			->method('userExists')
238
-			->with($this->equalTo('foo'))
239
-			->willReturn(false);
240
-
241
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
242
-		$manager->registerBackend($backend);
243
-
244
-		$this->assertEquals(null, $manager->get('foo'));
245
-	}
246
-
247
-	public function testGetTooLong(): void {
248
-		/** @var \Test\Util\User\Dummy|MockObject $backend */
249
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
250
-		$backend->expects($this->never())
251
-			->method('userExists')
252
-			->with($this->equalTo('foo'))
253
-			->willReturn(false);
254
-
255
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
256
-		$manager->registerBackend($backend);
257
-
258
-		$this->assertEquals(null, $manager->get('foo' . str_repeat('a', 62)));
259
-	}
260
-
261
-	public function testGetOneBackendDoNotTranslateLoginNames(): void {
262
-		/**
263
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
264
-		 */
265
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
266
-		$backend->expects($this->once())
267
-			->method('userExists')
268
-			->with($this->equalTo('bLeNdEr'))
269
-			->willReturn(true);
270
-		$backend->expects($this->never())
271
-			->method('loginName2UserName');
272
-
273
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
274
-		$manager->registerBackend($backend);
275
-
276
-		$this->assertEquals('bLeNdEr', $manager->get('bLeNdEr')->getUID());
277
-	}
278
-
279
-	public function testSearchOneBackend(): void {
280
-		/**
281
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
282
-		 */
283
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
284
-		$backend->expects($this->once())
285
-			->method('getUsers')
286
-			->with($this->equalTo('fo'))
287
-			->willReturn(['foo', 'afoo', 'Afoo1', 'Bfoo']);
288
-		$backend->expects($this->never())
289
-			->method('loginName2UserName');
290
-
291
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
292
-		$manager->registerBackend($backend);
293
-
294
-		$result = $manager->search('fo');
295
-		$this->assertEquals(4, count($result));
296
-		$this->assertEquals('afoo', array_shift($result)->getUID());
297
-		$this->assertEquals('Afoo1', array_shift($result)->getUID());
298
-		$this->assertEquals('Bfoo', array_shift($result)->getUID());
299
-		$this->assertEquals('foo', array_shift($result)->getUID());
300
-	}
301
-
302
-	public function testSearchTwoBackendLimitOffset(): void {
303
-		/**
304
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend1
305
-		 */
306
-		$backend1 = $this->createMock(\Test\Util\User\Dummy::class);
307
-		$backend1->expects($this->once())
308
-			->method('getUsers')
309
-			->with($this->equalTo('fo'), $this->equalTo(3), $this->equalTo(1))
310
-			->willReturn(['foo1', 'foo2']);
311
-		$backend1->expects($this->never())
312
-			->method('loginName2UserName');
313
-
314
-		/**
315
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend2
316
-		 */
317
-		$backend2 = $this->createMock(\Test\Util\User\Dummy::class);
318
-		$backend2->expects($this->once())
319
-			->method('getUsers')
320
-			->with($this->equalTo('fo'), $this->equalTo(3), $this->equalTo(1))
321
-			->willReturn(['foo3']);
322
-		$backend2->expects($this->never())
323
-			->method('loginName2UserName');
324
-
325
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
326
-		$manager->registerBackend($backend1);
327
-		$manager->registerBackend($backend2);
328
-
329
-		$result = $manager->search('fo', 3, 1);
330
-		$this->assertEquals(3, count($result));
331
-		$this->assertEquals('foo1', array_shift($result)->getUID());
332
-		$this->assertEquals('foo2', array_shift($result)->getUID());
333
-		$this->assertEquals('foo3', array_shift($result)->getUID());
334
-	}
335
-
336
-	public static function dataCreateUserInvalid(): array {
337
-		return [
338
-			['te?st', 'foo', 'Only the following characters are allowed in a username:'
339
-				. ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
340
-			["te\tst", '', 'Only the following characters are allowed in a username:'
341
-				. ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
342
-			["te\nst", '', 'Only the following characters are allowed in a username:'
343
-				. ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
344
-			["te\rst", '', 'Only the following characters are allowed in a username:'
345
-				. ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
346
-			["te\0st", '', 'Only the following characters are allowed in a username:'
347
-				. ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
348
-			["te\x0Bst", '', 'Only the following characters are allowed in a username:'
349
-				. ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
350
-			["te\xe2st", '', 'Only the following characters are allowed in a username:'
351
-				. ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
352
-			["te\x80st", '', 'Only the following characters are allowed in a username:'
353
-				. ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
354
-			["te\x8bst", '', 'Only the following characters are allowed in a username:'
355
-				. ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
356
-			['', 'foo', 'A valid username must be provided'],
357
-			[' ', 'foo', 'A valid username must be provided'],
358
-			[' test', 'foo', 'Username contains whitespace at the beginning or at the end'],
359
-			['test ', 'foo', 'Username contains whitespace at the beginning or at the end'],
360
-			['.', 'foo', 'Username must not consist of dots only'],
361
-			['..', 'foo', 'Username must not consist of dots only'],
362
-			['.test', '', 'A valid password must be provided'],
363
-			['test', '', 'A valid password must be provided'],
364
-			['test' . str_repeat('a', 61), '', 'Login is too long'],
365
-		];
366
-	}
367
-
368
-	/**
369
-	 * @dataProvider dataCreateUserInvalid
370
-	 */
371
-	public function testCreateUserInvalid($uid, $password, $exception): void {
372
-		/** @var \Test\Util\User\Dummy|\PHPUnit\Framework\MockObject\MockObject $backend */
373
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
374
-		$backend->expects($this->once())
375
-			->method('implementsActions')
376
-			->with(\OC\User\Backend::CREATE_USER)
377
-			->willReturn(true);
378
-
379
-
380
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
381
-		$manager->registerBackend($backend);
382
-
383
-		$this->expectException(\InvalidArgumentException::class, $exception);
384
-		$manager->createUser($uid, $password);
385
-	}
386
-
387
-	public function testCreateUserSingleBackendNotExists(): void {
388
-		/**
389
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
390
-		 */
391
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
392
-		$backend->expects($this->any())
393
-			->method('implementsActions')
394
-			->willReturn(true);
395
-
396
-		$backend->expects($this->once())
397
-			->method('createUser')
398
-			->with($this->equalTo('foo'), $this->equalTo('bar'));
399
-
400
-		$backend->expects($this->once())
401
-			->method('userExists')
402
-			->with($this->equalTo('foo'))
403
-			->willReturn(false);
404
-		$backend->expects($this->never())
405
-			->method('loginName2UserName');
406
-
407
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
408
-		$manager->registerBackend($backend);
409
-
410
-		$user = $manager->createUser('foo', 'bar');
411
-		$this->assertEquals('foo', $user->getUID());
412
-	}
413
-
414
-
415
-	public function testCreateUserSingleBackendExists(): void {
416
-		$this->expectException(\Exception::class);
417
-
418
-		/**
419
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
420
-		 */
421
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
422
-		$backend->expects($this->any())
423
-			->method('implementsActions')
424
-			->willReturn(true);
425
-
426
-		$backend->expects($this->never())
427
-			->method('createUser');
428
-
429
-		$backend->expects($this->once())
430
-			->method('userExists')
431
-			->with($this->equalTo('foo'))
432
-			->willReturn(true);
433
-
434
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
435
-		$manager->registerBackend($backend);
436
-
437
-		$manager->createUser('foo', 'bar');
438
-	}
439
-
440
-	public function testCreateUserSingleBackendNotSupported(): void {
441
-		/**
442
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
443
-		 */
444
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
445
-		$backend->expects($this->any())
446
-			->method('implementsActions')
447
-			->willReturn(false);
448
-
449
-		$backend->expects($this->never())
450
-			->method('createUser');
451
-
452
-		$backend->expects($this->never())
453
-			->method('userExists');
454
-
455
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
456
-		$manager->registerBackend($backend);
457
-
458
-		$this->assertFalse($manager->createUser('foo', 'bar'));
459
-	}
460
-
461
-	public function testCreateUserNoBackends(): void {
462
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
463
-
464
-		$this->assertFalse($manager->createUser('foo', 'bar'));
465
-	}
466
-
467
-
468
-	public function testCreateUserFromBackendWithBackendError(): void {
469
-		$this->expectException(\InvalidArgumentException::class);
470
-		$this->expectExceptionMessage('Could not create account');
471
-
472
-		/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject $config */
473
-		$config = $this->createMock(IConfig::class);
474
-		/** @var \Test\Util\User\Dummy|\PHPUnit\Framework\MockObject\MockObject $backend */
475
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
476
-		$backend
477
-			->expects($this->once())
478
-			->method('createUser')
479
-			->with('MyUid', 'MyPassword')
480
-			->willReturn(false);
481
-
482
-		$manager = new Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
483
-		$manager->createUserFromBackend('MyUid', 'MyPassword', $backend);
484
-	}
485
-
486
-
487
-	public function testCreateUserTwoBackendExists(): void {
488
-		$this->expectException(\Exception::class);
489
-
490
-		/**
491
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend1
492
-		 */
493
-		$backend1 = $this->createMock(\Test\Util\User\Dummy::class);
494
-		$backend1->expects($this->any())
495
-			->method('implementsActions')
496
-			->willReturn(true);
497
-
498
-		$backend1->expects($this->never())
499
-			->method('createUser');
500
-
501
-		$backend1->expects($this->once())
502
-			->method('userExists')
503
-			->with($this->equalTo('foo'))
504
-			->willReturn(false);
505
-
506
-		/**
507
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend2
508
-		 */
509
-		$backend2 = $this->createMock(\Test\Util\User\Dummy::class);
510
-		$backend2->expects($this->any())
511
-			->method('implementsActions')
512
-			->willReturn(true);
513
-
514
-		$backend2->expects($this->never())
515
-			->method('createUser');
516
-
517
-		$backend2->expects($this->once())
518
-			->method('userExists')
519
-			->with($this->equalTo('foo'))
520
-			->willReturn(true);
521
-
522
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
523
-		$manager->registerBackend($backend1);
524
-		$manager->registerBackend($backend2);
525
-
526
-		$manager->createUser('foo', 'bar');
527
-	}
528
-
529
-	public function testCountUsersNoBackend(): void {
530
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
531
-
532
-		$result = $manager->countUsers();
533
-		$this->assertTrue(is_array($result));
534
-		$this->assertTrue(empty($result));
535
-	}
536
-
537
-	public function testCountUsersOneBackend(): void {
538
-		/**
539
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
540
-		 */
541
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
542
-		$backend->expects($this->once())
543
-			->method('countUsers')
544
-			->willReturn(7);
545
-
546
-		$backend->expects($this->once())
547
-			->method('implementsActions')
548
-			->with(\OC\USER\BACKEND::COUNT_USERS)
549
-			->willReturn(true);
550
-
551
-		$backend->expects($this->once())
552
-			->method('getBackendName')
553
-			->willReturn('Mock_Test_Util_User_Dummy');
554
-
555
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
556
-		$manager->registerBackend($backend);
557
-
558
-		$result = $manager->countUsers();
559
-		$keys = array_keys($result);
560
-		$this->assertTrue(strpos($keys[0], 'Mock_Test_Util_User_Dummy') !== false);
561
-
562
-		$users = array_shift($result);
563
-		$this->assertEquals(7, $users);
564
-	}
565
-
566
-	public function testCountUsersTwoBackends(): void {
567
-		/**
568
-		 * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
569
-		 */
570
-		$backend1 = $this->createMock(\Test\Util\User\Dummy::class);
571
-		$backend1->expects($this->once())
572
-			->method('countUsers')
573
-			->willReturn(7);
574
-
575
-		$backend1->expects($this->once())
576
-			->method('implementsActions')
577
-			->with(\OC\USER\BACKEND::COUNT_USERS)
578
-			->willReturn(true);
579
-		$backend1->expects($this->once())
580
-			->method('getBackendName')
581
-			->willReturn('Mock_Test_Util_User_Dummy');
582
-
583
-		$backend2 = $this->createMock(\Test\Util\User\Dummy::class);
584
-		$backend2->expects($this->once())
585
-			->method('countUsers')
586
-			->willReturn(16);
587
-
588
-		$backend2->expects($this->once())
589
-			->method('implementsActions')
590
-			->with(\OC\USER\BACKEND::COUNT_USERS)
591
-			->willReturn(true);
592
-		$backend2->expects($this->once())
593
-			->method('getBackendName')
594
-			->willReturn('Mock_Test_Util_User_Dummy');
595
-
596
-		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
597
-		$manager->registerBackend($backend1);
598
-		$manager->registerBackend($backend2);
599
-
600
-		$result = $manager->countUsers();
601
-		//because the backends have the same class name, only one value expected
602
-		$this->assertEquals(1, count($result));
603
-		$keys = array_keys($result);
604
-		$this->assertTrue(strpos($keys[0], 'Mock_Test_Util_User_Dummy') !== false);
605
-
606
-		$users = array_shift($result);
607
-		//users from backends shall be summed up
608
-		$this->assertEquals(7 + 16, $users);
609
-	}
610
-
611
-	public function testCountUsersOnlyDisabled(): void {
612
-		$manager = \OCP\Server::get(IUserManager::class);
613
-		// count other users in the db before adding our own
614
-		$countBefore = $manager->countDisabledUsers();
615
-
616
-		//Add test users
617
-		$user1 = $manager->createUser('testdisabledcount1', 'testdisabledcount1');
618
-
619
-		$user2 = $manager->createUser('testdisabledcount2', 'testdisabledcount2');
620
-		$user2->setEnabled(false);
621
-
622
-		$user3 = $manager->createUser('testdisabledcount3', 'testdisabledcount3');
623
-
624
-		$user4 = $manager->createUser('testdisabledcount4', 'testdisabledcount4');
625
-		$user4->setEnabled(false);
626
-
627
-		$this->assertEquals($countBefore + 2, $manager->countDisabledUsers());
628
-
629
-		//cleanup
630
-		$user1->delete();
631
-		$user2->delete();
632
-		$user3->delete();
633
-		$user4->delete();
634
-	}
635
-
636
-	public function testCountUsersOnlySeen(): void {
637
-		$manager = \OCP\Server::get(IUserManager::class);
638
-		// count other users in the db before adding our own
639
-		$countBefore = $manager->countSeenUsers();
640
-
641
-		//Add test users
642
-		$user1 = $manager->createUser('testseencount1', 'testseencount1');
643
-		$user1->updateLastLoginTimestamp();
644
-
645
-		$user2 = $manager->createUser('testseencount2', 'testseencount2');
646
-		$user2->updateLastLoginTimestamp();
647
-
648
-		$user3 = $manager->createUser('testseencount3', 'testseencount3');
649
-
650
-		$user4 = $manager->createUser('testseencount4', 'testseencount4');
651
-		$user4->updateLastLoginTimestamp();
652
-
653
-		$this->assertEquals($countBefore + 3, $manager->countSeenUsers());
654
-
655
-		//cleanup
656
-		$user1->delete();
657
-		$user2->delete();
658
-		$user3->delete();
659
-		$user4->delete();
660
-	}
661
-
662
-	public function testCallForSeenUsers(): void {
663
-		$manager = \OCP\Server::get(IUserManager::class);
664
-		// count other users in the db before adding our own
665
-		$count = 0;
666
-		$function = function (IUser $user) use (&$count) {
667
-			$count++;
668
-		};
669
-		$manager->callForAllUsers($function, '', true);
670
-		$countBefore = $count;
671
-
672
-		//Add test users
673
-		$user1 = $manager->createUser('testseen1', 'testseen10');
674
-		$user1->updateLastLoginTimestamp();
675
-
676
-		$user2 = $manager->createUser('testseen2', 'testseen20');
677
-		$user2->updateLastLoginTimestamp();
678
-
679
-		$user3 = $manager->createUser('testseen3', 'testseen30');
680
-
681
-		$user4 = $manager->createUser('testseen4', 'testseen40');
682
-		$user4->updateLastLoginTimestamp();
683
-
684
-		$count = 0;
685
-		$manager->callForAllUsers($function, '', true);
686
-
687
-		$this->assertEquals($countBefore + 3, $count);
688
-
689
-		//cleanup
690
-		$user1->delete();
691
-		$user2->delete();
692
-		$user3->delete();
693
-		$user4->delete();
694
-	}
695
-
696
-	/**
697
-	 * @runInSeparateProcess
698
-	 * @preserveGlobalState disabled
699
-	 */
700
-	public function testRecentlyActive(): void {
701
-		$config = \OCP\Server::get(IConfig::class);
702
-		$manager = \OCP\Server::get(IUserManager::class);
703
-
704
-		// Create some users
705
-		$now = (string)time();
706
-		$user1 = $manager->createUser('test_active_1', 'test_active_1');
707
-		$config->setUserValue('test_active_1', 'login', 'lastLogin', $now);
708
-		$user1->setDisplayName('test active 1');
709
-		$user1->setSystemEMailAddress('[email protected]');
710
-
711
-		$user2 = $manager->createUser('TEST_ACTIVE_2_FRED', 'TEST_ACTIVE_2');
712
-		$config->setUserValue('TEST_ACTIVE_2_FRED', 'login', 'lastLogin', $now);
713
-		$user2->setDisplayName('TEST ACTIVE 2 UPPER');
714
-		$user2->setSystemEMailAddress('[email protected]');
715
-
716
-		$user3 = $manager->createUser('test_active_3', 'test_active_3');
717
-		$config->setUserValue('test_active_3', 'login', 'lastLogin', $now + 1);
718
-		$user3->setDisplayName('test active 3');
719
-
720
-		$user4 = $manager->createUser('test_active_4', 'test_active_4');
721
-		$config->setUserValue('test_active_4', 'login', 'lastLogin', $now);
722
-		$user4->setDisplayName('Test Active 4');
723
-
724
-		$user5 = $manager->createUser('test_inactive_1', 'test_inactive_1');
725
-		$user5->setDisplayName('Test Inactive 1');
726
-		$user2->setSystemEMailAddress('[email protected]');
727
-
728
-		// Search recently active
729
-		//  - No search, case-insensitive order
730
-		$users = $manager->getLastLoggedInUsers(4);
731
-		$this->assertEquals(['test_active_3', 'test_active_1', 'TEST_ACTIVE_2_FRED', 'test_active_4'], $users);
732
-		//  - Search, case-insensitive order
733
-		$users = $manager->getLastLoggedInUsers(search: 'act');
734
-		$this->assertEquals(['test_active_3', 'test_active_1', 'TEST_ACTIVE_2_FRED', 'test_active_4'], $users);
735
-		//  - No search with offset
736
-		$users = $manager->getLastLoggedInUsers(2, 2);
737
-		$this->assertEquals(['TEST_ACTIVE_2_FRED', 'test_active_4'], $users);
738
-		//  - Case insensitive search (email)
739
-		$users = $manager->getLastLoggedInUsers(search: 'active.com');
740
-		$this->assertEquals(['test_active_1', 'TEST_ACTIVE_2_FRED'], $users);
741
-		//  - Case insensitive search (display name)
742
-		$users = $manager->getLastLoggedInUsers(search: 'upper');
743
-		$this->assertEquals(['TEST_ACTIVE_2_FRED'], $users);
744
-		//  - Case insensitive search (uid)
745
-		$users = $manager->getLastLoggedInUsers(search: 'fred');
746
-		$this->assertEquals(['TEST_ACTIVE_2_FRED'], $users);
747
-
748
-		// Delete users and config keys
749
-		$user1->delete();
750
-		$user2->delete();
751
-		$user3->delete();
752
-		$user4->delete();
753
-		$user5->delete();
754
-	}
755
-
756
-	public function testDeleteUser(): void {
757
-		$config = $this->getMockBuilder(AllConfig::class)
758
-			->disableOriginalConstructor()
759
-			->getMock();
760
-		$config
761
-			->expects($this->any())
762
-			->method('getUserValue')
763
-			->willReturnArgument(3);
764
-		$config
765
-			->expects($this->any())
766
-			->method('getAppValue')
767
-			->willReturnArgument(2);
768
-
769
-		$manager = new \OC\User\Manager($config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
770
-		$backend = new \Test\Util\User\Dummy();
771
-
772
-		$manager->registerBackend($backend);
773
-		$backend->createUser('foo', 'bar');
774
-		$this->assertTrue($manager->userExists('foo'));
775
-		$manager->get('foo')->delete();
776
-		$this->assertFalse($manager->userExists('foo'));
777
-	}
778
-
779
-	public function testGetByEmail(): void {
780
-		$config = $this->getMockBuilder(AllConfig::class)
781
-			->disableOriginalConstructor()
782
-			->getMock();
783
-		$config
784
-			->expects($this->once())
785
-			->method('getUsersForUserValueCaseInsensitive')
786
-			->with('settings', 'email', '[email protected]')
787
-			->willReturn(['uid1', 'uid99', 'uid2']);
788
-
789
-		$backend = $this->createMock(\Test\Util\User\Dummy::class);
790
-		$backend->expects($this->exactly(3))
791
-			->method('userExists')
792
-			->willReturnMap([
793
-				['uid1', true],
794
-				['uid99', false],
795
-				['uid2', true]
796
-			]);
797
-
798
-		$manager = new \OC\User\Manager($config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
799
-		$manager->registerBackend($backend);
800
-
801
-		$users = $manager->getByEmail('[email protected]');
802
-		$this->assertCount(2, $users);
803
-		$this->assertEquals('uid1', $users[0]->getUID());
804
-		$this->assertEquals('uid2', $users[1]->getUID());
805
-	}
31
+    /** @var IConfig */
32
+    private $config;
33
+    /** @var IEventDispatcher */
34
+    private $eventDispatcher;
35
+    /** @var ICacheFactory */
36
+    private $cacheFactory;
37
+    /** @var ICache */
38
+    private $cache;
39
+    /** @var LoggerInterface */
40
+    private $logger;
41
+
42
+    protected function setUp(): void {
43
+        parent::setUp();
44
+
45
+        $this->config = $this->createMock(IConfig::class);
46
+        $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
47
+        $this->cacheFactory = $this->createMock(ICacheFactory::class);
48
+        $this->cache = $this->createMock(ICache::class);
49
+        $this->logger = $this->createMock(LoggerInterface::class);
50
+
51
+        $this->cacheFactory->method('createDistributed')
52
+            ->willReturn($this->cache);
53
+    }
54
+
55
+    public function testGetBackends(): void {
56
+        $userDummyBackend = $this->createMock(\Test\Util\User\Dummy::class);
57
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
58
+        $manager->registerBackend($userDummyBackend);
59
+        $this->assertEquals([$userDummyBackend], $manager->getBackends());
60
+        $dummyDatabaseBackend = $this->createMock(Database::class);
61
+        $manager->registerBackend($dummyDatabaseBackend);
62
+        $this->assertEquals([$userDummyBackend, $dummyDatabaseBackend], $manager->getBackends());
63
+    }
64
+
65
+
66
+    public function testUserExistsSingleBackendExists(): void {
67
+        /**
68
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
69
+         */
70
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
71
+        $backend->expects($this->once())
72
+            ->method('userExists')
73
+            ->with($this->equalTo('foo'))
74
+            ->willReturn(true);
75
+
76
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
77
+        $manager->registerBackend($backend);
78
+
79
+        $this->assertTrue($manager->userExists('foo'));
80
+    }
81
+
82
+    public function testUserExistsTooLong(): void {
83
+        /** @var \Test\Util\User\Dummy|MockObject $backend */
84
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
85
+        $backend->expects($this->never())
86
+            ->method('userExists')
87
+            ->with($this->equalTo('foo'))
88
+            ->willReturn(true);
89
+
90
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
91
+        $manager->registerBackend($backend);
92
+
93
+        $this->assertFalse($manager->userExists('foo' . str_repeat('a', 62)));
94
+    }
95
+
96
+    public function testUserExistsSingleBackendNotExists(): void {
97
+        /**
98
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
99
+         */
100
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
101
+        $backend->expects($this->once())
102
+            ->method('userExists')
103
+            ->with($this->equalTo('foo'))
104
+            ->willReturn(false);
105
+
106
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
107
+        $manager->registerBackend($backend);
108
+
109
+        $this->assertFalse($manager->userExists('foo'));
110
+    }
111
+
112
+    public function testUserExistsNoBackends(): void {
113
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
114
+
115
+        $this->assertFalse($manager->userExists('foo'));
116
+    }
117
+
118
+    public function testUserExistsTwoBackendsSecondExists(): void {
119
+        /**
120
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend1
121
+         */
122
+        $backend1 = $this->createMock(\Test\Util\User\Dummy::class);
123
+        $backend1->expects($this->once())
124
+            ->method('userExists')
125
+            ->with($this->equalTo('foo'))
126
+            ->willReturn(false);
127
+
128
+        /**
129
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend2
130
+         */
131
+        $backend2 = $this->createMock(\Test\Util\User\Dummy::class);
132
+        $backend2->expects($this->once())
133
+            ->method('userExists')
134
+            ->with($this->equalTo('foo'))
135
+            ->willReturn(true);
136
+
137
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
138
+        $manager->registerBackend($backend1);
139
+        $manager->registerBackend($backend2);
140
+
141
+        $this->assertTrue($manager->userExists('foo'));
142
+    }
143
+
144
+    public function testUserExistsTwoBackendsFirstExists(): void {
145
+        /**
146
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend1
147
+         */
148
+        $backend1 = $this->createMock(\Test\Util\User\Dummy::class);
149
+        $backend1->expects($this->once())
150
+            ->method('userExists')
151
+            ->with($this->equalTo('foo'))
152
+            ->willReturn(true);
153
+
154
+        /**
155
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend2
156
+         */
157
+        $backend2 = $this->createMock(\Test\Util\User\Dummy::class);
158
+        $backend2->expects($this->never())
159
+            ->method('userExists');
160
+
161
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
162
+        $manager->registerBackend($backend1);
163
+        $manager->registerBackend($backend2);
164
+
165
+        $this->assertTrue($manager->userExists('foo'));
166
+    }
167
+
168
+    public function testCheckPassword(): void {
169
+        /**
170
+         * @var \OC\User\Backend | \PHPUnit\Framework\MockObject\MockObject $backend
171
+         */
172
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
173
+        $backend->expects($this->once())
174
+            ->method('checkPassword')
175
+            ->with($this->equalTo('foo'), $this->equalTo('bar'))
176
+            ->willReturn(true);
177
+
178
+        $backend->expects($this->any())
179
+            ->method('implementsActions')
180
+            ->willReturnCallback(function ($actions) {
181
+                if ($actions === \OC\USER\BACKEND::CHECK_PASSWORD) {
182
+                    return true;
183
+                } else {
184
+                    return false;
185
+                }
186
+            });
187
+
188
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
189
+        $manager->registerBackend($backend);
190
+
191
+        $user = $manager->checkPassword('foo', 'bar');
192
+        $this->assertTrue($user instanceof \OC\User\User);
193
+    }
194
+
195
+    public function testCheckPasswordNotSupported(): void {
196
+        /**
197
+         * @var \OC\User\Backend | \PHPUnit\Framework\MockObject\MockObject $backend
198
+         */
199
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
200
+        $backend->expects($this->never())
201
+            ->method('checkPassword');
202
+
203
+        $backend->expects($this->any())
204
+            ->method('implementsActions')
205
+            ->willReturn(false);
206
+
207
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
208
+        $manager->registerBackend($backend);
209
+
210
+        $this->assertFalse($manager->checkPassword('foo', 'bar'));
211
+    }
212
+
213
+    public function testGetOneBackendExists(): void {
214
+        /**
215
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
216
+         */
217
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
218
+        $backend->expects($this->once())
219
+            ->method('userExists')
220
+            ->with($this->equalTo('foo'))
221
+            ->willReturn(true);
222
+        $backend->expects($this->never())
223
+            ->method('loginName2UserName');
224
+
225
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
226
+        $manager->registerBackend($backend);
227
+
228
+        $this->assertEquals('foo', $manager->get('foo')->getUID());
229
+    }
230
+
231
+    public function testGetOneBackendNotExists(): void {
232
+        /**
233
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
234
+         */
235
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
236
+        $backend->expects($this->once())
237
+            ->method('userExists')
238
+            ->with($this->equalTo('foo'))
239
+            ->willReturn(false);
240
+
241
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
242
+        $manager->registerBackend($backend);
243
+
244
+        $this->assertEquals(null, $manager->get('foo'));
245
+    }
246
+
247
+    public function testGetTooLong(): void {
248
+        /** @var \Test\Util\User\Dummy|MockObject $backend */
249
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
250
+        $backend->expects($this->never())
251
+            ->method('userExists')
252
+            ->with($this->equalTo('foo'))
253
+            ->willReturn(false);
254
+
255
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
256
+        $manager->registerBackend($backend);
257
+
258
+        $this->assertEquals(null, $manager->get('foo' . str_repeat('a', 62)));
259
+    }
260
+
261
+    public function testGetOneBackendDoNotTranslateLoginNames(): void {
262
+        /**
263
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
264
+         */
265
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
266
+        $backend->expects($this->once())
267
+            ->method('userExists')
268
+            ->with($this->equalTo('bLeNdEr'))
269
+            ->willReturn(true);
270
+        $backend->expects($this->never())
271
+            ->method('loginName2UserName');
272
+
273
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
274
+        $manager->registerBackend($backend);
275
+
276
+        $this->assertEquals('bLeNdEr', $manager->get('bLeNdEr')->getUID());
277
+    }
278
+
279
+    public function testSearchOneBackend(): void {
280
+        /**
281
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
282
+         */
283
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
284
+        $backend->expects($this->once())
285
+            ->method('getUsers')
286
+            ->with($this->equalTo('fo'))
287
+            ->willReturn(['foo', 'afoo', 'Afoo1', 'Bfoo']);
288
+        $backend->expects($this->never())
289
+            ->method('loginName2UserName');
290
+
291
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
292
+        $manager->registerBackend($backend);
293
+
294
+        $result = $manager->search('fo');
295
+        $this->assertEquals(4, count($result));
296
+        $this->assertEquals('afoo', array_shift($result)->getUID());
297
+        $this->assertEquals('Afoo1', array_shift($result)->getUID());
298
+        $this->assertEquals('Bfoo', array_shift($result)->getUID());
299
+        $this->assertEquals('foo', array_shift($result)->getUID());
300
+    }
301
+
302
+    public function testSearchTwoBackendLimitOffset(): void {
303
+        /**
304
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend1
305
+         */
306
+        $backend1 = $this->createMock(\Test\Util\User\Dummy::class);
307
+        $backend1->expects($this->once())
308
+            ->method('getUsers')
309
+            ->with($this->equalTo('fo'), $this->equalTo(3), $this->equalTo(1))
310
+            ->willReturn(['foo1', 'foo2']);
311
+        $backend1->expects($this->never())
312
+            ->method('loginName2UserName');
313
+
314
+        /**
315
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend2
316
+         */
317
+        $backend2 = $this->createMock(\Test\Util\User\Dummy::class);
318
+        $backend2->expects($this->once())
319
+            ->method('getUsers')
320
+            ->with($this->equalTo('fo'), $this->equalTo(3), $this->equalTo(1))
321
+            ->willReturn(['foo3']);
322
+        $backend2->expects($this->never())
323
+            ->method('loginName2UserName');
324
+
325
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
326
+        $manager->registerBackend($backend1);
327
+        $manager->registerBackend($backend2);
328
+
329
+        $result = $manager->search('fo', 3, 1);
330
+        $this->assertEquals(3, count($result));
331
+        $this->assertEquals('foo1', array_shift($result)->getUID());
332
+        $this->assertEquals('foo2', array_shift($result)->getUID());
333
+        $this->assertEquals('foo3', array_shift($result)->getUID());
334
+    }
335
+
336
+    public static function dataCreateUserInvalid(): array {
337
+        return [
338
+            ['te?st', 'foo', 'Only the following characters are allowed in a username:'
339
+                . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
340
+            ["te\tst", '', 'Only the following characters are allowed in a username:'
341
+                . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
342
+            ["te\nst", '', 'Only the following characters are allowed in a username:'
343
+                . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
344
+            ["te\rst", '', 'Only the following characters are allowed in a username:'
345
+                . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
346
+            ["te\0st", '', 'Only the following characters are allowed in a username:'
347
+                . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
348
+            ["te\x0Bst", '', 'Only the following characters are allowed in a username:'
349
+                . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
350
+            ["te\xe2st", '', 'Only the following characters are allowed in a username:'
351
+                . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
352
+            ["te\x80st", '', 'Only the following characters are allowed in a username:'
353
+                . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
354
+            ["te\x8bst", '', 'Only the following characters are allowed in a username:'
355
+                . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'],
356
+            ['', 'foo', 'A valid username must be provided'],
357
+            [' ', 'foo', 'A valid username must be provided'],
358
+            [' test', 'foo', 'Username contains whitespace at the beginning or at the end'],
359
+            ['test ', 'foo', 'Username contains whitespace at the beginning or at the end'],
360
+            ['.', 'foo', 'Username must not consist of dots only'],
361
+            ['..', 'foo', 'Username must not consist of dots only'],
362
+            ['.test', '', 'A valid password must be provided'],
363
+            ['test', '', 'A valid password must be provided'],
364
+            ['test' . str_repeat('a', 61), '', 'Login is too long'],
365
+        ];
366
+    }
367
+
368
+    /**
369
+     * @dataProvider dataCreateUserInvalid
370
+     */
371
+    public function testCreateUserInvalid($uid, $password, $exception): void {
372
+        /** @var \Test\Util\User\Dummy|\PHPUnit\Framework\MockObject\MockObject $backend */
373
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
374
+        $backend->expects($this->once())
375
+            ->method('implementsActions')
376
+            ->with(\OC\User\Backend::CREATE_USER)
377
+            ->willReturn(true);
378
+
379
+
380
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
381
+        $manager->registerBackend($backend);
382
+
383
+        $this->expectException(\InvalidArgumentException::class, $exception);
384
+        $manager->createUser($uid, $password);
385
+    }
386
+
387
+    public function testCreateUserSingleBackendNotExists(): void {
388
+        /**
389
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
390
+         */
391
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
392
+        $backend->expects($this->any())
393
+            ->method('implementsActions')
394
+            ->willReturn(true);
395
+
396
+        $backend->expects($this->once())
397
+            ->method('createUser')
398
+            ->with($this->equalTo('foo'), $this->equalTo('bar'));
399
+
400
+        $backend->expects($this->once())
401
+            ->method('userExists')
402
+            ->with($this->equalTo('foo'))
403
+            ->willReturn(false);
404
+        $backend->expects($this->never())
405
+            ->method('loginName2UserName');
406
+
407
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
408
+        $manager->registerBackend($backend);
409
+
410
+        $user = $manager->createUser('foo', 'bar');
411
+        $this->assertEquals('foo', $user->getUID());
412
+    }
413
+
414
+
415
+    public function testCreateUserSingleBackendExists(): void {
416
+        $this->expectException(\Exception::class);
417
+
418
+        /**
419
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
420
+         */
421
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
422
+        $backend->expects($this->any())
423
+            ->method('implementsActions')
424
+            ->willReturn(true);
425
+
426
+        $backend->expects($this->never())
427
+            ->method('createUser');
428
+
429
+        $backend->expects($this->once())
430
+            ->method('userExists')
431
+            ->with($this->equalTo('foo'))
432
+            ->willReturn(true);
433
+
434
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
435
+        $manager->registerBackend($backend);
436
+
437
+        $manager->createUser('foo', 'bar');
438
+    }
439
+
440
+    public function testCreateUserSingleBackendNotSupported(): void {
441
+        /**
442
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
443
+         */
444
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
445
+        $backend->expects($this->any())
446
+            ->method('implementsActions')
447
+            ->willReturn(false);
448
+
449
+        $backend->expects($this->never())
450
+            ->method('createUser');
451
+
452
+        $backend->expects($this->never())
453
+            ->method('userExists');
454
+
455
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
456
+        $manager->registerBackend($backend);
457
+
458
+        $this->assertFalse($manager->createUser('foo', 'bar'));
459
+    }
460
+
461
+    public function testCreateUserNoBackends(): void {
462
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
463
+
464
+        $this->assertFalse($manager->createUser('foo', 'bar'));
465
+    }
466
+
467
+
468
+    public function testCreateUserFromBackendWithBackendError(): void {
469
+        $this->expectException(\InvalidArgumentException::class);
470
+        $this->expectExceptionMessage('Could not create account');
471
+
472
+        /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject $config */
473
+        $config = $this->createMock(IConfig::class);
474
+        /** @var \Test\Util\User\Dummy|\PHPUnit\Framework\MockObject\MockObject $backend */
475
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
476
+        $backend
477
+            ->expects($this->once())
478
+            ->method('createUser')
479
+            ->with('MyUid', 'MyPassword')
480
+            ->willReturn(false);
481
+
482
+        $manager = new Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
483
+        $manager->createUserFromBackend('MyUid', 'MyPassword', $backend);
484
+    }
485
+
486
+
487
+    public function testCreateUserTwoBackendExists(): void {
488
+        $this->expectException(\Exception::class);
489
+
490
+        /**
491
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend1
492
+         */
493
+        $backend1 = $this->createMock(\Test\Util\User\Dummy::class);
494
+        $backend1->expects($this->any())
495
+            ->method('implementsActions')
496
+            ->willReturn(true);
497
+
498
+        $backend1->expects($this->never())
499
+            ->method('createUser');
500
+
501
+        $backend1->expects($this->once())
502
+            ->method('userExists')
503
+            ->with($this->equalTo('foo'))
504
+            ->willReturn(false);
505
+
506
+        /**
507
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend2
508
+         */
509
+        $backend2 = $this->createMock(\Test\Util\User\Dummy::class);
510
+        $backend2->expects($this->any())
511
+            ->method('implementsActions')
512
+            ->willReturn(true);
513
+
514
+        $backend2->expects($this->never())
515
+            ->method('createUser');
516
+
517
+        $backend2->expects($this->once())
518
+            ->method('userExists')
519
+            ->with($this->equalTo('foo'))
520
+            ->willReturn(true);
521
+
522
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
523
+        $manager->registerBackend($backend1);
524
+        $manager->registerBackend($backend2);
525
+
526
+        $manager->createUser('foo', 'bar');
527
+    }
528
+
529
+    public function testCountUsersNoBackend(): void {
530
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
531
+
532
+        $result = $manager->countUsers();
533
+        $this->assertTrue(is_array($result));
534
+        $this->assertTrue(empty($result));
535
+    }
536
+
537
+    public function testCountUsersOneBackend(): void {
538
+        /**
539
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
540
+         */
541
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
542
+        $backend->expects($this->once())
543
+            ->method('countUsers')
544
+            ->willReturn(7);
545
+
546
+        $backend->expects($this->once())
547
+            ->method('implementsActions')
548
+            ->with(\OC\USER\BACKEND::COUNT_USERS)
549
+            ->willReturn(true);
550
+
551
+        $backend->expects($this->once())
552
+            ->method('getBackendName')
553
+            ->willReturn('Mock_Test_Util_User_Dummy');
554
+
555
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
556
+        $manager->registerBackend($backend);
557
+
558
+        $result = $manager->countUsers();
559
+        $keys = array_keys($result);
560
+        $this->assertTrue(strpos($keys[0], 'Mock_Test_Util_User_Dummy') !== false);
561
+
562
+        $users = array_shift($result);
563
+        $this->assertEquals(7, $users);
564
+    }
565
+
566
+    public function testCountUsersTwoBackends(): void {
567
+        /**
568
+         * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
569
+         */
570
+        $backend1 = $this->createMock(\Test\Util\User\Dummy::class);
571
+        $backend1->expects($this->once())
572
+            ->method('countUsers')
573
+            ->willReturn(7);
574
+
575
+        $backend1->expects($this->once())
576
+            ->method('implementsActions')
577
+            ->with(\OC\USER\BACKEND::COUNT_USERS)
578
+            ->willReturn(true);
579
+        $backend1->expects($this->once())
580
+            ->method('getBackendName')
581
+            ->willReturn('Mock_Test_Util_User_Dummy');
582
+
583
+        $backend2 = $this->createMock(\Test\Util\User\Dummy::class);
584
+        $backend2->expects($this->once())
585
+            ->method('countUsers')
586
+            ->willReturn(16);
587
+
588
+        $backend2->expects($this->once())
589
+            ->method('implementsActions')
590
+            ->with(\OC\USER\BACKEND::COUNT_USERS)
591
+            ->willReturn(true);
592
+        $backend2->expects($this->once())
593
+            ->method('getBackendName')
594
+            ->willReturn('Mock_Test_Util_User_Dummy');
595
+
596
+        $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
597
+        $manager->registerBackend($backend1);
598
+        $manager->registerBackend($backend2);
599
+
600
+        $result = $manager->countUsers();
601
+        //because the backends have the same class name, only one value expected
602
+        $this->assertEquals(1, count($result));
603
+        $keys = array_keys($result);
604
+        $this->assertTrue(strpos($keys[0], 'Mock_Test_Util_User_Dummy') !== false);
605
+
606
+        $users = array_shift($result);
607
+        //users from backends shall be summed up
608
+        $this->assertEquals(7 + 16, $users);
609
+    }
610
+
611
+    public function testCountUsersOnlyDisabled(): void {
612
+        $manager = \OCP\Server::get(IUserManager::class);
613
+        // count other users in the db before adding our own
614
+        $countBefore = $manager->countDisabledUsers();
615
+
616
+        //Add test users
617
+        $user1 = $manager->createUser('testdisabledcount1', 'testdisabledcount1');
618
+
619
+        $user2 = $manager->createUser('testdisabledcount2', 'testdisabledcount2');
620
+        $user2->setEnabled(false);
621
+
622
+        $user3 = $manager->createUser('testdisabledcount3', 'testdisabledcount3');
623
+
624
+        $user4 = $manager->createUser('testdisabledcount4', 'testdisabledcount4');
625
+        $user4->setEnabled(false);
626
+
627
+        $this->assertEquals($countBefore + 2, $manager->countDisabledUsers());
628
+
629
+        //cleanup
630
+        $user1->delete();
631
+        $user2->delete();
632
+        $user3->delete();
633
+        $user4->delete();
634
+    }
635
+
636
+    public function testCountUsersOnlySeen(): void {
637
+        $manager = \OCP\Server::get(IUserManager::class);
638
+        // count other users in the db before adding our own
639
+        $countBefore = $manager->countSeenUsers();
640
+
641
+        //Add test users
642
+        $user1 = $manager->createUser('testseencount1', 'testseencount1');
643
+        $user1->updateLastLoginTimestamp();
644
+
645
+        $user2 = $manager->createUser('testseencount2', 'testseencount2');
646
+        $user2->updateLastLoginTimestamp();
647
+
648
+        $user3 = $manager->createUser('testseencount3', 'testseencount3');
649
+
650
+        $user4 = $manager->createUser('testseencount4', 'testseencount4');
651
+        $user4->updateLastLoginTimestamp();
652
+
653
+        $this->assertEquals($countBefore + 3, $manager->countSeenUsers());
654
+
655
+        //cleanup
656
+        $user1->delete();
657
+        $user2->delete();
658
+        $user3->delete();
659
+        $user4->delete();
660
+    }
661
+
662
+    public function testCallForSeenUsers(): void {
663
+        $manager = \OCP\Server::get(IUserManager::class);
664
+        // count other users in the db before adding our own
665
+        $count = 0;
666
+        $function = function (IUser $user) use (&$count) {
667
+            $count++;
668
+        };
669
+        $manager->callForAllUsers($function, '', true);
670
+        $countBefore = $count;
671
+
672
+        //Add test users
673
+        $user1 = $manager->createUser('testseen1', 'testseen10');
674
+        $user1->updateLastLoginTimestamp();
675
+
676
+        $user2 = $manager->createUser('testseen2', 'testseen20');
677
+        $user2->updateLastLoginTimestamp();
678
+
679
+        $user3 = $manager->createUser('testseen3', 'testseen30');
680
+
681
+        $user4 = $manager->createUser('testseen4', 'testseen40');
682
+        $user4->updateLastLoginTimestamp();
683
+
684
+        $count = 0;
685
+        $manager->callForAllUsers($function, '', true);
686
+
687
+        $this->assertEquals($countBefore + 3, $count);
688
+
689
+        //cleanup
690
+        $user1->delete();
691
+        $user2->delete();
692
+        $user3->delete();
693
+        $user4->delete();
694
+    }
695
+
696
+    /**
697
+     * @runInSeparateProcess
698
+     * @preserveGlobalState disabled
699
+     */
700
+    public function testRecentlyActive(): void {
701
+        $config = \OCP\Server::get(IConfig::class);
702
+        $manager = \OCP\Server::get(IUserManager::class);
703
+
704
+        // Create some users
705
+        $now = (string)time();
706
+        $user1 = $manager->createUser('test_active_1', 'test_active_1');
707
+        $config->setUserValue('test_active_1', 'login', 'lastLogin', $now);
708
+        $user1->setDisplayName('test active 1');
709
+        $user1->setSystemEMailAddress('[email protected]');
710
+
711
+        $user2 = $manager->createUser('TEST_ACTIVE_2_FRED', 'TEST_ACTIVE_2');
712
+        $config->setUserValue('TEST_ACTIVE_2_FRED', 'login', 'lastLogin', $now);
713
+        $user2->setDisplayName('TEST ACTIVE 2 UPPER');
714
+        $user2->setSystemEMailAddress('[email protected]');
715
+
716
+        $user3 = $manager->createUser('test_active_3', 'test_active_3');
717
+        $config->setUserValue('test_active_3', 'login', 'lastLogin', $now + 1);
718
+        $user3->setDisplayName('test active 3');
719
+
720
+        $user4 = $manager->createUser('test_active_4', 'test_active_4');
721
+        $config->setUserValue('test_active_4', 'login', 'lastLogin', $now);
722
+        $user4->setDisplayName('Test Active 4');
723
+
724
+        $user5 = $manager->createUser('test_inactive_1', 'test_inactive_1');
725
+        $user5->setDisplayName('Test Inactive 1');
726
+        $user2->setSystemEMailAddress('[email protected]');
727
+
728
+        // Search recently active
729
+        //  - No search, case-insensitive order
730
+        $users = $manager->getLastLoggedInUsers(4);
731
+        $this->assertEquals(['test_active_3', 'test_active_1', 'TEST_ACTIVE_2_FRED', 'test_active_4'], $users);
732
+        //  - Search, case-insensitive order
733
+        $users = $manager->getLastLoggedInUsers(search: 'act');
734
+        $this->assertEquals(['test_active_3', 'test_active_1', 'TEST_ACTIVE_2_FRED', 'test_active_4'], $users);
735
+        //  - No search with offset
736
+        $users = $manager->getLastLoggedInUsers(2, 2);
737
+        $this->assertEquals(['TEST_ACTIVE_2_FRED', 'test_active_4'], $users);
738
+        //  - Case insensitive search (email)
739
+        $users = $manager->getLastLoggedInUsers(search: 'active.com');
740
+        $this->assertEquals(['test_active_1', 'TEST_ACTIVE_2_FRED'], $users);
741
+        //  - Case insensitive search (display name)
742
+        $users = $manager->getLastLoggedInUsers(search: 'upper');
743
+        $this->assertEquals(['TEST_ACTIVE_2_FRED'], $users);
744
+        //  - Case insensitive search (uid)
745
+        $users = $manager->getLastLoggedInUsers(search: 'fred');
746
+        $this->assertEquals(['TEST_ACTIVE_2_FRED'], $users);
747
+
748
+        // Delete users and config keys
749
+        $user1->delete();
750
+        $user2->delete();
751
+        $user3->delete();
752
+        $user4->delete();
753
+        $user5->delete();
754
+    }
755
+
756
+    public function testDeleteUser(): void {
757
+        $config = $this->getMockBuilder(AllConfig::class)
758
+            ->disableOriginalConstructor()
759
+            ->getMock();
760
+        $config
761
+            ->expects($this->any())
762
+            ->method('getUserValue')
763
+            ->willReturnArgument(3);
764
+        $config
765
+            ->expects($this->any())
766
+            ->method('getAppValue')
767
+            ->willReturnArgument(2);
768
+
769
+        $manager = new \OC\User\Manager($config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
770
+        $backend = new \Test\Util\User\Dummy();
771
+
772
+        $manager->registerBackend($backend);
773
+        $backend->createUser('foo', 'bar');
774
+        $this->assertTrue($manager->userExists('foo'));
775
+        $manager->get('foo')->delete();
776
+        $this->assertFalse($manager->userExists('foo'));
777
+    }
778
+
779
+    public function testGetByEmail(): void {
780
+        $config = $this->getMockBuilder(AllConfig::class)
781
+            ->disableOriginalConstructor()
782
+            ->getMock();
783
+        $config
784
+            ->expects($this->once())
785
+            ->method('getUsersForUserValueCaseInsensitive')
786
+            ->with('settings', 'email', '[email protected]')
787
+            ->willReturn(['uid1', 'uid99', 'uid2']);
788
+
789
+        $backend = $this->createMock(\Test\Util\User\Dummy::class);
790
+        $backend->expects($this->exactly(3))
791
+            ->method('userExists')
792
+            ->willReturnMap([
793
+                ['uid1', true],
794
+                ['uid99', false],
795
+                ['uid2', true]
796
+            ]);
797
+
798
+        $manager = new \OC\User\Manager($config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
799
+        $manager->registerBackend($backend);
800
+
801
+        $users = $manager->getByEmail('[email protected]');
802
+        $this->assertCount(2, $users);
803
+        $this->assertEquals('uid1', $users[0]->getUID());
804
+        $this->assertEquals('uid2', $users[1]->getUID());
805
+    }
806 806
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -90,7 +90,7 @@  discard block
 block discarded – undo
90 90
 		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
91 91
 		$manager->registerBackend($backend);
92 92
 
93
-		$this->assertFalse($manager->userExists('foo' . str_repeat('a', 62)));
93
+		$this->assertFalse($manager->userExists('foo'.str_repeat('a', 62)));
94 94
 	}
95 95
 
96 96
 	public function testUserExistsSingleBackendNotExists(): void {
@@ -177,7 +177,7 @@  discard block
 block discarded – undo
177 177
 
178 178
 		$backend->expects($this->any())
179 179
 			->method('implementsActions')
180
-			->willReturnCallback(function ($actions) {
180
+			->willReturnCallback(function($actions) {
181 181
 				if ($actions === \OC\USER\BACKEND::CHECK_PASSWORD) {
182 182
 					return true;
183 183
 				} else {
@@ -255,7 +255,7 @@  discard block
 block discarded – undo
255 255
 		$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
256 256
 		$manager->registerBackend($backend);
257 257
 
258
-		$this->assertEquals(null, $manager->get('foo' . str_repeat('a', 62)));
258
+		$this->assertEquals(null, $manager->get('foo'.str_repeat('a', 62)));
259 259
 	}
260 260
 
261 261
 	public function testGetOneBackendDoNotTranslateLoginNames(): void {
@@ -361,7 +361,7 @@  discard block
 block discarded – undo
361 361
 			['..', 'foo', 'Username must not consist of dots only'],
362 362
 			['.test', '', 'A valid password must be provided'],
363 363
 			['test', '', 'A valid password must be provided'],
364
-			['test' . str_repeat('a', 61), '', 'Login is too long'],
364
+			['test'.str_repeat('a', 61), '', 'Login is too long'],
365 365
 		];
366 366
 	}
367 367
 
@@ -663,7 +663,7 @@  discard block
 block discarded – undo
663 663
 		$manager = \OCP\Server::get(IUserManager::class);
664 664
 		// count other users in the db before adding our own
665 665
 		$count = 0;
666
-		$function = function (IUser $user) use (&$count) {
666
+		$function = function(IUser $user) use (&$count) {
667 667
 			$count++;
668 668
 		};
669 669
 		$manager->callForAllUsers($function, '', true);
@@ -702,7 +702,7 @@  discard block
 block discarded – undo
702 702
 		$manager = \OCP\Server::get(IUserManager::class);
703 703
 
704 704
 		// Create some users
705
-		$now = (string)time();
705
+		$now = (string) time();
706 706
 		$user1 = $manager->createUser('test_active_1', 'test_active_1');
707 707
 		$config->setUserValue('test_active_1', 'login', 'lastLogin', $now);
708 708
 		$user1->setDisplayName('test active 1');
Please login to merge, or discard this patch.
lib/private/User/Manager.php 1 patch
Indentation   +784 added lines, -784 removed lines patch added patch discarded remove patch
@@ -53,788 +53,788 @@
 block discarded – undo
53 53
  * @package OC\User
54 54
  */
55 55
 class Manager extends PublicEmitter implements IUserManager {
56
-	/**
57
-	 * @var UserInterface[] $backends
58
-	 */
59
-	private array $backends = [];
60
-
61
-	/**
62
-	 * @var array<string,\OC\User\User> $cachedUsers
63
-	 */
64
-	private array $cachedUsers = [];
65
-
66
-	private ICache $cache;
67
-
68
-	private DisplayNameCache $displayNameCache;
69
-
70
-	public function __construct(
71
-		private IConfig $config,
72
-		ICacheFactory $cacheFactory,
73
-		private IEventDispatcher $eventDispatcher,
74
-		private LoggerInterface $logger,
75
-	) {
76
-		$this->cache = new WithLocalCache($cacheFactory->createDistributed('user_backend_map'));
77
-		$this->listen('\OC\User', 'postDelete', function (IUser $user): void {
78
-			unset($this->cachedUsers[$user->getUID()]);
79
-		});
80
-		$this->displayNameCache = new DisplayNameCache($cacheFactory, $this);
81
-	}
82
-
83
-	/**
84
-	 * Get the active backends
85
-	 * @return UserInterface[]
86
-	 */
87
-	public function getBackends(): array {
88
-		return $this->backends;
89
-	}
90
-
91
-	public function registerBackend(UserInterface $backend): void {
92
-		$this->backends[] = $backend;
93
-	}
94
-
95
-	public function removeBackend(UserInterface $backend): void {
96
-		$this->cachedUsers = [];
97
-		if (($i = array_search($backend, $this->backends)) !== false) {
98
-			unset($this->backends[$i]);
99
-		}
100
-	}
101
-
102
-	public function clearBackends(): void {
103
-		$this->cachedUsers = [];
104
-		$this->backends = [];
105
-	}
106
-
107
-	/**
108
-	 * get a user by user id
109
-	 *
110
-	 * @param string $uid
111
-	 * @return \OC\User\User|null Either the user or null if the specified user does not exist
112
-	 */
113
-	public function get($uid) {
114
-		if (is_null($uid) || $uid === '' || $uid === false) {
115
-			return null;
116
-		}
117
-		if (isset($this->cachedUsers[$uid])) { //check the cache first to prevent having to loop over the backends
118
-			return $this->cachedUsers[$uid];
119
-		}
120
-
121
-		if (strlen($uid) > IUser::MAX_USERID_LENGTH) {
122
-			return null;
123
-		}
124
-
125
-		$cachedBackend = $this->cache->get(sha1($uid));
126
-		if ($cachedBackend !== null && isset($this->backends[$cachedBackend])) {
127
-			// Cache has the info of the user backend already, so ask that one directly
128
-			$backend = $this->backends[$cachedBackend];
129
-			if ($backend->userExists($uid)) {
130
-				return $this->getUserObject($uid, $backend);
131
-			}
132
-		}
133
-
134
-		foreach ($this->backends as $i => $backend) {
135
-			if ($i === $cachedBackend) {
136
-				// Tried that one already
137
-				continue;
138
-			}
139
-
140
-			if ($backend->userExists($uid)) {
141
-				// Hash $uid to ensure that only valid characters are used for the cache key
142
-				$this->cache->set(sha1($uid), $i, 300);
143
-				return $this->getUserObject($uid, $backend);
144
-			}
145
-		}
146
-		return null;
147
-	}
148
-
149
-	public function getDisplayName(string $uid): ?string {
150
-		return $this->displayNameCache->getDisplayName($uid);
151
-	}
152
-
153
-	/**
154
-	 * get or construct the user object
155
-	 *
156
-	 * @param string $uid
157
-	 * @param \OCP\UserInterface $backend
158
-	 * @param bool $cacheUser If false the newly created user object will not be cached
159
-	 * @return \OC\User\User
160
-	 */
161
-	public function getUserObject($uid, $backend, $cacheUser = true) {
162
-		if ($backend instanceof IGetRealUIDBackend) {
163
-			$uid = $backend->getRealUID($uid);
164
-		}
165
-
166
-		if (isset($this->cachedUsers[$uid])) {
167
-			return $this->cachedUsers[$uid];
168
-		}
169
-
170
-		$user = new User($uid, $backend, $this->eventDispatcher, $this, $this->config);
171
-		if ($cacheUser) {
172
-			$this->cachedUsers[$uid] = $user;
173
-		}
174
-		return $user;
175
-	}
176
-
177
-	/**
178
-	 * check if a user exists
179
-	 *
180
-	 * @param string $uid
181
-	 * @return bool
182
-	 */
183
-	public function userExists($uid) {
184
-		if (strlen($uid) > IUser::MAX_USERID_LENGTH) {
185
-			return false;
186
-		}
187
-
188
-		$user = $this->get($uid);
189
-		return ($user !== null);
190
-	}
191
-
192
-	/**
193
-	 * Check if the password is valid for the user
194
-	 *
195
-	 * @param string $loginName
196
-	 * @param string $password
197
-	 * @return IUser|false the User object on success, false otherwise
198
-	 */
199
-	public function checkPassword($loginName, $password) {
200
-		$result = $this->checkPasswordNoLogging($loginName, $password);
201
-
202
-		if ($result === false) {
203
-			$this->logger->warning('Login failed: \'' . $loginName . '\' (Remote IP: \'' . \OC::$server->getRequest()->getRemoteAddress() . '\')', ['app' => 'core']);
204
-		}
205
-
206
-		return $result;
207
-	}
208
-
209
-	/**
210
-	 * Check if the password is valid for the user
211
-	 *
212
-	 * @internal
213
-	 * @param string $loginName
214
-	 * @param string $password
215
-	 * @return IUser|false the User object on success, false otherwise
216
-	 */
217
-	public function checkPasswordNoLogging($loginName, $password) {
218
-		$loginName = str_replace("\0", '', $loginName);
219
-		$password = str_replace("\0", '', $password);
220
-
221
-		$cachedBackend = $this->cache->get($loginName);
222
-		if ($cachedBackend !== null && isset($this->backends[$cachedBackend])) {
223
-			$backends = [$this->backends[$cachedBackend]];
224
-		} else {
225
-			$backends = $this->backends;
226
-		}
227
-		foreach ($backends as $backend) {
228
-			if ($backend instanceof ICheckPasswordBackend || $backend->implementsActions(Backend::CHECK_PASSWORD)) {
229
-				/** @var ICheckPasswordBackend $backend */
230
-				$uid = $backend->checkPassword($loginName, $password);
231
-				if ($uid !== false) {
232
-					return $this->getUserObject($uid, $backend);
233
-				}
234
-			}
235
-		}
236
-
237
-		// since http basic auth doesn't provide a standard way of handling non ascii password we allow password to be urlencoded
238
-		// we only do this decoding after using the plain password fails to maintain compatibility with any password that happens
239
-		// to contain urlencoded patterns by "accident".
240
-		$password = urldecode($password);
241
-
242
-		foreach ($backends as $backend) {
243
-			if ($backend instanceof ICheckPasswordBackend || $backend->implementsActions(Backend::CHECK_PASSWORD)) {
244
-				/** @var ICheckPasswordBackend|UserInterface $backend */
245
-				$uid = $backend->checkPassword($loginName, $password);
246
-				if ($uid !== false) {
247
-					return $this->getUserObject($uid, $backend);
248
-				}
249
-			}
250
-		}
251
-
252
-		return false;
253
-	}
254
-
255
-	/**
256
-	 * Search by user id
257
-	 *
258
-	 * @param string $pattern
259
-	 * @param int $limit
260
-	 * @param int $offset
261
-	 * @return IUser[]
262
-	 * @deprecated 27.0.0, use searchDisplayName instead
263
-	 */
264
-	public function search($pattern, $limit = null, $offset = null) {
265
-		$users = [];
266
-		foreach ($this->backends as $backend) {
267
-			$backendUsers = $backend->getUsers($pattern, $limit, $offset);
268
-			if (is_array($backendUsers)) {
269
-				foreach ($backendUsers as $uid) {
270
-					$users[$uid] = new LazyUser($uid, $this, null, $backend);
271
-				}
272
-			}
273
-		}
274
-
275
-		uasort($users, function (IUser $a, IUser $b) {
276
-			return strcasecmp($a->getUID(), $b->getUID());
277
-		});
278
-		return $users;
279
-	}
280
-
281
-	/**
282
-	 * Search by displayName
283
-	 *
284
-	 * @param string $pattern
285
-	 * @param int $limit
286
-	 * @param int $offset
287
-	 * @return IUser[]
288
-	 */
289
-	public function searchDisplayName($pattern, $limit = null, $offset = null) {
290
-		$users = [];
291
-		foreach ($this->backends as $backend) {
292
-			$backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
293
-			if (is_array($backendUsers)) {
294
-				foreach ($backendUsers as $uid => $displayName) {
295
-					$users[] = new LazyUser($uid, $this, $displayName, $backend);
296
-				}
297
-			}
298
-		}
299
-
300
-		usort($users, function (IUser $a, IUser $b) {
301
-			return strcasecmp($a->getDisplayName(), $b->getDisplayName());
302
-		});
303
-		return $users;
304
-	}
305
-
306
-	/**
307
-	 * @return IUser[]
308
-	 */
309
-	public function getDisabledUsers(?int $limit = null, int $offset = 0, string $search = ''): array {
310
-		$users = $this->config->getUsersForUserValue('core', 'enabled', 'false');
311
-		$users = array_combine(
312
-			$users,
313
-			array_map(
314
-				fn (string $uid): IUser => new LazyUser($uid, $this),
315
-				$users
316
-			)
317
-		);
318
-		if ($search !== '') {
319
-			$users = array_filter(
320
-				$users,
321
-				function (IUser $user) use ($search): bool {
322
-					try {
323
-						return mb_stripos($user->getUID(), $search) !== false ||
324
-						mb_stripos($user->getDisplayName(), $search) !== false ||
325
-						mb_stripos($user->getEMailAddress() ?? '', $search) !== false;
326
-					} catch (NoUserException $ex) {
327
-						$this->logger->error('Error while filtering disabled users', ['exception' => $ex, 'userUID' => $user->getUID()]);
328
-						return false;
329
-					}
330
-				});
331
-		}
332
-
333
-		$tempLimit = ($limit === null ? null : $limit + $offset);
334
-		foreach ($this->backends as $backend) {
335
-			if (($tempLimit !== null) && (count($users) >= $tempLimit)) {
336
-				break;
337
-			}
338
-			if ($backend instanceof IProvideEnabledStateBackend) {
339
-				$backendUsers = $backend->getDisabledUserList(($tempLimit === null ? null : $tempLimit - count($users)), 0, $search);
340
-				foreach ($backendUsers as $uid) {
341
-					$users[$uid] = new LazyUser($uid, $this, null, $backend);
342
-				}
343
-			}
344
-		}
345
-
346
-		return array_slice($users, $offset, $limit);
347
-	}
348
-
349
-	/**
350
-	 * Search known users (from phonebook sync) by displayName
351
-	 *
352
-	 * @param string $searcher
353
-	 * @param string $pattern
354
-	 * @param int|null $limit
355
-	 * @param int|null $offset
356
-	 * @return IUser[]
357
-	 */
358
-	public function searchKnownUsersByDisplayName(string $searcher, string $pattern, ?int $limit = null, ?int $offset = null): array {
359
-		$users = [];
360
-		foreach ($this->backends as $backend) {
361
-			if ($backend instanceof ISearchKnownUsersBackend) {
362
-				$backendUsers = $backend->searchKnownUsersByDisplayName($searcher, $pattern, $limit, $offset);
363
-			} else {
364
-				// Better than nothing, but filtering after pagination can remove lots of results.
365
-				$backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
366
-			}
367
-			if (is_array($backendUsers)) {
368
-				foreach ($backendUsers as $uid => $displayName) {
369
-					$users[] = $this->getUserObject($uid, $backend);
370
-				}
371
-			}
372
-		}
373
-
374
-		usort($users, function ($a, $b) {
375
-			/**
376
-			 * @var IUser $a
377
-			 * @var IUser $b
378
-			 */
379
-			return strcasecmp($a->getDisplayName(), $b->getDisplayName());
380
-		});
381
-		return $users;
382
-	}
383
-
384
-	/**
385
-	 * @param string $uid
386
-	 * @param string $password
387
-	 * @return false|IUser the created user or false
388
-	 * @throws \InvalidArgumentException
389
-	 * @throws HintException
390
-	 */
391
-	public function createUser($uid, $password) {
392
-		// DI injection is not used here as IRegistry needs the user manager itself for user count and thus it would create a cyclic dependency
393
-		/** @var IAssertion $assertion */
394
-		$assertion = \OC::$server->get(IAssertion::class);
395
-		$assertion->createUserIsLegit();
396
-
397
-		$localBackends = [];
398
-		foreach ($this->backends as $backend) {
399
-			if ($backend instanceof Database) {
400
-				// First check if there is another user backend
401
-				$localBackends[] = $backend;
402
-				continue;
403
-			}
404
-
405
-			if ($backend->implementsActions(Backend::CREATE_USER)) {
406
-				return $this->createUserFromBackend($uid, $password, $backend);
407
-			}
408
-		}
409
-
410
-		foreach ($localBackends as $backend) {
411
-			if ($backend->implementsActions(Backend::CREATE_USER)) {
412
-				return $this->createUserFromBackend($uid, $password, $backend);
413
-			}
414
-		}
415
-
416
-		return false;
417
-	}
418
-
419
-	/**
420
-	 * @param string $uid
421
-	 * @param string $password
422
-	 * @param UserInterface $backend
423
-	 * @return IUser|false
424
-	 * @throws \InvalidArgumentException
425
-	 */
426
-	public function createUserFromBackend($uid, $password, UserInterface $backend) {
427
-		$l = \OCP\Util::getL10N('lib');
428
-
429
-		$this->validateUserId($uid, true);
430
-
431
-		// No empty password
432
-		if (trim($password) === '') {
433
-			throw new \InvalidArgumentException($l->t('A valid password must be provided'));
434
-		}
435
-
436
-		// Check if user already exists
437
-		if ($this->userExists($uid)) {
438
-			throw new \InvalidArgumentException($l->t('The Login is already being used'));
439
-		}
440
-
441
-		/** @deprecated 21.0.0 use BeforeUserCreatedEvent event with the IEventDispatcher instead */
442
-		$this->emit('\OC\User', 'preCreateUser', [$uid, $password]);
443
-		$this->eventDispatcher->dispatchTyped(new BeforeUserCreatedEvent($uid, $password));
444
-		$state = $backend->createUser($uid, $password);
445
-		if ($state === false) {
446
-			throw new \InvalidArgumentException($l->t('Could not create account'));
447
-		}
448
-		$user = $this->getUserObject($uid, $backend);
449
-		if ($user instanceof IUser) {
450
-			/** @deprecated 21.0.0 use UserCreatedEvent event with the IEventDispatcher instead */
451
-			$this->emit('\OC\User', 'postCreateUser', [$user, $password]);
452
-			$this->eventDispatcher->dispatchTyped(new UserCreatedEvent($user, $password));
453
-			return $user;
454
-		}
455
-		return false;
456
-	}
457
-
458
-	/**
459
-	 * returns how many users per backend exist (if supported by backend)
460
-	 *
461
-	 * @param boolean $hasLoggedIn when true only users that have a lastLogin
462
-	 *                             entry in the preferences table will be affected
463
-	 * @return array<string, int> an array of backend class as key and count number as value
464
-	 */
465
-	public function countUsers() {
466
-		$userCountStatistics = [];
467
-		foreach ($this->backends as $backend) {
468
-			if ($backend instanceof ICountUsersBackend || $backend->implementsActions(Backend::COUNT_USERS)) {
469
-				/** @var ICountUsersBackend|IUserBackend $backend */
470
-				$backendUsers = $backend->countUsers();
471
-				if ($backendUsers !== false) {
472
-					if ($backend instanceof IUserBackend) {
473
-						$name = $backend->getBackendName();
474
-					} else {
475
-						$name = get_class($backend);
476
-					}
477
-					if (isset($userCountStatistics[$name])) {
478
-						$userCountStatistics[$name] += $backendUsers;
479
-					} else {
480
-						$userCountStatistics[$name] = $backendUsers;
481
-					}
482
-				}
483
-			}
484
-		}
485
-		return $userCountStatistics;
486
-	}
487
-
488
-	public function countUsersTotal(int $limit = 0, bool $onlyMappedUsers = false): int|false {
489
-		$userCount = false;
490
-
491
-		foreach ($this->backends as $backend) {
492
-			if ($onlyMappedUsers && $backend instanceof ICountMappedUsersBackend) {
493
-				$backendUsers = $backend->countMappedUsers();
494
-			} elseif ($backend instanceof ILimitAwareCountUsersBackend) {
495
-				$backendUsers = $backend->countUsers($limit);
496
-			} elseif ($backend instanceof ICountUsersBackend || $backend->implementsActions(Backend::COUNT_USERS)) {
497
-				/** @var ICountUsersBackend $backend */
498
-				$backendUsers = $backend->countUsers();
499
-			} else {
500
-				$this->logger->debug('Skip backend for user count: ' . get_class($backend));
501
-				continue;
502
-			}
503
-			if ($backendUsers !== false) {
504
-				$userCount = (int)$userCount + $backendUsers;
505
-				if ($limit > 0) {
506
-					if ($userCount >= $limit) {
507
-						break;
508
-					}
509
-					$limit -= $userCount;
510
-				}
511
-			} else {
512
-				$this->logger->warning('Can not determine user count for ' . get_class($backend));
513
-			}
514
-		}
515
-		return $userCount;
516
-	}
517
-
518
-	/**
519
-	 * returns how many users per backend exist in the requested groups (if supported by backend)
520
-	 *
521
-	 * @param IGroup[] $groups an array of groups to search in
522
-	 * @param int $limit limit to stop counting
523
-	 * @return array{int,int} total number of users, and number of disabled users in the given groups, below $limit. If limit is reached, -1 is returned for number of disabled users
524
-	 */
525
-	public function countUsersAndDisabledUsersOfGroups(array $groups, int $limit): array {
526
-		$users = [];
527
-		$disabled = [];
528
-		foreach ($groups as $group) {
529
-			foreach ($group->getUsers() as $user) {
530
-				$users[$user->getUID()] = 1;
531
-				if (!$user->isEnabled()) {
532
-					$disabled[$user->getUID()] = 1;
533
-				}
534
-				if (count($users) >= $limit) {
535
-					return [count($users),-1];
536
-				}
537
-			}
538
-		}
539
-		return [count($users),count($disabled)];
540
-	}
541
-
542
-	/**
543
-	 * The callback is executed for each user on each backend.
544
-	 * If the callback returns false no further users will be retrieved.
545
-	 *
546
-	 * @psalm-param \Closure(\OCP\IUser):?bool $callback
547
-	 * @param string $search
548
-	 * @param boolean $onlySeen when true only users that have a lastLogin entry
549
-	 *                          in the preferences table will be affected
550
-	 * @since 9.0.0
551
-	 */
552
-	public function callForAllUsers(\Closure $callback, $search = '', $onlySeen = false) {
553
-		if ($onlySeen) {
554
-			$this->callForSeenUsers($callback);
555
-		} else {
556
-			foreach ($this->getBackends() as $backend) {
557
-				$limit = 500;
558
-				$offset = 0;
559
-				do {
560
-					$users = $backend->getUsers($search, $limit, $offset);
561
-					foreach ($users as $uid) {
562
-						if (!$backend->userExists($uid)) {
563
-							continue;
564
-						}
565
-						$user = $this->getUserObject($uid, $backend, false);
566
-						$return = $callback($user);
567
-						if ($return === false) {
568
-							break;
569
-						}
570
-					}
571
-					$offset += $limit;
572
-				} while (count($users) >= $limit);
573
-			}
574
-		}
575
-	}
576
-
577
-	/**
578
-	 * returns how many users are disabled
579
-	 *
580
-	 * @return int
581
-	 * @since 12.0.0
582
-	 */
583
-	public function countDisabledUsers(): int {
584
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
585
-		$queryBuilder->select($queryBuilder->func()->count('*'))
586
-			->from('preferences')
587
-			->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core')))
588
-			->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
589
-			->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR));
590
-
591
-
592
-		$result = $queryBuilder->execute();
593
-		$count = $result->fetchOne();
594
-		$result->closeCursor();
595
-
596
-		if ($count !== false) {
597
-			$count = (int)$count;
598
-		} else {
599
-			$count = 0;
600
-		}
601
-
602
-		return $count;
603
-	}
604
-
605
-	/**
606
-	 * returns how many users have logged in once
607
-	 *
608
-	 * @return int
609
-	 * @since 11.0.0
610
-	 */
611
-	public function countSeenUsers() {
612
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
613
-		$queryBuilder->select($queryBuilder->func()->count('*'))
614
-			->from('preferences')
615
-			->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('login')))
616
-			->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('lastLogin')));
617
-
618
-		$query = $queryBuilder->execute();
619
-
620
-		$result = (int)$query->fetchOne();
621
-		$query->closeCursor();
622
-
623
-		return $result;
624
-	}
625
-
626
-	public function callForSeenUsers(\Closure $callback) {
627
-		$users = $this->getSeenUsers();
628
-		foreach ($users as $user) {
629
-			$return = $callback($user);
630
-			if ($return === false) {
631
-				return;
632
-			}
633
-		}
634
-	}
635
-
636
-	/**
637
-	 * Getting all userIds that have a listLogin value requires checking the
638
-	 * value in php because on oracle you cannot use a clob in a where clause,
639
-	 * preventing us from doing a not null or length(value) > 0 check.
640
-	 *
641
-	 * @param int $limit
642
-	 * @param int $offset
643
-	 * @return string[] with user ids
644
-	 */
645
-	private function getSeenUserIds($limit = null, $offset = null) {
646
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
647
-		$queryBuilder->select(['userid'])
648
-			->from('preferences')
649
-			->where($queryBuilder->expr()->eq(
650
-				'appid', $queryBuilder->createNamedParameter('login'))
651
-			)
652
-			->andWhere($queryBuilder->expr()->eq(
653
-				'configkey', $queryBuilder->createNamedParameter('lastLogin'))
654
-			)
655
-			->andWhere($queryBuilder->expr()->isNotNull('configvalue')
656
-			);
657
-
658
-		if ($limit !== null) {
659
-			$queryBuilder->setMaxResults($limit);
660
-		}
661
-		if ($offset !== null) {
662
-			$queryBuilder->setFirstResult($offset);
663
-		}
664
-		$query = $queryBuilder->execute();
665
-		$result = [];
666
-
667
-		while ($row = $query->fetch()) {
668
-			$result[] = $row['userid'];
669
-		}
670
-
671
-		$query->closeCursor();
672
-
673
-		return $result;
674
-	}
675
-
676
-	/**
677
-	 * @param string $email
678
-	 * @return IUser[]
679
-	 * @since 9.1.0
680
-	 */
681
-	public function getByEmail($email) {
682
-		// looking for 'email' only (and not primary_mail) is intentional
683
-		$userIds = $this->config->getUsersForUserValueCaseInsensitive('settings', 'email', $email);
684
-
685
-		$users = array_map(function ($uid) {
686
-			return $this->get($uid);
687
-		}, $userIds);
688
-
689
-		return array_values(array_filter($users, function ($u) {
690
-			return ($u instanceof IUser);
691
-		}));
692
-	}
693
-
694
-	/**
695
-	 * @param string $uid
696
-	 * @param bool $checkDataDirectory
697
-	 * @throws \InvalidArgumentException Message is an already translated string with a reason why the id is not valid
698
-	 * @since 26.0.0
699
-	 */
700
-	public function validateUserId(string $uid, bool $checkDataDirectory = false): void {
701
-		$l = Server::get(IFactory::class)->get('lib');
702
-
703
-		// Check the ID for bad characters
704
-		// Allowed are: "a-z", "A-Z", "0-9", spaces and "_.@-'"
705
-		if (preg_match('/[^a-zA-Z0-9 _.@\-\']/', $uid)) {
706
-			throw new \InvalidArgumentException($l->t('Only the following characters are allowed in an Login:'
707
-				. ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'));
708
-		}
709
-
710
-		// No empty user ID
711
-		if (trim($uid) === '') {
712
-			throw new \InvalidArgumentException($l->t('A valid Login must be provided'));
713
-		}
714
-
715
-		// No whitespace at the beginning or at the end
716
-		if (trim($uid) !== $uid) {
717
-			throw new \InvalidArgumentException($l->t('Login contains whitespace at the beginning or at the end'));
718
-		}
719
-
720
-		// User ID only consists of 1 or 2 dots (directory traversal)
721
-		if ($uid === '.' || $uid === '..') {
722
-			throw new \InvalidArgumentException($l->t('Login must not consist of dots only'));
723
-		}
724
-
725
-		// User ID is too long
726
-		if (strlen($uid) > IUser::MAX_USERID_LENGTH) {
727
-			throw new \InvalidArgumentException($l->t('Login is too long'));
728
-		}
729
-
730
-		if (!$this->verifyUid($uid, $checkDataDirectory)) {
731
-			throw new \InvalidArgumentException($l->t('Login is invalid because files already exist for this user'));
732
-		}
733
-	}
734
-
735
-	/**
736
-	 * Gets the list of user ids sorted by lastLogin, from most recent to least recent
737
-	 *
738
-	 * @param int|null $limit how many users to fetch (default: 25, max: 100)
739
-	 * @param int $offset from which offset to fetch
740
-	 * @param string $search search users based on search params
741
-	 * @return list<string> list of user IDs
742
-	 */
743
-	public function getLastLoggedInUsers(?int $limit = null, int $offset = 0, string $search = ''): array {
744
-		// We can't load all users who already logged in
745
-		$limit = min(100, $limit ?: 25);
746
-
747
-		$connection = \OC::$server->getDatabaseConnection();
748
-		$queryBuilder = $connection->getQueryBuilder();
749
-		$queryBuilder->select('pref_login.userid')
750
-			->from('preferences', 'pref_login')
751
-			->where($queryBuilder->expr()->eq('pref_login.appid', $queryBuilder->expr()->literal('login')))
752
-			->andWhere($queryBuilder->expr()->eq('pref_login.configkey', $queryBuilder->expr()->literal('lastLogin')))
753
-			->setFirstResult($offset)
754
-			->setMaxResults($limit)
755
-		;
756
-
757
-		// Oracle don't want to run ORDER BY on CLOB column
758
-		$loginOrder = $connection->getDatabasePlatform() instanceof OraclePlatform
759
-			? $queryBuilder->expr()->castColumn('pref_login.configvalue', IQueryBuilder::PARAM_INT)
760
-			: 'pref_login.configvalue';
761
-		$queryBuilder
762
-			->orderBy($loginOrder, 'DESC')
763
-			->addOrderBy($queryBuilder->func()->lower('pref_login.userid'), 'ASC');
764
-
765
-		if ($search !== '') {
766
-			$displayNameMatches = $this->searchDisplayName($search);
767
-			$matchedUids = array_map(static fn (IUser $u): string => $u->getUID(), $displayNameMatches);
768
-
769
-			$queryBuilder
770
-				->leftJoin('pref_login', 'preferences', 'pref_email', $queryBuilder->expr()->andX(
771
-					$queryBuilder->expr()->eq('pref_login.userid', 'pref_email.userid'),
772
-					$queryBuilder->expr()->eq('pref_email.appid', $queryBuilder->expr()->literal('settings')),
773
-					$queryBuilder->expr()->eq('pref_email.configkey', $queryBuilder->expr()->literal('email')),
774
-				))
775
-				->andWhere($queryBuilder->expr()->orX(
776
-					$queryBuilder->expr()->in('pref_login.userid', $queryBuilder->createNamedParameter($matchedUids, IQueryBuilder::PARAM_STR_ARRAY)),
777
-				));
778
-		}
779
-
780
-		/** @var list<string> */
781
-		$list = $queryBuilder->executeQuery()->fetchAll(\PDO::FETCH_COLUMN);
782
-
783
-		return $list;
784
-	}
785
-
786
-	private function verifyUid(string $uid, bool $checkDataDirectory = false): bool {
787
-		$appdata = 'appdata_' . $this->config->getSystemValueString('instanceid');
788
-
789
-		if (\in_array($uid, [
790
-			'.htaccess',
791
-			'files_external',
792
-			'__groupfolders',
793
-			'.ncdata',
794
-			'owncloud.log',
795
-			'nextcloud.log',
796
-			'updater.log',
797
-			'audit.log',
798
-			$appdata], true)) {
799
-			return false;
800
-		}
801
-
802
-		if (!$checkDataDirectory) {
803
-			return true;
804
-		}
805
-
806
-		$dataDirectory = $this->config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data');
807
-
808
-		return !file_exists(rtrim($dataDirectory, '/') . '/' . $uid);
809
-	}
810
-
811
-	public function getDisplayNameCache(): DisplayNameCache {
812
-		return $this->displayNameCache;
813
-	}
814
-
815
-	/**
816
-	 * Gets the list of users sorted by lastLogin, from most recent to least recent
817
-	 *
818
-	 * @param int $offset from which offset to fetch
819
-	 * @return \Iterator<IUser> list of user IDs
820
-	 * @since 30.0.0
821
-	 */
822
-	public function getSeenUsers(int $offset = 0): \Iterator {
823
-		$limit = 1000;
824
-
825
-		do {
826
-			$userIds = $this->getSeenUserIds($limit, $offset);
827
-			$offset += $limit;
828
-
829
-			foreach ($userIds as $userId) {
830
-				foreach ($this->backends as $backend) {
831
-					if ($backend->userExists($userId)) {
832
-						$user = $this->getUserObject($userId, $backend, false);
833
-						yield $user;
834
-						break;
835
-					}
836
-				}
837
-			}
838
-		} while (count($userIds) === $limit);
839
-	}
56
+    /**
57
+     * @var UserInterface[] $backends
58
+     */
59
+    private array $backends = [];
60
+
61
+    /**
62
+     * @var array<string,\OC\User\User> $cachedUsers
63
+     */
64
+    private array $cachedUsers = [];
65
+
66
+    private ICache $cache;
67
+
68
+    private DisplayNameCache $displayNameCache;
69
+
70
+    public function __construct(
71
+        private IConfig $config,
72
+        ICacheFactory $cacheFactory,
73
+        private IEventDispatcher $eventDispatcher,
74
+        private LoggerInterface $logger,
75
+    ) {
76
+        $this->cache = new WithLocalCache($cacheFactory->createDistributed('user_backend_map'));
77
+        $this->listen('\OC\User', 'postDelete', function (IUser $user): void {
78
+            unset($this->cachedUsers[$user->getUID()]);
79
+        });
80
+        $this->displayNameCache = new DisplayNameCache($cacheFactory, $this);
81
+    }
82
+
83
+    /**
84
+     * Get the active backends
85
+     * @return UserInterface[]
86
+     */
87
+    public function getBackends(): array {
88
+        return $this->backends;
89
+    }
90
+
91
+    public function registerBackend(UserInterface $backend): void {
92
+        $this->backends[] = $backend;
93
+    }
94
+
95
+    public function removeBackend(UserInterface $backend): void {
96
+        $this->cachedUsers = [];
97
+        if (($i = array_search($backend, $this->backends)) !== false) {
98
+            unset($this->backends[$i]);
99
+        }
100
+    }
101
+
102
+    public function clearBackends(): void {
103
+        $this->cachedUsers = [];
104
+        $this->backends = [];
105
+    }
106
+
107
+    /**
108
+     * get a user by user id
109
+     *
110
+     * @param string $uid
111
+     * @return \OC\User\User|null Either the user or null if the specified user does not exist
112
+     */
113
+    public function get($uid) {
114
+        if (is_null($uid) || $uid === '' || $uid === false) {
115
+            return null;
116
+        }
117
+        if (isset($this->cachedUsers[$uid])) { //check the cache first to prevent having to loop over the backends
118
+            return $this->cachedUsers[$uid];
119
+        }
120
+
121
+        if (strlen($uid) > IUser::MAX_USERID_LENGTH) {
122
+            return null;
123
+        }
124
+
125
+        $cachedBackend = $this->cache->get(sha1($uid));
126
+        if ($cachedBackend !== null && isset($this->backends[$cachedBackend])) {
127
+            // Cache has the info of the user backend already, so ask that one directly
128
+            $backend = $this->backends[$cachedBackend];
129
+            if ($backend->userExists($uid)) {
130
+                return $this->getUserObject($uid, $backend);
131
+            }
132
+        }
133
+
134
+        foreach ($this->backends as $i => $backend) {
135
+            if ($i === $cachedBackend) {
136
+                // Tried that one already
137
+                continue;
138
+            }
139
+
140
+            if ($backend->userExists($uid)) {
141
+                // Hash $uid to ensure that only valid characters are used for the cache key
142
+                $this->cache->set(sha1($uid), $i, 300);
143
+                return $this->getUserObject($uid, $backend);
144
+            }
145
+        }
146
+        return null;
147
+    }
148
+
149
+    public function getDisplayName(string $uid): ?string {
150
+        return $this->displayNameCache->getDisplayName($uid);
151
+    }
152
+
153
+    /**
154
+     * get or construct the user object
155
+     *
156
+     * @param string $uid
157
+     * @param \OCP\UserInterface $backend
158
+     * @param bool $cacheUser If false the newly created user object will not be cached
159
+     * @return \OC\User\User
160
+     */
161
+    public function getUserObject($uid, $backend, $cacheUser = true) {
162
+        if ($backend instanceof IGetRealUIDBackend) {
163
+            $uid = $backend->getRealUID($uid);
164
+        }
165
+
166
+        if (isset($this->cachedUsers[$uid])) {
167
+            return $this->cachedUsers[$uid];
168
+        }
169
+
170
+        $user = new User($uid, $backend, $this->eventDispatcher, $this, $this->config);
171
+        if ($cacheUser) {
172
+            $this->cachedUsers[$uid] = $user;
173
+        }
174
+        return $user;
175
+    }
176
+
177
+    /**
178
+     * check if a user exists
179
+     *
180
+     * @param string $uid
181
+     * @return bool
182
+     */
183
+    public function userExists($uid) {
184
+        if (strlen($uid) > IUser::MAX_USERID_LENGTH) {
185
+            return false;
186
+        }
187
+
188
+        $user = $this->get($uid);
189
+        return ($user !== null);
190
+    }
191
+
192
+    /**
193
+     * Check if the password is valid for the user
194
+     *
195
+     * @param string $loginName
196
+     * @param string $password
197
+     * @return IUser|false the User object on success, false otherwise
198
+     */
199
+    public function checkPassword($loginName, $password) {
200
+        $result = $this->checkPasswordNoLogging($loginName, $password);
201
+
202
+        if ($result === false) {
203
+            $this->logger->warning('Login failed: \'' . $loginName . '\' (Remote IP: \'' . \OC::$server->getRequest()->getRemoteAddress() . '\')', ['app' => 'core']);
204
+        }
205
+
206
+        return $result;
207
+    }
208
+
209
+    /**
210
+     * Check if the password is valid for the user
211
+     *
212
+     * @internal
213
+     * @param string $loginName
214
+     * @param string $password
215
+     * @return IUser|false the User object on success, false otherwise
216
+     */
217
+    public function checkPasswordNoLogging($loginName, $password) {
218
+        $loginName = str_replace("\0", '', $loginName);
219
+        $password = str_replace("\0", '', $password);
220
+
221
+        $cachedBackend = $this->cache->get($loginName);
222
+        if ($cachedBackend !== null && isset($this->backends[$cachedBackend])) {
223
+            $backends = [$this->backends[$cachedBackend]];
224
+        } else {
225
+            $backends = $this->backends;
226
+        }
227
+        foreach ($backends as $backend) {
228
+            if ($backend instanceof ICheckPasswordBackend || $backend->implementsActions(Backend::CHECK_PASSWORD)) {
229
+                /** @var ICheckPasswordBackend $backend */
230
+                $uid = $backend->checkPassword($loginName, $password);
231
+                if ($uid !== false) {
232
+                    return $this->getUserObject($uid, $backend);
233
+                }
234
+            }
235
+        }
236
+
237
+        // since http basic auth doesn't provide a standard way of handling non ascii password we allow password to be urlencoded
238
+        // we only do this decoding after using the plain password fails to maintain compatibility with any password that happens
239
+        // to contain urlencoded patterns by "accident".
240
+        $password = urldecode($password);
241
+
242
+        foreach ($backends as $backend) {
243
+            if ($backend instanceof ICheckPasswordBackend || $backend->implementsActions(Backend::CHECK_PASSWORD)) {
244
+                /** @var ICheckPasswordBackend|UserInterface $backend */
245
+                $uid = $backend->checkPassword($loginName, $password);
246
+                if ($uid !== false) {
247
+                    return $this->getUserObject($uid, $backend);
248
+                }
249
+            }
250
+        }
251
+
252
+        return false;
253
+    }
254
+
255
+    /**
256
+     * Search by user id
257
+     *
258
+     * @param string $pattern
259
+     * @param int $limit
260
+     * @param int $offset
261
+     * @return IUser[]
262
+     * @deprecated 27.0.0, use searchDisplayName instead
263
+     */
264
+    public function search($pattern, $limit = null, $offset = null) {
265
+        $users = [];
266
+        foreach ($this->backends as $backend) {
267
+            $backendUsers = $backend->getUsers($pattern, $limit, $offset);
268
+            if (is_array($backendUsers)) {
269
+                foreach ($backendUsers as $uid) {
270
+                    $users[$uid] = new LazyUser($uid, $this, null, $backend);
271
+                }
272
+            }
273
+        }
274
+
275
+        uasort($users, function (IUser $a, IUser $b) {
276
+            return strcasecmp($a->getUID(), $b->getUID());
277
+        });
278
+        return $users;
279
+    }
280
+
281
+    /**
282
+     * Search by displayName
283
+     *
284
+     * @param string $pattern
285
+     * @param int $limit
286
+     * @param int $offset
287
+     * @return IUser[]
288
+     */
289
+    public function searchDisplayName($pattern, $limit = null, $offset = null) {
290
+        $users = [];
291
+        foreach ($this->backends as $backend) {
292
+            $backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
293
+            if (is_array($backendUsers)) {
294
+                foreach ($backendUsers as $uid => $displayName) {
295
+                    $users[] = new LazyUser($uid, $this, $displayName, $backend);
296
+                }
297
+            }
298
+        }
299
+
300
+        usort($users, function (IUser $a, IUser $b) {
301
+            return strcasecmp($a->getDisplayName(), $b->getDisplayName());
302
+        });
303
+        return $users;
304
+    }
305
+
306
+    /**
307
+     * @return IUser[]
308
+     */
309
+    public function getDisabledUsers(?int $limit = null, int $offset = 0, string $search = ''): array {
310
+        $users = $this->config->getUsersForUserValue('core', 'enabled', 'false');
311
+        $users = array_combine(
312
+            $users,
313
+            array_map(
314
+                fn (string $uid): IUser => new LazyUser($uid, $this),
315
+                $users
316
+            )
317
+        );
318
+        if ($search !== '') {
319
+            $users = array_filter(
320
+                $users,
321
+                function (IUser $user) use ($search): bool {
322
+                    try {
323
+                        return mb_stripos($user->getUID(), $search) !== false ||
324
+                        mb_stripos($user->getDisplayName(), $search) !== false ||
325
+                        mb_stripos($user->getEMailAddress() ?? '', $search) !== false;
326
+                    } catch (NoUserException $ex) {
327
+                        $this->logger->error('Error while filtering disabled users', ['exception' => $ex, 'userUID' => $user->getUID()]);
328
+                        return false;
329
+                    }
330
+                });
331
+        }
332
+
333
+        $tempLimit = ($limit === null ? null : $limit + $offset);
334
+        foreach ($this->backends as $backend) {
335
+            if (($tempLimit !== null) && (count($users) >= $tempLimit)) {
336
+                break;
337
+            }
338
+            if ($backend instanceof IProvideEnabledStateBackend) {
339
+                $backendUsers = $backend->getDisabledUserList(($tempLimit === null ? null : $tempLimit - count($users)), 0, $search);
340
+                foreach ($backendUsers as $uid) {
341
+                    $users[$uid] = new LazyUser($uid, $this, null, $backend);
342
+                }
343
+            }
344
+        }
345
+
346
+        return array_slice($users, $offset, $limit);
347
+    }
348
+
349
+    /**
350
+     * Search known users (from phonebook sync) by displayName
351
+     *
352
+     * @param string $searcher
353
+     * @param string $pattern
354
+     * @param int|null $limit
355
+     * @param int|null $offset
356
+     * @return IUser[]
357
+     */
358
+    public function searchKnownUsersByDisplayName(string $searcher, string $pattern, ?int $limit = null, ?int $offset = null): array {
359
+        $users = [];
360
+        foreach ($this->backends as $backend) {
361
+            if ($backend instanceof ISearchKnownUsersBackend) {
362
+                $backendUsers = $backend->searchKnownUsersByDisplayName($searcher, $pattern, $limit, $offset);
363
+            } else {
364
+                // Better than nothing, but filtering after pagination can remove lots of results.
365
+                $backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
366
+            }
367
+            if (is_array($backendUsers)) {
368
+                foreach ($backendUsers as $uid => $displayName) {
369
+                    $users[] = $this->getUserObject($uid, $backend);
370
+                }
371
+            }
372
+        }
373
+
374
+        usort($users, function ($a, $b) {
375
+            /**
376
+             * @var IUser $a
377
+             * @var IUser $b
378
+             */
379
+            return strcasecmp($a->getDisplayName(), $b->getDisplayName());
380
+        });
381
+        return $users;
382
+    }
383
+
384
+    /**
385
+     * @param string $uid
386
+     * @param string $password
387
+     * @return false|IUser the created user or false
388
+     * @throws \InvalidArgumentException
389
+     * @throws HintException
390
+     */
391
+    public function createUser($uid, $password) {
392
+        // DI injection is not used here as IRegistry needs the user manager itself for user count and thus it would create a cyclic dependency
393
+        /** @var IAssertion $assertion */
394
+        $assertion = \OC::$server->get(IAssertion::class);
395
+        $assertion->createUserIsLegit();
396
+
397
+        $localBackends = [];
398
+        foreach ($this->backends as $backend) {
399
+            if ($backend instanceof Database) {
400
+                // First check if there is another user backend
401
+                $localBackends[] = $backend;
402
+                continue;
403
+            }
404
+
405
+            if ($backend->implementsActions(Backend::CREATE_USER)) {
406
+                return $this->createUserFromBackend($uid, $password, $backend);
407
+            }
408
+        }
409
+
410
+        foreach ($localBackends as $backend) {
411
+            if ($backend->implementsActions(Backend::CREATE_USER)) {
412
+                return $this->createUserFromBackend($uid, $password, $backend);
413
+            }
414
+        }
415
+
416
+        return false;
417
+    }
418
+
419
+    /**
420
+     * @param string $uid
421
+     * @param string $password
422
+     * @param UserInterface $backend
423
+     * @return IUser|false
424
+     * @throws \InvalidArgumentException
425
+     */
426
+    public function createUserFromBackend($uid, $password, UserInterface $backend) {
427
+        $l = \OCP\Util::getL10N('lib');
428
+
429
+        $this->validateUserId($uid, true);
430
+
431
+        // No empty password
432
+        if (trim($password) === '') {
433
+            throw new \InvalidArgumentException($l->t('A valid password must be provided'));
434
+        }
435
+
436
+        // Check if user already exists
437
+        if ($this->userExists($uid)) {
438
+            throw new \InvalidArgumentException($l->t('The Login is already being used'));
439
+        }
440
+
441
+        /** @deprecated 21.0.0 use BeforeUserCreatedEvent event with the IEventDispatcher instead */
442
+        $this->emit('\OC\User', 'preCreateUser', [$uid, $password]);
443
+        $this->eventDispatcher->dispatchTyped(new BeforeUserCreatedEvent($uid, $password));
444
+        $state = $backend->createUser($uid, $password);
445
+        if ($state === false) {
446
+            throw new \InvalidArgumentException($l->t('Could not create account'));
447
+        }
448
+        $user = $this->getUserObject($uid, $backend);
449
+        if ($user instanceof IUser) {
450
+            /** @deprecated 21.0.0 use UserCreatedEvent event with the IEventDispatcher instead */
451
+            $this->emit('\OC\User', 'postCreateUser', [$user, $password]);
452
+            $this->eventDispatcher->dispatchTyped(new UserCreatedEvent($user, $password));
453
+            return $user;
454
+        }
455
+        return false;
456
+    }
457
+
458
+    /**
459
+     * returns how many users per backend exist (if supported by backend)
460
+     *
461
+     * @param boolean $hasLoggedIn when true only users that have a lastLogin
462
+     *                             entry in the preferences table will be affected
463
+     * @return array<string, int> an array of backend class as key and count number as value
464
+     */
465
+    public function countUsers() {
466
+        $userCountStatistics = [];
467
+        foreach ($this->backends as $backend) {
468
+            if ($backend instanceof ICountUsersBackend || $backend->implementsActions(Backend::COUNT_USERS)) {
469
+                /** @var ICountUsersBackend|IUserBackend $backend */
470
+                $backendUsers = $backend->countUsers();
471
+                if ($backendUsers !== false) {
472
+                    if ($backend instanceof IUserBackend) {
473
+                        $name = $backend->getBackendName();
474
+                    } else {
475
+                        $name = get_class($backend);
476
+                    }
477
+                    if (isset($userCountStatistics[$name])) {
478
+                        $userCountStatistics[$name] += $backendUsers;
479
+                    } else {
480
+                        $userCountStatistics[$name] = $backendUsers;
481
+                    }
482
+                }
483
+            }
484
+        }
485
+        return $userCountStatistics;
486
+    }
487
+
488
+    public function countUsersTotal(int $limit = 0, bool $onlyMappedUsers = false): int|false {
489
+        $userCount = false;
490
+
491
+        foreach ($this->backends as $backend) {
492
+            if ($onlyMappedUsers && $backend instanceof ICountMappedUsersBackend) {
493
+                $backendUsers = $backend->countMappedUsers();
494
+            } elseif ($backend instanceof ILimitAwareCountUsersBackend) {
495
+                $backendUsers = $backend->countUsers($limit);
496
+            } elseif ($backend instanceof ICountUsersBackend || $backend->implementsActions(Backend::COUNT_USERS)) {
497
+                /** @var ICountUsersBackend $backend */
498
+                $backendUsers = $backend->countUsers();
499
+            } else {
500
+                $this->logger->debug('Skip backend for user count: ' . get_class($backend));
501
+                continue;
502
+            }
503
+            if ($backendUsers !== false) {
504
+                $userCount = (int)$userCount + $backendUsers;
505
+                if ($limit > 0) {
506
+                    if ($userCount >= $limit) {
507
+                        break;
508
+                    }
509
+                    $limit -= $userCount;
510
+                }
511
+            } else {
512
+                $this->logger->warning('Can not determine user count for ' . get_class($backend));
513
+            }
514
+        }
515
+        return $userCount;
516
+    }
517
+
518
+    /**
519
+     * returns how many users per backend exist in the requested groups (if supported by backend)
520
+     *
521
+     * @param IGroup[] $groups an array of groups to search in
522
+     * @param int $limit limit to stop counting
523
+     * @return array{int,int} total number of users, and number of disabled users in the given groups, below $limit. If limit is reached, -1 is returned for number of disabled users
524
+     */
525
+    public function countUsersAndDisabledUsersOfGroups(array $groups, int $limit): array {
526
+        $users = [];
527
+        $disabled = [];
528
+        foreach ($groups as $group) {
529
+            foreach ($group->getUsers() as $user) {
530
+                $users[$user->getUID()] = 1;
531
+                if (!$user->isEnabled()) {
532
+                    $disabled[$user->getUID()] = 1;
533
+                }
534
+                if (count($users) >= $limit) {
535
+                    return [count($users),-1];
536
+                }
537
+            }
538
+        }
539
+        return [count($users),count($disabled)];
540
+    }
541
+
542
+    /**
543
+     * The callback is executed for each user on each backend.
544
+     * If the callback returns false no further users will be retrieved.
545
+     *
546
+     * @psalm-param \Closure(\OCP\IUser):?bool $callback
547
+     * @param string $search
548
+     * @param boolean $onlySeen when true only users that have a lastLogin entry
549
+     *                          in the preferences table will be affected
550
+     * @since 9.0.0
551
+     */
552
+    public function callForAllUsers(\Closure $callback, $search = '', $onlySeen = false) {
553
+        if ($onlySeen) {
554
+            $this->callForSeenUsers($callback);
555
+        } else {
556
+            foreach ($this->getBackends() as $backend) {
557
+                $limit = 500;
558
+                $offset = 0;
559
+                do {
560
+                    $users = $backend->getUsers($search, $limit, $offset);
561
+                    foreach ($users as $uid) {
562
+                        if (!$backend->userExists($uid)) {
563
+                            continue;
564
+                        }
565
+                        $user = $this->getUserObject($uid, $backend, false);
566
+                        $return = $callback($user);
567
+                        if ($return === false) {
568
+                            break;
569
+                        }
570
+                    }
571
+                    $offset += $limit;
572
+                } while (count($users) >= $limit);
573
+            }
574
+        }
575
+    }
576
+
577
+    /**
578
+     * returns how many users are disabled
579
+     *
580
+     * @return int
581
+     * @since 12.0.0
582
+     */
583
+    public function countDisabledUsers(): int {
584
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
585
+        $queryBuilder->select($queryBuilder->func()->count('*'))
586
+            ->from('preferences')
587
+            ->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core')))
588
+            ->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
589
+            ->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR));
590
+
591
+
592
+        $result = $queryBuilder->execute();
593
+        $count = $result->fetchOne();
594
+        $result->closeCursor();
595
+
596
+        if ($count !== false) {
597
+            $count = (int)$count;
598
+        } else {
599
+            $count = 0;
600
+        }
601
+
602
+        return $count;
603
+    }
604
+
605
+    /**
606
+     * returns how many users have logged in once
607
+     *
608
+     * @return int
609
+     * @since 11.0.0
610
+     */
611
+    public function countSeenUsers() {
612
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
613
+        $queryBuilder->select($queryBuilder->func()->count('*'))
614
+            ->from('preferences')
615
+            ->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('login')))
616
+            ->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('lastLogin')));
617
+
618
+        $query = $queryBuilder->execute();
619
+
620
+        $result = (int)$query->fetchOne();
621
+        $query->closeCursor();
622
+
623
+        return $result;
624
+    }
625
+
626
+    public function callForSeenUsers(\Closure $callback) {
627
+        $users = $this->getSeenUsers();
628
+        foreach ($users as $user) {
629
+            $return = $callback($user);
630
+            if ($return === false) {
631
+                return;
632
+            }
633
+        }
634
+    }
635
+
636
+    /**
637
+     * Getting all userIds that have a listLogin value requires checking the
638
+     * value in php because on oracle you cannot use a clob in a where clause,
639
+     * preventing us from doing a not null or length(value) > 0 check.
640
+     *
641
+     * @param int $limit
642
+     * @param int $offset
643
+     * @return string[] with user ids
644
+     */
645
+    private function getSeenUserIds($limit = null, $offset = null) {
646
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
647
+        $queryBuilder->select(['userid'])
648
+            ->from('preferences')
649
+            ->where($queryBuilder->expr()->eq(
650
+                'appid', $queryBuilder->createNamedParameter('login'))
651
+            )
652
+            ->andWhere($queryBuilder->expr()->eq(
653
+                'configkey', $queryBuilder->createNamedParameter('lastLogin'))
654
+            )
655
+            ->andWhere($queryBuilder->expr()->isNotNull('configvalue')
656
+            );
657
+
658
+        if ($limit !== null) {
659
+            $queryBuilder->setMaxResults($limit);
660
+        }
661
+        if ($offset !== null) {
662
+            $queryBuilder->setFirstResult($offset);
663
+        }
664
+        $query = $queryBuilder->execute();
665
+        $result = [];
666
+
667
+        while ($row = $query->fetch()) {
668
+            $result[] = $row['userid'];
669
+        }
670
+
671
+        $query->closeCursor();
672
+
673
+        return $result;
674
+    }
675
+
676
+    /**
677
+     * @param string $email
678
+     * @return IUser[]
679
+     * @since 9.1.0
680
+     */
681
+    public function getByEmail($email) {
682
+        // looking for 'email' only (and not primary_mail) is intentional
683
+        $userIds = $this->config->getUsersForUserValueCaseInsensitive('settings', 'email', $email);
684
+
685
+        $users = array_map(function ($uid) {
686
+            return $this->get($uid);
687
+        }, $userIds);
688
+
689
+        return array_values(array_filter($users, function ($u) {
690
+            return ($u instanceof IUser);
691
+        }));
692
+    }
693
+
694
+    /**
695
+     * @param string $uid
696
+     * @param bool $checkDataDirectory
697
+     * @throws \InvalidArgumentException Message is an already translated string with a reason why the id is not valid
698
+     * @since 26.0.0
699
+     */
700
+    public function validateUserId(string $uid, bool $checkDataDirectory = false): void {
701
+        $l = Server::get(IFactory::class)->get('lib');
702
+
703
+        // Check the ID for bad characters
704
+        // Allowed are: "a-z", "A-Z", "0-9", spaces and "_.@-'"
705
+        if (preg_match('/[^a-zA-Z0-9 _.@\-\']/', $uid)) {
706
+            throw new \InvalidArgumentException($l->t('Only the following characters are allowed in an Login:'
707
+                . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'));
708
+        }
709
+
710
+        // No empty user ID
711
+        if (trim($uid) === '') {
712
+            throw new \InvalidArgumentException($l->t('A valid Login must be provided'));
713
+        }
714
+
715
+        // No whitespace at the beginning or at the end
716
+        if (trim($uid) !== $uid) {
717
+            throw new \InvalidArgumentException($l->t('Login contains whitespace at the beginning or at the end'));
718
+        }
719
+
720
+        // User ID only consists of 1 or 2 dots (directory traversal)
721
+        if ($uid === '.' || $uid === '..') {
722
+            throw new \InvalidArgumentException($l->t('Login must not consist of dots only'));
723
+        }
724
+
725
+        // User ID is too long
726
+        if (strlen($uid) > IUser::MAX_USERID_LENGTH) {
727
+            throw new \InvalidArgumentException($l->t('Login is too long'));
728
+        }
729
+
730
+        if (!$this->verifyUid($uid, $checkDataDirectory)) {
731
+            throw new \InvalidArgumentException($l->t('Login is invalid because files already exist for this user'));
732
+        }
733
+    }
734
+
735
+    /**
736
+     * Gets the list of user ids sorted by lastLogin, from most recent to least recent
737
+     *
738
+     * @param int|null $limit how many users to fetch (default: 25, max: 100)
739
+     * @param int $offset from which offset to fetch
740
+     * @param string $search search users based on search params
741
+     * @return list<string> list of user IDs
742
+     */
743
+    public function getLastLoggedInUsers(?int $limit = null, int $offset = 0, string $search = ''): array {
744
+        // We can't load all users who already logged in
745
+        $limit = min(100, $limit ?: 25);
746
+
747
+        $connection = \OC::$server->getDatabaseConnection();
748
+        $queryBuilder = $connection->getQueryBuilder();
749
+        $queryBuilder->select('pref_login.userid')
750
+            ->from('preferences', 'pref_login')
751
+            ->where($queryBuilder->expr()->eq('pref_login.appid', $queryBuilder->expr()->literal('login')))
752
+            ->andWhere($queryBuilder->expr()->eq('pref_login.configkey', $queryBuilder->expr()->literal('lastLogin')))
753
+            ->setFirstResult($offset)
754
+            ->setMaxResults($limit)
755
+        ;
756
+
757
+        // Oracle don't want to run ORDER BY on CLOB column
758
+        $loginOrder = $connection->getDatabasePlatform() instanceof OraclePlatform
759
+            ? $queryBuilder->expr()->castColumn('pref_login.configvalue', IQueryBuilder::PARAM_INT)
760
+            : 'pref_login.configvalue';
761
+        $queryBuilder
762
+            ->orderBy($loginOrder, 'DESC')
763
+            ->addOrderBy($queryBuilder->func()->lower('pref_login.userid'), 'ASC');
764
+
765
+        if ($search !== '') {
766
+            $displayNameMatches = $this->searchDisplayName($search);
767
+            $matchedUids = array_map(static fn (IUser $u): string => $u->getUID(), $displayNameMatches);
768
+
769
+            $queryBuilder
770
+                ->leftJoin('pref_login', 'preferences', 'pref_email', $queryBuilder->expr()->andX(
771
+                    $queryBuilder->expr()->eq('pref_login.userid', 'pref_email.userid'),
772
+                    $queryBuilder->expr()->eq('pref_email.appid', $queryBuilder->expr()->literal('settings')),
773
+                    $queryBuilder->expr()->eq('pref_email.configkey', $queryBuilder->expr()->literal('email')),
774
+                ))
775
+                ->andWhere($queryBuilder->expr()->orX(
776
+                    $queryBuilder->expr()->in('pref_login.userid', $queryBuilder->createNamedParameter($matchedUids, IQueryBuilder::PARAM_STR_ARRAY)),
777
+                ));
778
+        }
779
+
780
+        /** @var list<string> */
781
+        $list = $queryBuilder->executeQuery()->fetchAll(\PDO::FETCH_COLUMN);
782
+
783
+        return $list;
784
+    }
785
+
786
+    private function verifyUid(string $uid, bool $checkDataDirectory = false): bool {
787
+        $appdata = 'appdata_' . $this->config->getSystemValueString('instanceid');
788
+
789
+        if (\in_array($uid, [
790
+            '.htaccess',
791
+            'files_external',
792
+            '__groupfolders',
793
+            '.ncdata',
794
+            'owncloud.log',
795
+            'nextcloud.log',
796
+            'updater.log',
797
+            'audit.log',
798
+            $appdata], true)) {
799
+            return false;
800
+        }
801
+
802
+        if (!$checkDataDirectory) {
803
+            return true;
804
+        }
805
+
806
+        $dataDirectory = $this->config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data');
807
+
808
+        return !file_exists(rtrim($dataDirectory, '/') . '/' . $uid);
809
+    }
810
+
811
+    public function getDisplayNameCache(): DisplayNameCache {
812
+        return $this->displayNameCache;
813
+    }
814
+
815
+    /**
816
+     * Gets the list of users sorted by lastLogin, from most recent to least recent
817
+     *
818
+     * @param int $offset from which offset to fetch
819
+     * @return \Iterator<IUser> list of user IDs
820
+     * @since 30.0.0
821
+     */
822
+    public function getSeenUsers(int $offset = 0): \Iterator {
823
+        $limit = 1000;
824
+
825
+        do {
826
+            $userIds = $this->getSeenUserIds($limit, $offset);
827
+            $offset += $limit;
828
+
829
+            foreach ($userIds as $userId) {
830
+                foreach ($this->backends as $backend) {
831
+                    if ($backend->userExists($userId)) {
832
+                        $user = $this->getUserObject($userId, $backend, false);
833
+                        yield $user;
834
+                        break;
835
+                    }
836
+                }
837
+            }
838
+        } while (count($userIds) === $limit);
839
+    }
840 840
 }
Please login to merge, or discard this patch.
lib/private/User/DisplayNameCache.php 1 patch
Indentation   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -25,58 +25,58 @@
 block discarded – undo
25 25
  * @template-implements IEventListener<UserChangedEvent|UserDeletedEvent>
26 26
  */
27 27
 class DisplayNameCache implements IEventListener {
28
-	private array $cache = [];
29
-	private ICache $memCache;
30
-	private IUserManager $userManager;
28
+    private array $cache = [];
29
+    private ICache $memCache;
30
+    private IUserManager $userManager;
31 31
 
32
-	public function __construct(ICacheFactory $cacheFactory, IUserManager $userManager) {
33
-		$this->memCache = $cacheFactory->createDistributed('displayNameMappingCache');
34
-		$this->userManager = $userManager;
35
-	}
32
+    public function __construct(ICacheFactory $cacheFactory, IUserManager $userManager) {
33
+        $this->memCache = $cacheFactory->createDistributed('displayNameMappingCache');
34
+        $this->userManager = $userManager;
35
+    }
36 36
 
37
-	public function getDisplayName(string $userId): ?string {
38
-		if (isset($this->cache[$userId])) {
39
-			return $this->cache[$userId];
40
-		}
37
+    public function getDisplayName(string $userId): ?string {
38
+        if (isset($this->cache[$userId])) {
39
+            return $this->cache[$userId];
40
+        }
41 41
 
42
-		if (strlen($userId) > IUser::MAX_USERID_LENGTH) {
43
-			return null;
44
-		}
42
+        if (strlen($userId) > IUser::MAX_USERID_LENGTH) {
43
+            return null;
44
+        }
45 45
 
46
-		$displayName = $this->memCache->get($userId);
47
-		if ($displayName) {
48
-			$this->cache[$userId] = $displayName;
49
-			return $displayName;
50
-		}
46
+        $displayName = $this->memCache->get($userId);
47
+        if ($displayName) {
48
+            $this->cache[$userId] = $displayName;
49
+            return $displayName;
50
+        }
51 51
 
52
-		$user = $this->userManager->get($userId);
53
-		if ($user) {
54
-			$displayName = $user->getDisplayName();
55
-		} else {
56
-			$displayName = null;
57
-		}
58
-		$this->cache[$userId] = $displayName;
59
-		$this->memCache->set($userId, $displayName, 60 * 10); // 10 minutes
52
+        $user = $this->userManager->get($userId);
53
+        if ($user) {
54
+            $displayName = $user->getDisplayName();
55
+        } else {
56
+            $displayName = null;
57
+        }
58
+        $this->cache[$userId] = $displayName;
59
+        $this->memCache->set($userId, $displayName, 60 * 10); // 10 minutes
60 60
 
61
-		return $displayName;
62
-	}
61
+        return $displayName;
62
+    }
63 63
 
64
-	public function clear(): void {
65
-		$this->cache = [];
66
-		$this->memCache->clear();
67
-	}
64
+    public function clear(): void {
65
+        $this->cache = [];
66
+        $this->memCache->clear();
67
+    }
68 68
 
69
-	public function handle(Event $event): void {
70
-		if ($event instanceof UserChangedEvent && $event->getFeature() === 'displayName') {
71
-			$userId = $event->getUser()->getUID();
72
-			$newDisplayName = $event->getValue();
73
-			$this->cache[$userId] = $newDisplayName;
74
-			$this->memCache->set($userId, $newDisplayName, 60 * 10); // 10 minutes
75
-		}
76
-		if ($event instanceof UserDeletedEvent) {
77
-			$userId = $event->getUser()->getUID();
78
-			unset($this->cache[$userId]);
79
-			$this->memCache->remove($userId);
80
-		}
81
-	}
69
+    public function handle(Event $event): void {
70
+        if ($event instanceof UserChangedEvent && $event->getFeature() === 'displayName') {
71
+            $userId = $event->getUser()->getUID();
72
+            $newDisplayName = $event->getValue();
73
+            $this->cache[$userId] = $newDisplayName;
74
+            $this->memCache->set($userId, $newDisplayName, 60 * 10); // 10 minutes
75
+        }
76
+        if ($event instanceof UserDeletedEvent) {
77
+            $userId = $event->getUser()->getUID();
78
+            unset($this->cache[$userId]);
79
+            $this->memCache->remove($userId);
80
+        }
81
+    }
82 82
 }
Please login to merge, or discard this patch.
lib/public/IUser.php 1 patch
Indentation   +290 added lines, -290 removed lines patch added patch discarded remove patch
@@ -15,294 +15,294 @@
 block discarded – undo
15 15
  * @since 8.0.0
16 16
  */
17 17
 interface IUser {
18
-	/**
19
-	 * @since 32.0.0
20
-	 */
21
-	public const MAX_USERID_LENGTH = 64;
22
-
23
-	/**
24
-	 * get the user id
25
-	 *
26
-	 * @return string
27
-	 * @since 8.0.0
28
-	 */
29
-	public function getUID();
30
-
31
-	/**
32
-	 * get the display name for the user, if no specific display name is set it will fallback to the user id
33
-	 *
34
-	 * @return string
35
-	 * @since 8.0.0
36
-	 */
37
-	public function getDisplayName();
38
-
39
-	/**
40
-	 * set the display name for the user
41
-	 *
42
-	 * @param string $displayName
43
-	 * @return bool
44
-	 * @since 8.0.0
45
-	 *
46
-	 * @since 25.0.0 Throw InvalidArgumentException
47
-	 * @throws \InvalidArgumentException
48
-	 */
49
-	public function setDisplayName($displayName);
50
-
51
-	/**
52
-	 * returns the timestamp of the user's last login or 0 if the user did never
53
-	 * login
54
-	 *
55
-	 * @return int
56
-	 * @since 8.0.0
57
-	 */
58
-	public function getLastLogin(): int;
59
-
60
-	/**
61
-	 * Returns the timestamp of the user's first login, 0 if the user did never login, or -1 if the data is unknown (first login was on an older version)
62
-	 *
63
-	 * @since 31.0.0
64
-	 */
65
-	public function getFirstLogin(): int;
66
-
67
-	/**
68
-	 * Updates the timestamp of the most recent login of this user (and first login if needed)
69
-	 *
70
-	 * @return bool whether this is the first login
71
-	 * @since 8.0.0
72
-	 */
73
-	public function updateLastLoginTimestamp(): bool;
74
-
75
-	/**
76
-	 * Delete the user
77
-	 *
78
-	 * @return bool
79
-	 * @since 8.0.0
80
-	 */
81
-	public function delete();
82
-
83
-	/**
84
-	 * Set the password of the user
85
-	 *
86
-	 * @param string $password
87
-	 * @param string $recoveryPassword for the encryption app to reset encryption keys
88
-	 * @return bool
89
-	 * @since 8.0.0
90
-	 */
91
-	public function setPassword($password, $recoveryPassword = null);
92
-
93
-	/**
94
-	 * Get the password hash of the user
95
-	 *
96
-	 * @return ?string the password hash hashed by `\OCP\Security\IHasher::hash()`
97
-	 * @since 30.0.0
98
-	 */
99
-	public function getPasswordHash(): ?string;
100
-
101
-	/**
102
-	 * Set the password hash of the user
103
-	 *
104
-	 * @param string $passwordHash the password hash hashed by `\OCP\Security\IHasher::hash()`
105
-	 * @throws InvalidArgumentException when `$passwordHash` is not a valid hash
106
-	 * @since 30.0.0
107
-	 */
108
-	public function setPasswordHash(string $passwordHash): bool;
109
-
110
-	/**
111
-	 * get the users home folder to mount
112
-	 *
113
-	 * @return string
114
-	 * @since 8.0.0
115
-	 */
116
-	public function getHome();
117
-
118
-	/**
119
-	 * Get the name of the backend class the user is connected with
120
-	 *
121
-	 * @return string
122
-	 * @since 8.0.0
123
-	 */
124
-	public function getBackendClassName();
125
-
126
-	/**
127
-	 * Get the backend for the current user object
128
-	 * @return ?UserInterface
129
-	 * @since 15.0.0
130
-	 */
131
-	public function getBackend();
132
-
133
-	/**
134
-	 * check if the backend allows the user to change their avatar on Personal page
135
-	 *
136
-	 * @return bool
137
-	 * @since 8.0.0
138
-	 */
139
-	public function canChangeAvatar();
140
-
141
-	/**
142
-	 * check if the backend supports changing passwords
143
-	 *
144
-	 * @return bool
145
-	 * @since 8.0.0
146
-	 */
147
-	public function canChangePassword();
148
-
149
-	/**
150
-	 * check if the backend supports changing display names
151
-	 *
152
-	 * @return bool
153
-	 * @since 8.0.0
154
-	 */
155
-	public function canChangeDisplayName();
156
-
157
-	/**
158
-	 * Check if the backend supports changing email
159
-	 *
160
-	 * @since 32.0.0
161
-	 */
162
-	public function canChangeEmail(): bool;
163
-
164
-	/**
165
-	 * check if the user is enabled
166
-	 *
167
-	 * @return bool
168
-	 * @since 8.0.0
169
-	 */
170
-	public function isEnabled();
171
-
172
-	/**
173
-	 * set the enabled status for the user
174
-	 *
175
-	 * @param bool $enabled
176
-	 * @since 8.0.0
177
-	 */
178
-	public function setEnabled(bool $enabled = true);
179
-
180
-	/**
181
-	 * get the user's email address
182
-	 *
183
-	 * @return string|null
184
-	 * @since 9.0.0
185
-	 */
186
-	public function getEMailAddress();
187
-
188
-	/**
189
-	 * get the user's system email address
190
-	 *
191
-	 * The system mail address may be read only and may be set from different
192
-	 * sources like LDAP, SAML or simply the admin.
193
-	 *
194
-	 * Use this getter only when the system address is needed. For picking the
195
-	 * proper address to e.g. send a mail to, use getEMailAddress().
196
-	 *
197
-	 * @return string|null
198
-	 * @since 23.0.0
199
-	 */
200
-	public function getSystemEMailAddress(): ?string;
201
-
202
-	/**
203
-	 * get the user's preferred email address
204
-	 *
205
-	 * The primary mail address may be set be the user to specify a different
206
-	 * email address where mails by Nextcloud are sent to. It is not necessarily
207
-	 * set.
208
-	 *
209
-	 * Use this getter only when the primary address is needed. For picking the
210
-	 * proper address to e.g. send a mail to, use getEMailAddress().
211
-	 *
212
-	 * @return string|null
213
-	 * @since 23.0.0
214
-	 */
215
-	public function getPrimaryEMailAddress(): ?string;
216
-
217
-	/**
218
-	 * get the avatar image if it exists
219
-	 *
220
-	 * @param int $size
221
-	 * @return IImage|null
222
-	 * @since 9.0.0
223
-	 */
224
-	public function getAvatarImage($size);
225
-
226
-	/**
227
-	 * get the federation cloud id
228
-	 *
229
-	 * @return string
230
-	 * @since 9.0.0
231
-	 */
232
-	public function getCloudId();
233
-
234
-	/**
235
-	 * set the email address of the user
236
-	 *
237
-	 * It is an alias to setSystemEMailAddress()
238
-	 *
239
-	 * @param string|null $mailAddress
240
-	 * @return void
241
-	 * @since 9.0.0
242
-	 * @deprecated 23.0.0 use setSystemEMailAddress() or setPrimaryEMailAddress()
243
-	 */
244
-	public function setEMailAddress($mailAddress);
245
-
246
-	/**
247
-	 * Set the system email address of the user
248
-	 *
249
-	 * This is supposed to be used when the email is set from different sources
250
-	 * (i.e. other user backends, admin).
251
-	 *
252
-	 * @since 23.0.0
253
-	 */
254
-	public function setSystemEMailAddress(string $mailAddress): void;
255
-
256
-	/**
257
-	 * Set the primary email address of the user.
258
-	 *
259
-	 * This method should be typically called when the user is changing their
260
-	 * own primary address and is not allowed to change their system email.
261
-	 *
262
-	 * The mail address provided here must be already registered as an
263
-	 * additional mail in the user account and also be verified locally. Also
264
-	 * an empty string is allowed to delete this preference.
265
-	 *
266
-	 * @throws InvalidArgumentException when the provided email address does not
267
-	 *                                  satisfy constraints.
268
-	 *
269
-	 * @since 23.0.0
270
-	 */
271
-	public function setPrimaryEMailAddress(string $mailAddress): void;
272
-
273
-	/**
274
-	 * get the users' quota in human readable form. If a specific quota is not
275
-	 * set for the user, the default value is returned. If a default setting
276
-	 * was not set otherwise, it is return as 'none', i.e. quota is not limited.
277
-	 *
278
-	 * @return string
279
-	 * @since 9.0.0
280
-	 */
281
-	public function getQuota();
282
-
283
-	/**
284
-	 * set the users' quota
285
-	 *
286
-	 * @param string $quota
287
-	 * @return void
288
-	 * @since 9.0.0
289
-	 */
290
-	public function setQuota($quota);
291
-
292
-	/**
293
-	 * Get the user's manager UIDs
294
-	 *
295
-	 * @since 27.0.0
296
-	 * @return string[]
297
-	 */
298
-	public function getManagerUids(): array;
299
-
300
-	/**
301
-	 * Set the user's manager UIDs
302
-	 *
303
-	 * @param string[] $uids UIDs of all managers
304
-	 * @return void
305
-	 * @since 27.0.0
306
-	 */
307
-	public function setManagerUids(array $uids): void;
18
+    /**
19
+     * @since 32.0.0
20
+     */
21
+    public const MAX_USERID_LENGTH = 64;
22
+
23
+    /**
24
+     * get the user id
25
+     *
26
+     * @return string
27
+     * @since 8.0.0
28
+     */
29
+    public function getUID();
30
+
31
+    /**
32
+     * get the display name for the user, if no specific display name is set it will fallback to the user id
33
+     *
34
+     * @return string
35
+     * @since 8.0.0
36
+     */
37
+    public function getDisplayName();
38
+
39
+    /**
40
+     * set the display name for the user
41
+     *
42
+     * @param string $displayName
43
+     * @return bool
44
+     * @since 8.0.0
45
+     *
46
+     * @since 25.0.0 Throw InvalidArgumentException
47
+     * @throws \InvalidArgumentException
48
+     */
49
+    public function setDisplayName($displayName);
50
+
51
+    /**
52
+     * returns the timestamp of the user's last login or 0 if the user did never
53
+     * login
54
+     *
55
+     * @return int
56
+     * @since 8.0.0
57
+     */
58
+    public function getLastLogin(): int;
59
+
60
+    /**
61
+     * Returns the timestamp of the user's first login, 0 if the user did never login, or -1 if the data is unknown (first login was on an older version)
62
+     *
63
+     * @since 31.0.0
64
+     */
65
+    public function getFirstLogin(): int;
66
+
67
+    /**
68
+     * Updates the timestamp of the most recent login of this user (and first login if needed)
69
+     *
70
+     * @return bool whether this is the first login
71
+     * @since 8.0.0
72
+     */
73
+    public function updateLastLoginTimestamp(): bool;
74
+
75
+    /**
76
+     * Delete the user
77
+     *
78
+     * @return bool
79
+     * @since 8.0.0
80
+     */
81
+    public function delete();
82
+
83
+    /**
84
+     * Set the password of the user
85
+     *
86
+     * @param string $password
87
+     * @param string $recoveryPassword for the encryption app to reset encryption keys
88
+     * @return bool
89
+     * @since 8.0.0
90
+     */
91
+    public function setPassword($password, $recoveryPassword = null);
92
+
93
+    /**
94
+     * Get the password hash of the user
95
+     *
96
+     * @return ?string the password hash hashed by `\OCP\Security\IHasher::hash()`
97
+     * @since 30.0.0
98
+     */
99
+    public function getPasswordHash(): ?string;
100
+
101
+    /**
102
+     * Set the password hash of the user
103
+     *
104
+     * @param string $passwordHash the password hash hashed by `\OCP\Security\IHasher::hash()`
105
+     * @throws InvalidArgumentException when `$passwordHash` is not a valid hash
106
+     * @since 30.0.0
107
+     */
108
+    public function setPasswordHash(string $passwordHash): bool;
109
+
110
+    /**
111
+     * get the users home folder to mount
112
+     *
113
+     * @return string
114
+     * @since 8.0.0
115
+     */
116
+    public function getHome();
117
+
118
+    /**
119
+     * Get the name of the backend class the user is connected with
120
+     *
121
+     * @return string
122
+     * @since 8.0.0
123
+     */
124
+    public function getBackendClassName();
125
+
126
+    /**
127
+     * Get the backend for the current user object
128
+     * @return ?UserInterface
129
+     * @since 15.0.0
130
+     */
131
+    public function getBackend();
132
+
133
+    /**
134
+     * check if the backend allows the user to change their avatar on Personal page
135
+     *
136
+     * @return bool
137
+     * @since 8.0.0
138
+     */
139
+    public function canChangeAvatar();
140
+
141
+    /**
142
+     * check if the backend supports changing passwords
143
+     *
144
+     * @return bool
145
+     * @since 8.0.0
146
+     */
147
+    public function canChangePassword();
148
+
149
+    /**
150
+     * check if the backend supports changing display names
151
+     *
152
+     * @return bool
153
+     * @since 8.0.0
154
+     */
155
+    public function canChangeDisplayName();
156
+
157
+    /**
158
+     * Check if the backend supports changing email
159
+     *
160
+     * @since 32.0.0
161
+     */
162
+    public function canChangeEmail(): bool;
163
+
164
+    /**
165
+     * check if the user is enabled
166
+     *
167
+     * @return bool
168
+     * @since 8.0.0
169
+     */
170
+    public function isEnabled();
171
+
172
+    /**
173
+     * set the enabled status for the user
174
+     *
175
+     * @param bool $enabled
176
+     * @since 8.0.0
177
+     */
178
+    public function setEnabled(bool $enabled = true);
179
+
180
+    /**
181
+     * get the user's email address
182
+     *
183
+     * @return string|null
184
+     * @since 9.0.0
185
+     */
186
+    public function getEMailAddress();
187
+
188
+    /**
189
+     * get the user's system email address
190
+     *
191
+     * The system mail address may be read only and may be set from different
192
+     * sources like LDAP, SAML or simply the admin.
193
+     *
194
+     * Use this getter only when the system address is needed. For picking the
195
+     * proper address to e.g. send a mail to, use getEMailAddress().
196
+     *
197
+     * @return string|null
198
+     * @since 23.0.0
199
+     */
200
+    public function getSystemEMailAddress(): ?string;
201
+
202
+    /**
203
+     * get the user's preferred email address
204
+     *
205
+     * The primary mail address may be set be the user to specify a different
206
+     * email address where mails by Nextcloud are sent to. It is not necessarily
207
+     * set.
208
+     *
209
+     * Use this getter only when the primary address is needed. For picking the
210
+     * proper address to e.g. send a mail to, use getEMailAddress().
211
+     *
212
+     * @return string|null
213
+     * @since 23.0.0
214
+     */
215
+    public function getPrimaryEMailAddress(): ?string;
216
+
217
+    /**
218
+     * get the avatar image if it exists
219
+     *
220
+     * @param int $size
221
+     * @return IImage|null
222
+     * @since 9.0.0
223
+     */
224
+    public function getAvatarImage($size);
225
+
226
+    /**
227
+     * get the federation cloud id
228
+     *
229
+     * @return string
230
+     * @since 9.0.0
231
+     */
232
+    public function getCloudId();
233
+
234
+    /**
235
+     * set the email address of the user
236
+     *
237
+     * It is an alias to setSystemEMailAddress()
238
+     *
239
+     * @param string|null $mailAddress
240
+     * @return void
241
+     * @since 9.0.0
242
+     * @deprecated 23.0.0 use setSystemEMailAddress() or setPrimaryEMailAddress()
243
+     */
244
+    public function setEMailAddress($mailAddress);
245
+
246
+    /**
247
+     * Set the system email address of the user
248
+     *
249
+     * This is supposed to be used when the email is set from different sources
250
+     * (i.e. other user backends, admin).
251
+     *
252
+     * @since 23.0.0
253
+     */
254
+    public function setSystemEMailAddress(string $mailAddress): void;
255
+
256
+    /**
257
+     * Set the primary email address of the user.
258
+     *
259
+     * This method should be typically called when the user is changing their
260
+     * own primary address and is not allowed to change their system email.
261
+     *
262
+     * The mail address provided here must be already registered as an
263
+     * additional mail in the user account and also be verified locally. Also
264
+     * an empty string is allowed to delete this preference.
265
+     *
266
+     * @throws InvalidArgumentException when the provided email address does not
267
+     *                                  satisfy constraints.
268
+     *
269
+     * @since 23.0.0
270
+     */
271
+    public function setPrimaryEMailAddress(string $mailAddress): void;
272
+
273
+    /**
274
+     * get the users' quota in human readable form. If a specific quota is not
275
+     * set for the user, the default value is returned. If a default setting
276
+     * was not set otherwise, it is return as 'none', i.e. quota is not limited.
277
+     *
278
+     * @return string
279
+     * @since 9.0.0
280
+     */
281
+    public function getQuota();
282
+
283
+    /**
284
+     * set the users' quota
285
+     *
286
+     * @param string $quota
287
+     * @return void
288
+     * @since 9.0.0
289
+     */
290
+    public function setQuota($quota);
291
+
292
+    /**
293
+     * Get the user's manager UIDs
294
+     *
295
+     * @since 27.0.0
296
+     * @return string[]
297
+     */
298
+    public function getManagerUids(): array;
299
+
300
+    /**
301
+     * Set the user's manager UIDs
302
+     *
303
+     * @param string[] $uids UIDs of all managers
304
+     * @return void
305
+     * @since 27.0.0
306
+     */
307
+    public function setManagerUids(array $uids): void;
308 308
 }
Please login to merge, or discard this patch.