Completed
Push — master ( 994b8c...0f8bf0 )
by Thomas
13:59
created

Manager::clearBackends()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Joas Schilling <[email protected]>
5
 * @author Jörn Friedrich Dreyer <[email protected]>
6
 * @author Lukas Reschke <[email protected]>
7
 * @author Michael U <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Robin Appelman <[email protected]>
10
 * @author Robin McCorkell <[email protected]>
11
 * @author Roeland Jago Douma <[email protected]>
12
 * @author Thomas Müller <[email protected]>
13
 * @author Vincent Chan <[email protected]>
14
 * @author Volkan Gezer <[email protected]>
15
 *
16
 * @copyright Copyright (c) 2016, ownCloud GmbH.
17
 * @license AGPL-3.0
18
 *
19
 * This code is free software: you can redistribute it and/or modify
20
 * it under the terms of the GNU Affero General Public License, version 3,
21
 * as published by the Free Software Foundation.
22
 *
23
 * This program is distributed in the hope that it will be useful,
24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
 * GNU Affero General Public License for more details.
27
 *
28
 * You should have received a copy of the GNU Affero General Public License, version 3,
29
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
30
 *
31
 */
32
33
namespace OC\User;
34
35
use OC\Hooks\PublicEmitter;
36
use OCP\IUser;
37
use OCP\IUserBackend;
38
use OCP\IUserManager;
39
use OCP\IConfig;
40
41
/**
42
 * Class Manager
43
 *
44
 * Hooks available in scope \OC\User:
45
 * - preSetPassword(\OC\User\User $user, string $password, string $recoverPassword)
46
 * - postSetPassword(\OC\User\User $user, string $password, string $recoverPassword)
47
 * - preDelete(\OC\User\User $user)
48
 * - postDelete(\OC\User\User $user)
49
 * - preCreateUser(string $uid, string $password)
50
 * - postCreateUser(\OC\User\User $user, string $password)
51
 * - change(\OC\User\User $user)
52
 *
53
 * @package OC\User
54
 */
55
class Manager extends PublicEmitter implements IUserManager {
56
	/**
57
	 * @var \OCP\UserInterface[] $backends
58
	 */
59
	private $backends = [];
60
61
	/**
62
	 * @var \OC\User\User[] $cachedUsers
63
	 */
64
	private $cachedUsers = [];
65
66
	/**
67
	 * @var \OCP\IConfig $config
68
	 */
69
	private $config;
70
71
	/**
72
	 * @param \OCP\IConfig $config
73
	 */
74
	public function __construct(IConfig $config = null) {
75
		$this->config = $config;
76
		$cachedUsers = &$this->cachedUsers;
77
		$this->listen('\OC\User', 'postDelete', function ($user) use (&$cachedUsers) {
78
			/** @var \OC\User\User $user */
79
			unset($cachedUsers[$user->getUID()]);
80
		});
81
	}
82
83
	/**
84
	 * Get the active backends
85
	 * @return \OCP\UserInterface[]
86
	 */
87
	public function getBackends() {
88
		return $this->backends;
89
	}
90
91
	/**
92
	 * register a user backend
93
	 *
94
	 * @param \OCP\UserInterface $backend
95
	 */
96
	public function registerBackend($backend) {
97
		$this->backends[] = $backend;
98
	}
99
100
	/**
101
	 * remove a user backend
102
	 *
103
	 * @param \OCP\UserInterface $backend
104
	 */
105
	public function removeBackend($backend) {
106
		$this->cachedUsers = [];
107
		if (($i = array_search($backend, $this->backends)) !== false) {
108
			unset($this->backends[$i]);
109
		}
110
	}
111
112
	/**
113
	 * remove all user backends
114
	 */
115
	public function clearBackends() {
116
		$this->cachedUsers = [];
117
		$this->backends = [];
118
	}
119
120
	/**
121
	 * get a user by user id
122
	 *
123
	 * @param string $uid
124
	 * @return \OC\User\User|null Either the user or null if the specified user does not exist
125
	 */
126
	public function get($uid) {
127
		if (isset($this->cachedUsers[$uid])) { //check the cache first to prevent having to loop over the backends
128
			return $this->cachedUsers[$uid];
129
		}
130
		foreach ($this->backends as $backend) {
131
			if ($backend->userExists($uid)) {
132
				return $this->getUserObject($uid, $backend);
133
			}
134
		}
135
		return null;
136
	}
137
138
	/**
139
	 * get or construct the user object
140
	 *
141
	 * @param string $uid
142
	 * @param \OCP\UserInterface $backend
143
	 * @param bool $cacheUser If false the newly created user object will not be cached
144
	 * @return \OC\User\User
145
	 */
146
	protected function getUserObject($uid, $backend, $cacheUser = true) {
147
		if (isset($this->cachedUsers[$uid])) {
148
			return $this->cachedUsers[$uid];
149
		}
150
151
		if (method_exists($backend, 'loginName2UserName')) {
152
			$loginName = $backend->loginName2UserName($uid);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface OCP\UserInterface as the method loginName2UserName() does only exist in the following implementations of said interface: OC\User\Database.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
153
			if ($loginName !== false) {
154
				$uid = $loginName;
155
			}
156
			if (isset($this->cachedUsers[$uid])) {
157
				return $this->cachedUsers[$uid];
158
			}
159
		}
160
161
		$user = new User($uid, $backend, $this, $this->config);
162
		if ($cacheUser) {
163
			$this->cachedUsers[$uid] = $user;
164
		}
165
		return $user;
166
	}
167
168
	/**
169
	 * check if a user exists
170
	 *
171
	 * @param string $uid
172
	 * @return bool
173
	 */
174
	public function userExists($uid) {
175
		$user = $this->get($uid);
176
		return ($user !== null);
177
	}
178
179
	/**
180
	 * Check if the password is valid for the user
181
	 *
182
	 * @param string $loginName
183
	 * @param string $password
184
	 * @return mixed the User object on success, false otherwise
185
	 */
186
	public function checkPassword($loginName, $password) {
187
		$loginName = str_replace("\0", '', $loginName);
188
		$password = str_replace("\0", '', $password);
189
		
190
		foreach ($this->backends as $backend) {
191
			if ($backend->implementsActions(Backend::CHECK_PASSWORD)) {
192
				$uid = $backend->checkPassword($loginName, $password);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface OCP\UserInterface as the method checkPassword() does only exist in the following implementations of said interface: OC\User\Database.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
193
				if ($uid !== false) {
194
					return $this->getUserObject($uid, $backend);
195
				}
196
			}
197
		}
198
199
		\OC::$server->getLogger()->warning('Login failed: \''. $loginName .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). '\')', ['app' => 'core']);
200
		return false;
201
	}
202
203
	/**
204
	 * search by user id
205
	 *
206
	 * @param string $pattern
207
	 * @param int $limit
208
	 * @param int $offset
209
	 * @return \OC\User\User[]
210
	 */
211 View Code Duplication
	public function search($pattern, $limit = null, $offset = null) {
212
		$users = [];
213
		foreach ($this->backends as $backend) {
214
			$backendUsers = $backend->getUsers($pattern, $limit, $offset);
215
			if (is_array($backendUsers)) {
216
				foreach ($backendUsers as $uid) {
217
					$users[$uid] = $this->getUserObject($uid, $backend);
218
				}
219
			}
220
		}
221
222
		uasort($users, function ($a, $b) {
223
			/**
224
			 * @var \OC\User\User $a
225
			 * @var \OC\User\User $b
226
			 */
227
			return strcmp($a->getUID(), $b->getUID());
228
		});
229
		return $users;
230
	}
231
232
	/**
233
	 * search by displayName
234
	 *
235
	 * @param string $pattern
236
	 * @param int $limit
237
	 * @param int $offset
238
	 * @return \OC\User\User[]
239
	 */
240 View Code Duplication
	public function searchDisplayName($pattern, $limit = null, $offset = null) {
241
		$users = [];
242
		foreach ($this->backends as $backend) {
243
			$backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
244
			if (is_array($backendUsers)) {
245
				foreach ($backendUsers as $uid => $displayName) {
246
					$users[] = $this->getUserObject($uid, $backend);
247
				}
248
			}
249
		}
250
251
		usort($users, function ($a, $b) {
252
			/**
253
			 * @var \OC\User\User $a
254
			 * @var \OC\User\User $b
255
			 */
256
			return strcmp($a->getDisplayName(), $b->getDisplayName());
257
		});
258
		return $users;
259
	}
260
261
	/**
262
	 * @param string $uid
263
	 * @param string $password
264
	 * @throws \Exception
265
	 * @return bool|\OC\User\User the created user or false
266
	 */
267
	public function createUser($uid, $password) {
268
		$l = \OC::$server->getL10N('lib');
269
		// Check the name for bad characters
270
		// Allowed are: "a-z", "A-Z", "0-9" and "_.@-'"
271
		if (preg_match('/[^a-zA-Z0-9 _\.@\-\']/', $uid)) {
272
			throw new \Exception($l->t('Only the following characters are allowed in a username:'
273
				. ' "a-z", "A-Z", "0-9", and "_.@-\'"'));
274
		}
275
		// No empty username
276
		if (trim($uid) == '') {
277
			throw new \Exception($l->t('A valid username must be provided'));
278
		}
279
		// No whitespace at the beginning or at the end
280
		if (strlen(trim($uid, "\t\n\r\0\x0B\xe2\x80\x8b")) !== strlen(trim($uid))) {
281
			throw new \Exception($l->t('Username contains whitespace at the beginning or at the end'));
282
		}
283
		// No empty password
284
		if (trim($password) == '') {
285
			throw new \Exception($l->t('A valid password must be provided'));
286
		}
287
288
		// Check if user already exists
289
		if ($this->userExists($uid)) {
290
			throw new \Exception($l->t('The username is already being used'));
291
		}
292
293
		$this->emit('\OC\User', 'preCreateUser', [$uid, $password]);
294
		foreach ($this->backends as $backend) {
295
			if ($backend->implementsActions(Backend::CREATE_USER)) {
296
				$backend->createUser($uid, $password);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface OCP\UserInterface as the method createUser() does only exist in the following implementations of said interface: OC\User\Database.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
297
				$user = $this->getUserObject($uid, $backend);
298
				$this->emit('\OC\User', 'postCreateUser', [$user, $password]);
299
				return $user;
300
			}
301
		}
302
		return false;
303
	}
304
305
	/**
306
	 * returns how many users per backend exist (if supported by backend)
307
	 *
308
	 * @param boolean $hasLoggedIn when true only users that have a lastLogin
309
	 *                entry in the preferences table will be affected
310
	 * @return array|int an array of backend class as key and count number as value
311
	 *                if $hasLoggedIn is true only an int is returned
312
	 */
313
	public function countUsers($hasLoggedIn = false) {
314
		if ($hasLoggedIn) {
315
			return $this->countSeenUsers();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->countSeenUsers(); (integer) is incompatible with the return type declared by the interface OCP\IUserManager::countUsers of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
316
		}
317
		$userCountStatistics = [];
318
		foreach ($this->backends as $backend) {
319
			if ($backend->implementsActions(Backend::COUNT_USERS)) {
320
				$backendUsers = $backend->countUsers();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface OCP\UserInterface as the method countUsers() does only exist in the following implementations of said interface: OC\User\Database.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
321
				if($backendUsers !== false) {
322
					if($backend instanceof IUserBackend) {
323
						$name = $backend->getBackendName();
324
					} else {
325
						$name = get_class($backend);
326
					}
327
					if(isset($userCountStatistics[$name])) {
328
						$userCountStatistics[$name] += $backendUsers;
329
					} else {
330
						$userCountStatistics[$name] = $backendUsers;
331
					}
332
				}
333
			}
334
		}
335
		return $userCountStatistics;
336
	}
337
338
	/**
339
	 * The callback is executed for each user on each backend.
340
	 * If the callback returns false no further users will be retrieved.
341
	 *
342
	 * @param \Closure $callback
343
	 * @param string $search
344
	 * @param boolean $onlySeen when true only users that have a lastLogin entry
345
	 *                in the preferences table will be affected
346
	 * @since 9.0.0
347
	 */
348
	public function callForAllUsers(\Closure $callback, $search = '', $onlySeen = false) {
349
		if ($onlySeen) {
350
			$this->callForSeenUsers($callback);
351
		} else {
352
			foreach ($this->getBackends() as $backend) {
353
				$limit = 500;
354
				$offset = 0;
355
				do {
356
					$users = $backend->getUsers($search, $limit, $offset);
357 View Code Duplication
					foreach ($users as $uid) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
358
						if (!$backend->userExists($uid)) {
359
							continue;
360
						}
361
						$user = $this->getUserObject($uid, $backend, false);
362
						$return = $callback($user);
363
						if ($return === false) {
364
							break;
365
						}
366
					}
367
					$offset += $limit;
368
				} while (count($users) >= $limit);
369
			}
370
		}
371
	}
372
373
	/**
374
	 * returns how many users have logged in once
375
	 *
376
	 * @return int
377
	 * @since 9.2.0
378
	 */
379
	public function countSeenUsers() {
380
		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
381
		$queryBuilder->select($queryBuilder->createFunction('COUNT(*)'))
382
			->from('preferences')
383
			->where($queryBuilder->expr()->eq(
384
				'appid', $queryBuilder->createNamedParameter('login'))
385
			)
386
			->andWhere($queryBuilder->expr()->eq(
387
				'configkey', $queryBuilder->createNamedParameter('lastLogin'))
388
			)
389
			->andWhere($queryBuilder->expr()->isNotNull('configvalue')
390
			);
391
392
		$query = $queryBuilder->execute();
393
		return (int)$query->fetchColumn();
394
	}
395
396
	/**
397
	 * @param \Closure $callback
398
	 * @param string $search
0 ignored issues
show
Bug introduced by
There is no parameter named $search. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
399
	 * @since 9.2.0
400
	 */
401
	public function callForSeenUsers (\Closure $callback) {
402
		$limit = 1000;
403
		$offset = 0;
404
		do {
405
			$userIds = $this->getSeenUserIds($limit, $offset);
406
			$offset += $limit;
407 View Code Duplication
			foreach ($userIds as $userId) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
408
				foreach ($this->backends as $backend) {
409
					if ($backend->userExists($userId)) {
410
						$user = $this->getUserObject($userId, $backend, false);
411
						$return = $callback($user);
412
						if ($return === false) {
413
							return;
414
						}
415
					}
416
				}
417
			}
418
		} while (count($userIds) >= $limit);
419
	}
420
421
	/**
422
	 * Getting all userIds that have a listLogin value requires checking the
423
	 * value in php because on oracle you cannot use a clob in a where clause,
424
	 * preventing us from doing a not null or length(value) > 0 check.
425
	 * 
426
	 * @param int $limit
427
	 * @param int $offset
428
	 * @return string[] with user ids
429
	 */
430
	private function getSeenUserIds($limit = null, $offset = null) {
431
		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
432
		$queryBuilder->select(['userid'])
433
			->from('preferences')
434
			->where($queryBuilder->expr()->eq(
435
				'appid', $queryBuilder->createNamedParameter('login'))
436
			)
437
			->andWhere($queryBuilder->expr()->eq(
438
				'configkey', $queryBuilder->createNamedParameter('lastLogin'))
439
			)
440
			->andWhere($queryBuilder->expr()->isNotNull('configvalue')
441
			);
442
443
		if ($limit !== null) {
444
			$queryBuilder->setMaxResults($limit);
445
		}
446
		if ($offset !== null) {
447
			$queryBuilder->setFirstResult($offset);
448
		}
449
		$query = $queryBuilder->execute();
450
		$result = [];
451
452
		while ($row = $query->fetch()) {
453
			$result[] = $row['userid'];
454
		}
455
456
		return $result;
457
	}
458
	/**
459
	 * @param string $email
460
	 * @return IUser[]
461
	 * @since 9.1.0
462
	 */
463
	public function getByEmail($email) {
464
		$userIds = $this->config->getUsersForUserValue('settings', 'email', $email);
465
466
		return array_map(function($uid) {
467
			return $this->get($uid);
468
		}, $userIds);
469
	}
470
}
471