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