Completed
Pull Request — master (#9063)
by Björn
21:47
created
lib/private/User/Manager.php 1 patch
Indentation   +483 added lines, -483 removed lines patch added patch discarded remove patch
@@ -54,487 +54,487 @@
 block discarded – undo
54 54
  * @package OC\User
55 55
  */
56 56
 class Manager extends PublicEmitter implements IUserManager {
57
-	/**
58
-	 * @var \OCP\UserInterface[] $backends
59
-	 */
60
-	private $backends = array();
61
-
62
-	/**
63
-	 * @var \OC\User\User[] $cachedUsers
64
-	 */
65
-	private $cachedUsers = array();
66
-
67
-	/**
68
-	 * @var \OCP\IConfig $config
69
-	 */
70
-	private $config;
71
-
72
-	/**
73
-	 * @param \OCP\IConfig $config
74
-	 */
75
-	public function __construct(IConfig $config) {
76
-		$this->config = $config;
77
-		$cachedUsers = &$this->cachedUsers;
78
-		$this->listen('\OC\User', 'postDelete', function ($user) use (&$cachedUsers) {
79
-			/** @var \OC\User\User $user */
80
-			unset($cachedUsers[$user->getUID()]);
81
-		});
82
-	}
83
-
84
-	/**
85
-	 * Get the active backends
86
-	 * @return \OCP\UserInterface[]
87
-	 */
88
-	public function getBackends() {
89
-		return $this->backends;
90
-	}
91
-
92
-	/**
93
-	 * register a user backend
94
-	 *
95
-	 * @param \OCP\UserInterface $backend
96
-	 */
97
-	public function registerBackend($backend) {
98
-		$this->backends[] = $backend;
99
-	}
100
-
101
-	/**
102
-	 * remove a user backend
103
-	 *
104
-	 * @param \OCP\UserInterface $backend
105
-	 */
106
-	public function removeBackend($backend) {
107
-		$this->cachedUsers = array();
108
-		if (($i = array_search($backend, $this->backends)) !== false) {
109
-			unset($this->backends[$i]);
110
-		}
111
-	}
112
-
113
-	/**
114
-	 * remove all user backends
115
-	 */
116
-	public function clearBackends() {
117
-		$this->cachedUsers = array();
118
-		$this->backends = array();
119
-	}
120
-
121
-	/**
122
-	 * get a user by user id
123
-	 *
124
-	 * @param string $uid
125
-	 * @return \OC\User\User|null Either the user or null if the specified user does not exist
126
-	 */
127
-	public function get($uid) {
128
-		if (is_null($uid) || $uid === '' || $uid === false) {
129
-			return null;
130
-		}
131
-		if (isset($this->cachedUsers[$uid])) { //check the cache first to prevent having to loop over the backends
132
-			return $this->cachedUsers[$uid];
133
-		}
134
-		foreach ($this->backends as $backend) {
135
-			if ($backend->userExists($uid)) {
136
-				return $this->getUserObject($uid, $backend);
137
-			}
138
-		}
139
-		return null;
140
-	}
141
-
142
-	/**
143
-	 * get or construct the user object
144
-	 *
145
-	 * @param string $uid
146
-	 * @param \OCP\UserInterface $backend
147
-	 * @param bool $cacheUser If false the newly created user object will not be cached
148
-	 * @return \OC\User\User
149
-	 */
150
-	protected function getUserObject($uid, $backend, $cacheUser = true) {
151
-		if (isset($this->cachedUsers[$uid])) {
152
-			return $this->cachedUsers[$uid];
153
-		}
154
-
155
-		$user = new User($uid, $backend, $this, $this->config);
156
-		if ($cacheUser) {
157
-			$this->cachedUsers[$uid] = $user;
158
-		}
159
-		return $user;
160
-	}
161
-
162
-	/**
163
-	 * check if a user exists
164
-	 *
165
-	 * @param string $uid
166
-	 * @return bool
167
-	 */
168
-	public function userExists($uid) {
169
-		$user = $this->get($uid);
170
-		return ($user !== null);
171
-	}
172
-
173
-	/**
174
-	 * Check if the password is valid for the user
175
-	 *
176
-	 * @param string $loginName
177
-	 * @param string $password
178
-	 * @return mixed the User object on success, false otherwise
179
-	 */
180
-	public function checkPassword($loginName, $password) {
181
-		$result = $this->checkPasswordNoLogging($loginName, $password);
182
-
183
-		if ($result === false) {
184
-			\OC::$server->getLogger()->warning('Login failed: \''. $loginName .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). '\')', ['app' => 'core']);
185
-		}
186
-
187
-		return $result;
188
-	}
189
-
190
-	/**
191
-	 * Check if the password is valid for the user
192
-	 *
193
-	 * @internal
194
-	 * @param string $loginName
195
-	 * @param string $password
196
-	 * @return mixed the User object on success, false otherwise
197
-	 */
198
-	public function checkPasswordNoLogging($loginName, $password) {
199
-		$loginName = str_replace("\0", '', $loginName);
200
-		$password = str_replace("\0", '', $password);
201
-
202
-		foreach ($this->backends as $backend) {
203
-			if ($backend->implementsActions(Backend::CHECK_PASSWORD)) {
204
-				$uid = $backend->checkPassword($loginName, $password);
205
-				if ($uid !== false) {
206
-					return $this->getUserObject($uid, $backend);
207
-				}
208
-			}
209
-		}
210
-
211
-		return false;
212
-	}
213
-
214
-	/**
215
-	 * search by user id
216
-	 *
217
-	 * @param string $pattern
218
-	 * @param int $limit
219
-	 * @param int $offset
220
-	 * @return \OC\User\User[]
221
-	 */
222
-	public function search($pattern, $limit = null, $offset = null) {
223
-		$users = array();
224
-		foreach ($this->backends as $backend) {
225
-			$backendUsers = $backend->getUsers($pattern, $limit, $offset);
226
-			if (is_array($backendUsers)) {
227
-				foreach ($backendUsers as $uid) {
228
-					$users[$uid] = $this->getUserObject($uid, $backend);
229
-				}
230
-			}
231
-		}
232
-
233
-		uasort($users, function ($a, $b) {
234
-			/**
235
-			 * @var \OC\User\User $a
236
-			 * @var \OC\User\User $b
237
-			 */
238
-			return strcmp($a->getUID(), $b->getUID());
239
-		});
240
-		return $users;
241
-	}
242
-
243
-	/**
244
-	 * search by displayName
245
-	 *
246
-	 * @param string $pattern
247
-	 * @param int $limit
248
-	 * @param int $offset
249
-	 * @return \OC\User\User[]
250
-	 */
251
-	public function searchDisplayName($pattern, $limit = null, $offset = null) {
252
-		$users = array();
253
-		foreach ($this->backends as $backend) {
254
-			$backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
255
-			if (is_array($backendUsers)) {
256
-				foreach ($backendUsers as $uid => $displayName) {
257
-					$users[] = $this->getUserObject($uid, $backend);
258
-				}
259
-			}
260
-		}
261
-
262
-		usort($users, function ($a, $b) {
263
-			/**
264
-			 * @var \OC\User\User $a
265
-			 * @var \OC\User\User $b
266
-			 */
267
-			return strcmp(strtolower($a->getDisplayName()), strtolower($b->getDisplayName()));
268
-		});
269
-		return $users;
270
-	}
271
-
272
-	/**
273
-	 * @param string $uid
274
-	 * @param string $password
275
-	 * @throws \InvalidArgumentException
276
-	 * @return bool|IUser the created user or false
277
-	 */
278
-	public function createUser($uid, $password) {
279
-		$localBackends = [];
280
-		foreach ($this->backends as $backend) {
281
-			if ($backend instanceof Database) {
282
-				// First check if there is another user backend
283
-				$localBackends[] = $backend;
284
-				continue;
285
-			}
286
-
287
-			if ($backend->implementsActions(Backend::CREATE_USER)) {
288
-				return $this->createUserFromBackend($uid, $password, $backend);
289
-			}
290
-		}
291
-
292
-		foreach ($localBackends as $backend) {
293
-			if ($backend->implementsActions(Backend::CREATE_USER)) {
294
-				return $this->createUserFromBackend($uid, $password, $backend);
295
-			}
296
-		}
297
-
298
-		return false;
299
-	}
300
-
301
-	/**
302
-	 * @param string $uid
303
-	 * @param string $password
304
-	 * @param UserInterface $backend
305
-	 * @return IUser|null
306
-	 * @throws \InvalidArgumentException
307
-	 */
308
-	public function createUserFromBackend($uid, $password, UserInterface $backend) {
309
-		$l = \OC::$server->getL10N('lib');
310
-
311
-		// Check the name for bad characters
312
-		// Allowed are: "a-z", "A-Z", "0-9" and "_.@-'"
313
-		if (preg_match('/[^a-zA-Z0-9 _\.@\-\']/', $uid)) {
314
-			throw new \InvalidArgumentException($l->t('Only the following characters are allowed in a username:'
315
-				. ' "a-z", "A-Z", "0-9", and "_.@-\'"'));
316
-		}
317
-		// No empty username
318
-		if (trim($uid) === '') {
319
-			throw new \InvalidArgumentException($l->t('A valid username must be provided'));
320
-		}
321
-		// No whitespace at the beginning or at the end
322
-		if (trim($uid) !== $uid) {
323
-			throw new \InvalidArgumentException($l->t('Username contains whitespace at the beginning or at the end'));
324
-		}
325
-		// Username only consists of 1 or 2 dots (directory traversal)
326
-		if ($uid === '.' || $uid === '..') {
327
-			throw new \InvalidArgumentException($l->t('Username must not consist of dots only'));
328
-		}
329
-		// No empty password
330
-		if (trim($password) === '') {
331
-			throw new \InvalidArgumentException($l->t('A valid password must be provided'));
332
-		}
333
-
334
-		// Check if user already exists
335
-		if ($this->userExists($uid)) {
336
-			throw new \InvalidArgumentException($l->t('The username is already being used'));
337
-		}
338
-
339
-		$this->emit('\OC\User', 'preCreateUser', [$uid, $password]);
340
-		$state = $backend->createUser($uid, $password);
341
-		if($state === false) {
342
-			throw new \InvalidArgumentException($l->t('Could not create user'));
343
-		}
344
-		$user = $this->getUserObject($uid, $backend);
345
-		if ($user instanceof IUser) {
346
-			$this->emit('\OC\User', 'postCreateUser', [$user, $password]);
347
-		}
348
-		return $user;
349
-	}
350
-
351
-	/**
352
-	 * returns how many users per backend exist (if supported by backend)
353
-	 *
354
-	 * @param boolean $hasLoggedIn when true only users that have a lastLogin
355
-	 *                entry in the preferences table will be affected
356
-	 * @return array|int an array of backend class as key and count number as value
357
-	 *                if $hasLoggedIn is true only an int is returned
358
-	 */
359
-	public function countUsers($hasLoggedIn = false) {
360
-		if ($hasLoggedIn) {
361
-			return $this->countSeenUsers();
362
-		}
363
-		$userCountStatistics = [];
364
-		foreach ($this->backends as $backend) {
365
-			if ($backend->implementsActions(Backend::COUNT_USERS)) {
366
-				$backendUsers = $backend->countUsers();
367
-				if($backendUsers !== false) {
368
-					if($backend instanceof IUserBackend) {
369
-						$name = $backend->getBackendName();
370
-					} else {
371
-						$name = get_class($backend);
372
-					}
373
-					if(isset($userCountStatistics[$name])) {
374
-						$userCountStatistics[$name] += $backendUsers;
375
-					} else {
376
-						$userCountStatistics[$name] = $backendUsers;
377
-					}
378
-				}
379
-			}
380
-		}
381
-		return $userCountStatistics;
382
-	}
383
-
384
-	/**
385
-	 * The callback is executed for each user on each backend.
386
-	 * If the callback returns false no further users will be retrieved.
387
-	 *
388
-	 * @param \Closure $callback
389
-	 * @param string $search
390
-	 * @param boolean $onlySeen when true only users that have a lastLogin entry
391
-	 *                in the preferences table will be affected
392
-	 * @since 9.0.0
393
-	 */
394
-	public function callForAllUsers(\Closure $callback, $search = '', $onlySeen = false) {
395
-		if ($onlySeen) {
396
-			$this->callForSeenUsers($callback);
397
-		} else {
398
-			foreach ($this->getBackends() as $backend) {
399
-				$limit = 500;
400
-				$offset = 0;
401
-				do {
402
-					$users = $backend->getUsers($search, $limit, $offset);
403
-					foreach ($users as $uid) {
404
-						if (!$backend->userExists($uid)) {
405
-							continue;
406
-						}
407
-						$user = $this->getUserObject($uid, $backend, false);
408
-						$return = $callback($user);
409
-						if ($return === false) {
410
-							break;
411
-						}
412
-					}
413
-					$offset += $limit;
414
-				} while (count($users) >= $limit);
415
-			}
416
-		}
417
-	}
418
-
419
-	/**
420
-	 * returns how many users have logged in once
421
-	 *
422
-	 * @return int
423
-	 * @since 12.0.0
424
-	 */
425
-	public function countDisabledUsers() {
426
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
427
-		$queryBuilder->select($queryBuilder->createFunction('COUNT(*)'))
428
-			->from('preferences')
429
-			->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core')))
430
-			->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
431
-			->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR));
432
-
433
-		$query = $queryBuilder->execute();
434
-
435
-		$result = (int)$query->fetchColumn();
436
-		$query->closeCursor();
437
-
438
-		return $result;
439
-	}
440
-
441
-	/**
442
-	 * returns how many users have logged in once
443
-	 *
444
-	 * @return int
445
-	 * @since 11.0.0
446
-	 */
447
-	public function countSeenUsers() {
448
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
449
-		$queryBuilder->select($queryBuilder->createFunction('COUNT(*)'))
450
-			->from('preferences')
451
-			->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('login')))
452
-			->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('lastLogin')))
453
-			->andWhere($queryBuilder->expr()->isNotNull('configvalue'));
454
-
455
-		$query = $queryBuilder->execute();
456
-
457
-		$result = (int)$query->fetchColumn();
458
-		$query->closeCursor();
459
-
460
-		return $result;
461
-	}
462
-
463
-	/**
464
-	 * @param \Closure $callback
465
-	 * @since 11.0.0
466
-	 */
467
-	public function callForSeenUsers(\Closure $callback) {
468
-		$limit = 1000;
469
-		$offset = 0;
470
-		do {
471
-			$userIds = $this->getSeenUserIds($limit, $offset);
472
-			$offset += $limit;
473
-			foreach ($userIds as $userId) {
474
-				foreach ($this->backends as $backend) {
475
-					if ($backend->userExists($userId)) {
476
-						$user = $this->getUserObject($userId, $backend, false);
477
-						$return = $callback($user);
478
-						if ($return === false) {
479
-							return;
480
-						}
481
-						break;
482
-					}
483
-				}
484
-			}
485
-		} while (count($userIds) >= $limit);
486
-	}
487
-
488
-	/**
489
-	 * Getting all userIds that have a listLogin value requires checking the
490
-	 * value in php because on oracle you cannot use a clob in a where clause,
491
-	 * preventing us from doing a not null or length(value) > 0 check.
492
-	 *
493
-	 * @param int $limit
494
-	 * @param int $offset
495
-	 * @return string[] with user ids
496
-	 */
497
-	private function getSeenUserIds($limit = null, $offset = null) {
498
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
499
-		$queryBuilder->select(['userid'])
500
-			->from('preferences')
501
-			->where($queryBuilder->expr()->eq(
502
-				'appid', $queryBuilder->createNamedParameter('login'))
503
-			)
504
-			->andWhere($queryBuilder->expr()->eq(
505
-				'configkey', $queryBuilder->createNamedParameter('lastLogin'))
506
-			)
507
-			->andWhere($queryBuilder->expr()->isNotNull('configvalue')
508
-			);
509
-
510
-		if ($limit !== null) {
511
-			$queryBuilder->setMaxResults($limit);
512
-		}
513
-		if ($offset !== null) {
514
-			$queryBuilder->setFirstResult($offset);
515
-		}
516
-		$query = $queryBuilder->execute();
517
-		$result = [];
518
-
519
-		while ($row = $query->fetch()) {
520
-			$result[] = $row['userid'];
521
-		}
522
-
523
-		$query->closeCursor();
524
-
525
-		return $result;
526
-	}
527
-
528
-	/**
529
-	 * @param string $email
530
-	 * @return IUser[]
531
-	 * @since 9.1.0
532
-	 */
533
-	public function getByEmail($email) {
534
-		$userIds = $this->config->getUsersForUserValue('settings', 'email', $email);
535
-
536
-		return array_map(function($uid) {
537
-			return $this->get($uid);
538
-		}, $userIds);
539
-	}
57
+    /**
58
+     * @var \OCP\UserInterface[] $backends
59
+     */
60
+    private $backends = array();
61
+
62
+    /**
63
+     * @var \OC\User\User[] $cachedUsers
64
+     */
65
+    private $cachedUsers = array();
66
+
67
+    /**
68
+     * @var \OCP\IConfig $config
69
+     */
70
+    private $config;
71
+
72
+    /**
73
+     * @param \OCP\IConfig $config
74
+     */
75
+    public function __construct(IConfig $config) {
76
+        $this->config = $config;
77
+        $cachedUsers = &$this->cachedUsers;
78
+        $this->listen('\OC\User', 'postDelete', function ($user) use (&$cachedUsers) {
79
+            /** @var \OC\User\User $user */
80
+            unset($cachedUsers[$user->getUID()]);
81
+        });
82
+    }
83
+
84
+    /**
85
+     * Get the active backends
86
+     * @return \OCP\UserInterface[]
87
+     */
88
+    public function getBackends() {
89
+        return $this->backends;
90
+    }
91
+
92
+    /**
93
+     * register a user backend
94
+     *
95
+     * @param \OCP\UserInterface $backend
96
+     */
97
+    public function registerBackend($backend) {
98
+        $this->backends[] = $backend;
99
+    }
100
+
101
+    /**
102
+     * remove a user backend
103
+     *
104
+     * @param \OCP\UserInterface $backend
105
+     */
106
+    public function removeBackend($backend) {
107
+        $this->cachedUsers = array();
108
+        if (($i = array_search($backend, $this->backends)) !== false) {
109
+            unset($this->backends[$i]);
110
+        }
111
+    }
112
+
113
+    /**
114
+     * remove all user backends
115
+     */
116
+    public function clearBackends() {
117
+        $this->cachedUsers = array();
118
+        $this->backends = array();
119
+    }
120
+
121
+    /**
122
+     * get a user by user id
123
+     *
124
+     * @param string $uid
125
+     * @return \OC\User\User|null Either the user or null if the specified user does not exist
126
+     */
127
+    public function get($uid) {
128
+        if (is_null($uid) || $uid === '' || $uid === false) {
129
+            return null;
130
+        }
131
+        if (isset($this->cachedUsers[$uid])) { //check the cache first to prevent having to loop over the backends
132
+            return $this->cachedUsers[$uid];
133
+        }
134
+        foreach ($this->backends as $backend) {
135
+            if ($backend->userExists($uid)) {
136
+                return $this->getUserObject($uid, $backend);
137
+            }
138
+        }
139
+        return null;
140
+    }
141
+
142
+    /**
143
+     * get or construct the user object
144
+     *
145
+     * @param string $uid
146
+     * @param \OCP\UserInterface $backend
147
+     * @param bool $cacheUser If false the newly created user object will not be cached
148
+     * @return \OC\User\User
149
+     */
150
+    protected function getUserObject($uid, $backend, $cacheUser = true) {
151
+        if (isset($this->cachedUsers[$uid])) {
152
+            return $this->cachedUsers[$uid];
153
+        }
154
+
155
+        $user = new User($uid, $backend, $this, $this->config);
156
+        if ($cacheUser) {
157
+            $this->cachedUsers[$uid] = $user;
158
+        }
159
+        return $user;
160
+    }
161
+
162
+    /**
163
+     * check if a user exists
164
+     *
165
+     * @param string $uid
166
+     * @return bool
167
+     */
168
+    public function userExists($uid) {
169
+        $user = $this->get($uid);
170
+        return ($user !== null);
171
+    }
172
+
173
+    /**
174
+     * Check if the password is valid for the user
175
+     *
176
+     * @param string $loginName
177
+     * @param string $password
178
+     * @return mixed the User object on success, false otherwise
179
+     */
180
+    public function checkPassword($loginName, $password) {
181
+        $result = $this->checkPasswordNoLogging($loginName, $password);
182
+
183
+        if ($result === false) {
184
+            \OC::$server->getLogger()->warning('Login failed: \''. $loginName .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). '\')', ['app' => 'core']);
185
+        }
186
+
187
+        return $result;
188
+    }
189
+
190
+    /**
191
+     * Check if the password is valid for the user
192
+     *
193
+     * @internal
194
+     * @param string $loginName
195
+     * @param string $password
196
+     * @return mixed the User object on success, false otherwise
197
+     */
198
+    public function checkPasswordNoLogging($loginName, $password) {
199
+        $loginName = str_replace("\0", '', $loginName);
200
+        $password = str_replace("\0", '', $password);
201
+
202
+        foreach ($this->backends as $backend) {
203
+            if ($backend->implementsActions(Backend::CHECK_PASSWORD)) {
204
+                $uid = $backend->checkPassword($loginName, $password);
205
+                if ($uid !== false) {
206
+                    return $this->getUserObject($uid, $backend);
207
+                }
208
+            }
209
+        }
210
+
211
+        return false;
212
+    }
213
+
214
+    /**
215
+     * search by user id
216
+     *
217
+     * @param string $pattern
218
+     * @param int $limit
219
+     * @param int $offset
220
+     * @return \OC\User\User[]
221
+     */
222
+    public function search($pattern, $limit = null, $offset = null) {
223
+        $users = array();
224
+        foreach ($this->backends as $backend) {
225
+            $backendUsers = $backend->getUsers($pattern, $limit, $offset);
226
+            if (is_array($backendUsers)) {
227
+                foreach ($backendUsers as $uid) {
228
+                    $users[$uid] = $this->getUserObject($uid, $backend);
229
+                }
230
+            }
231
+        }
232
+
233
+        uasort($users, function ($a, $b) {
234
+            /**
235
+             * @var \OC\User\User $a
236
+             * @var \OC\User\User $b
237
+             */
238
+            return strcmp($a->getUID(), $b->getUID());
239
+        });
240
+        return $users;
241
+    }
242
+
243
+    /**
244
+     * search by displayName
245
+     *
246
+     * @param string $pattern
247
+     * @param int $limit
248
+     * @param int $offset
249
+     * @return \OC\User\User[]
250
+     */
251
+    public function searchDisplayName($pattern, $limit = null, $offset = null) {
252
+        $users = array();
253
+        foreach ($this->backends as $backend) {
254
+            $backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
255
+            if (is_array($backendUsers)) {
256
+                foreach ($backendUsers as $uid => $displayName) {
257
+                    $users[] = $this->getUserObject($uid, $backend);
258
+                }
259
+            }
260
+        }
261
+
262
+        usort($users, function ($a, $b) {
263
+            /**
264
+             * @var \OC\User\User $a
265
+             * @var \OC\User\User $b
266
+             */
267
+            return strcmp(strtolower($a->getDisplayName()), strtolower($b->getDisplayName()));
268
+        });
269
+        return $users;
270
+    }
271
+
272
+    /**
273
+     * @param string $uid
274
+     * @param string $password
275
+     * @throws \InvalidArgumentException
276
+     * @return bool|IUser the created user or false
277
+     */
278
+    public function createUser($uid, $password) {
279
+        $localBackends = [];
280
+        foreach ($this->backends as $backend) {
281
+            if ($backend instanceof Database) {
282
+                // First check if there is another user backend
283
+                $localBackends[] = $backend;
284
+                continue;
285
+            }
286
+
287
+            if ($backend->implementsActions(Backend::CREATE_USER)) {
288
+                return $this->createUserFromBackend($uid, $password, $backend);
289
+            }
290
+        }
291
+
292
+        foreach ($localBackends as $backend) {
293
+            if ($backend->implementsActions(Backend::CREATE_USER)) {
294
+                return $this->createUserFromBackend($uid, $password, $backend);
295
+            }
296
+        }
297
+
298
+        return false;
299
+    }
300
+
301
+    /**
302
+     * @param string $uid
303
+     * @param string $password
304
+     * @param UserInterface $backend
305
+     * @return IUser|null
306
+     * @throws \InvalidArgumentException
307
+     */
308
+    public function createUserFromBackend($uid, $password, UserInterface $backend) {
309
+        $l = \OC::$server->getL10N('lib');
310
+
311
+        // Check the name for bad characters
312
+        // Allowed are: "a-z", "A-Z", "0-9" and "_.@-'"
313
+        if (preg_match('/[^a-zA-Z0-9 _\.@\-\']/', $uid)) {
314
+            throw new \InvalidArgumentException($l->t('Only the following characters are allowed in a username:'
315
+                . ' "a-z", "A-Z", "0-9", and "_.@-\'"'));
316
+        }
317
+        // No empty username
318
+        if (trim($uid) === '') {
319
+            throw new \InvalidArgumentException($l->t('A valid username must be provided'));
320
+        }
321
+        // No whitespace at the beginning or at the end
322
+        if (trim($uid) !== $uid) {
323
+            throw new \InvalidArgumentException($l->t('Username contains whitespace at the beginning or at the end'));
324
+        }
325
+        // Username only consists of 1 or 2 dots (directory traversal)
326
+        if ($uid === '.' || $uid === '..') {
327
+            throw new \InvalidArgumentException($l->t('Username must not consist of dots only'));
328
+        }
329
+        // No empty password
330
+        if (trim($password) === '') {
331
+            throw new \InvalidArgumentException($l->t('A valid password must be provided'));
332
+        }
333
+
334
+        // Check if user already exists
335
+        if ($this->userExists($uid)) {
336
+            throw new \InvalidArgumentException($l->t('The username is already being used'));
337
+        }
338
+
339
+        $this->emit('\OC\User', 'preCreateUser', [$uid, $password]);
340
+        $state = $backend->createUser($uid, $password);
341
+        if($state === false) {
342
+            throw new \InvalidArgumentException($l->t('Could not create user'));
343
+        }
344
+        $user = $this->getUserObject($uid, $backend);
345
+        if ($user instanceof IUser) {
346
+            $this->emit('\OC\User', 'postCreateUser', [$user, $password]);
347
+        }
348
+        return $user;
349
+    }
350
+
351
+    /**
352
+     * returns how many users per backend exist (if supported by backend)
353
+     *
354
+     * @param boolean $hasLoggedIn when true only users that have a lastLogin
355
+     *                entry in the preferences table will be affected
356
+     * @return array|int an array of backend class as key and count number as value
357
+     *                if $hasLoggedIn is true only an int is returned
358
+     */
359
+    public function countUsers($hasLoggedIn = false) {
360
+        if ($hasLoggedIn) {
361
+            return $this->countSeenUsers();
362
+        }
363
+        $userCountStatistics = [];
364
+        foreach ($this->backends as $backend) {
365
+            if ($backend->implementsActions(Backend::COUNT_USERS)) {
366
+                $backendUsers = $backend->countUsers();
367
+                if($backendUsers !== false) {
368
+                    if($backend instanceof IUserBackend) {
369
+                        $name = $backend->getBackendName();
370
+                    } else {
371
+                        $name = get_class($backend);
372
+                    }
373
+                    if(isset($userCountStatistics[$name])) {
374
+                        $userCountStatistics[$name] += $backendUsers;
375
+                    } else {
376
+                        $userCountStatistics[$name] = $backendUsers;
377
+                    }
378
+                }
379
+            }
380
+        }
381
+        return $userCountStatistics;
382
+    }
383
+
384
+    /**
385
+     * The callback is executed for each user on each backend.
386
+     * If the callback returns false no further users will be retrieved.
387
+     *
388
+     * @param \Closure $callback
389
+     * @param string $search
390
+     * @param boolean $onlySeen when true only users that have a lastLogin entry
391
+     *                in the preferences table will be affected
392
+     * @since 9.0.0
393
+     */
394
+    public function callForAllUsers(\Closure $callback, $search = '', $onlySeen = false) {
395
+        if ($onlySeen) {
396
+            $this->callForSeenUsers($callback);
397
+        } else {
398
+            foreach ($this->getBackends() as $backend) {
399
+                $limit = 500;
400
+                $offset = 0;
401
+                do {
402
+                    $users = $backend->getUsers($search, $limit, $offset);
403
+                    foreach ($users as $uid) {
404
+                        if (!$backend->userExists($uid)) {
405
+                            continue;
406
+                        }
407
+                        $user = $this->getUserObject($uid, $backend, false);
408
+                        $return = $callback($user);
409
+                        if ($return === false) {
410
+                            break;
411
+                        }
412
+                    }
413
+                    $offset += $limit;
414
+                } while (count($users) >= $limit);
415
+            }
416
+        }
417
+    }
418
+
419
+    /**
420
+     * returns how many users have logged in once
421
+     *
422
+     * @return int
423
+     * @since 12.0.0
424
+     */
425
+    public function countDisabledUsers() {
426
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
427
+        $queryBuilder->select($queryBuilder->createFunction('COUNT(*)'))
428
+            ->from('preferences')
429
+            ->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core')))
430
+            ->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
431
+            ->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR));
432
+
433
+        $query = $queryBuilder->execute();
434
+
435
+        $result = (int)$query->fetchColumn();
436
+        $query->closeCursor();
437
+
438
+        return $result;
439
+    }
440
+
441
+    /**
442
+     * returns how many users have logged in once
443
+     *
444
+     * @return int
445
+     * @since 11.0.0
446
+     */
447
+    public function countSeenUsers() {
448
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
449
+        $queryBuilder->select($queryBuilder->createFunction('COUNT(*)'))
450
+            ->from('preferences')
451
+            ->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('login')))
452
+            ->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('lastLogin')))
453
+            ->andWhere($queryBuilder->expr()->isNotNull('configvalue'));
454
+
455
+        $query = $queryBuilder->execute();
456
+
457
+        $result = (int)$query->fetchColumn();
458
+        $query->closeCursor();
459
+
460
+        return $result;
461
+    }
462
+
463
+    /**
464
+     * @param \Closure $callback
465
+     * @since 11.0.0
466
+     */
467
+    public function callForSeenUsers(\Closure $callback) {
468
+        $limit = 1000;
469
+        $offset = 0;
470
+        do {
471
+            $userIds = $this->getSeenUserIds($limit, $offset);
472
+            $offset += $limit;
473
+            foreach ($userIds as $userId) {
474
+                foreach ($this->backends as $backend) {
475
+                    if ($backend->userExists($userId)) {
476
+                        $user = $this->getUserObject($userId, $backend, false);
477
+                        $return = $callback($user);
478
+                        if ($return === false) {
479
+                            return;
480
+                        }
481
+                        break;
482
+                    }
483
+                }
484
+            }
485
+        } while (count($userIds) >= $limit);
486
+    }
487
+
488
+    /**
489
+     * Getting all userIds that have a listLogin value requires checking the
490
+     * value in php because on oracle you cannot use a clob in a where clause,
491
+     * preventing us from doing a not null or length(value) > 0 check.
492
+     *
493
+     * @param int $limit
494
+     * @param int $offset
495
+     * @return string[] with user ids
496
+     */
497
+    private function getSeenUserIds($limit = null, $offset = null) {
498
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
499
+        $queryBuilder->select(['userid'])
500
+            ->from('preferences')
501
+            ->where($queryBuilder->expr()->eq(
502
+                'appid', $queryBuilder->createNamedParameter('login'))
503
+            )
504
+            ->andWhere($queryBuilder->expr()->eq(
505
+                'configkey', $queryBuilder->createNamedParameter('lastLogin'))
506
+            )
507
+            ->andWhere($queryBuilder->expr()->isNotNull('configvalue')
508
+            );
509
+
510
+        if ($limit !== null) {
511
+            $queryBuilder->setMaxResults($limit);
512
+        }
513
+        if ($offset !== null) {
514
+            $queryBuilder->setFirstResult($offset);
515
+        }
516
+        $query = $queryBuilder->execute();
517
+        $result = [];
518
+
519
+        while ($row = $query->fetch()) {
520
+            $result[] = $row['userid'];
521
+        }
522
+
523
+        $query->closeCursor();
524
+
525
+        return $result;
526
+    }
527
+
528
+    /**
529
+     * @param string $email
530
+     * @return IUser[]
531
+     * @since 9.1.0
532
+     */
533
+    public function getByEmail($email) {
534
+        $userIds = $this->config->getUsersForUserValue('settings', 'email', $email);
535
+
536
+        return array_map(function($uid) {
537
+            return $this->get($uid);
538
+        }, $userIds);
539
+    }
540 540
 }
Please login to merge, or discard this patch.