|
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 Morris Jobke <[email protected]> |
|
8
|
|
|
* @author RealRancor <[email protected]> |
|
9
|
|
|
* @author Robin Appelman <[email protected]> |
|
10
|
|
|
* @author Robin McCorkell <[email protected]> |
|
11
|
|
|
* @author Volkan Gezer <[email protected]> |
|
12
|
|
|
* |
|
13
|
|
|
* @copyright Copyright (c) 2015, ownCloud, Inc. |
|
14
|
|
|
* @license AGPL-3.0 |
|
15
|
|
|
* |
|
16
|
|
|
* This code is free software: you can redistribute it and/or modify |
|
17
|
|
|
* it under the terms of the GNU Affero General Public License, version 3, |
|
18
|
|
|
* as published by the Free Software Foundation. |
|
19
|
|
|
* |
|
20
|
|
|
* This program is distributed in the hope that it will be useful, |
|
21
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
22
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
23
|
|
|
* GNU Affero General Public License for more details. |
|
24
|
|
|
* |
|
25
|
|
|
* You should have received a copy of the GNU Affero General Public License, version 3, |
|
26
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/> |
|
27
|
|
|
* |
|
28
|
|
|
*/ |
|
29
|
|
|
|
|
30
|
|
|
namespace OC\User; |
|
31
|
|
|
|
|
32
|
|
|
use OC\Hooks\PublicEmitter; |
|
33
|
|
|
use OCP\IUserManager; |
|
34
|
|
|
use OCP\IConfig; |
|
35
|
|
|
|
|
36
|
|
|
/** |
|
37
|
|
|
* Class Manager |
|
38
|
|
|
* |
|
39
|
|
|
* Hooks available in scope \OC\User: |
|
40
|
|
|
* - preSetPassword(\OC\User\User $user, string $password, string $recoverPassword) |
|
41
|
|
|
* - postSetPassword(\OC\User\User $user, string $password, string $recoverPassword) |
|
42
|
|
|
* - preDelete(\OC\User\User $user) |
|
43
|
|
|
* - postDelete(\OC\User\User $user) |
|
44
|
|
|
* - preCreateUser(string $uid, string $password) |
|
45
|
|
|
* - postCreateUser(\OC\User\User $user, string $password) |
|
46
|
|
|
* |
|
47
|
|
|
* @package OC\User |
|
48
|
|
|
*/ |
|
49
|
|
|
class Manager extends PublicEmitter implements IUserManager { |
|
50
|
|
|
/** |
|
51
|
|
|
* @var \OCP\UserInterface [] $backends |
|
52
|
|
|
*/ |
|
53
|
|
|
private $backends = array(); |
|
54
|
|
|
|
|
55
|
|
|
/** |
|
56
|
|
|
* @var \OC\User\User[] $cachedUsers |
|
57
|
|
|
*/ |
|
58
|
|
|
private $cachedUsers = array(); |
|
59
|
|
|
|
|
60
|
|
|
/** |
|
61
|
|
|
* @var \OCP\IConfig $config |
|
62
|
|
|
*/ |
|
63
|
|
|
private $config; |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* @param \OCP\IConfig $config |
|
67
|
|
|
*/ |
|
68
|
816 |
|
public function __construct(IConfig $config = null) { |
|
69
|
236 |
|
$this->config = $config; |
|
70
|
236 |
|
$cachedUsers = &$this->cachedUsers; |
|
71
|
|
|
$this->listen('\OC\User', 'postDelete', function ($user) use (&$cachedUsers) { |
|
72
|
|
|
/** @var \OC\User\User $user */ |
|
73
|
581 |
|
unset($cachedUsers[$user->getUID()]); |
|
74
|
816 |
|
}); |
|
75
|
|
|
$this->listen('\OC\User', 'postLogin', function ($user) { |
|
76
|
|
|
/** @var \OC\User\User $user */ |
|
77
|
263 |
|
$user->updateLastLoginTimestamp(); |
|
78
|
498 |
|
}); |
|
79
|
|
|
$this->listen('\OC\User', 'postRememberedLogin', function ($user) { |
|
80
|
|
|
/** @var \OC\User\User $user */ |
|
81
|
1 |
|
$user->updateLastLoginTimestamp(); |
|
82
|
236 |
|
}); |
|
83
|
236 |
|
} |
|
84
|
|
|
|
|
85
|
|
|
/** |
|
86
|
|
|
* Get the active backends |
|
87
|
|
|
* @return \OCP\UserInterface[] |
|
88
|
|
|
*/ |
|
89
|
1 |
|
public function getBackends() { |
|
90
|
1 |
|
return $this->backends; |
|
|
|
|
|
|
91
|
|
|
} |
|
92
|
|
|
|
|
93
|
|
|
/** |
|
94
|
|
|
* register a user backend |
|
95
|
|
|
* |
|
96
|
|
|
* @param \OCP\UserInterface $backend |
|
97
|
|
|
*/ |
|
98
|
744 |
|
public function registerBackend($backend) { |
|
99
|
744 |
|
$this->backends[] = $backend; |
|
100
|
744 |
|
} |
|
101
|
|
|
|
|
102
|
|
|
/** |
|
103
|
|
|
* remove a user backend |
|
104
|
|
|
* |
|
105
|
|
|
* @param \OCP\UserInterface $backend |
|
106
|
|
|
*/ |
|
107
|
211 |
|
public function removeBackend($backend) { |
|
108
|
211 |
|
$this->cachedUsers = array(); |
|
109
|
211 |
|
if (($i = array_search($backend, $this->backends)) !== false) { |
|
110
|
211 |
|
unset($this->backends[$i]); |
|
111
|
211 |
|
} |
|
112
|
211 |
|
} |
|
113
|
|
|
|
|
114
|
|
|
/** |
|
115
|
|
|
* remove all user backends |
|
116
|
|
|
*/ |
|
117
|
435 |
|
public function clearBackends() { |
|
118
|
435 |
|
$this->cachedUsers = array(); |
|
119
|
435 |
|
$this->backends = array(); |
|
|
|
|
|
|
120
|
435 |
|
} |
|
121
|
|
|
|
|
122
|
|
|
/** |
|
123
|
|
|
* get a user by user id |
|
124
|
|
|
* |
|
125
|
|
|
* @param string $uid |
|
126
|
|
|
* @return \OC\User\User|null Either the user or null if the specified user does not exist |
|
127
|
|
|
*/ |
|
128
|
1281 |
|
public function get($uid) { |
|
129
|
1281 |
|
if (isset($this->cachedUsers[$uid])) { //check the cache first to prevent having to loop over the backends |
|
130
|
1115 |
|
return $this->cachedUsers[$uid]; |
|
131
|
|
|
} |
|
132
|
1093 |
|
foreach ($this->backends as $backend) { |
|
|
|
|
|
|
133
|
1086 |
|
if ($backend->userExists($uid)) { |
|
134
|
259 |
|
return $this->getUserObject($uid, $backend); |
|
135
|
|
|
} |
|
136
|
1069 |
|
} |
|
137
|
1048 |
|
return null; |
|
138
|
|
|
} |
|
139
|
|
|
|
|
140
|
|
|
/** |
|
141
|
|
|
* get or construct the user object |
|
142
|
|
|
* |
|
143
|
|
|
* @param string $uid |
|
144
|
|
|
* @param \OCP\UserInterface $backend |
|
145
|
|
|
* @return \OC\User\User |
|
146
|
|
|
*/ |
|
147
|
1105 |
|
protected function getUserObject($uid, $backend) { |
|
148
|
1105 |
|
if (isset($this->cachedUsers[$uid])) { |
|
149
|
263 |
|
return $this->cachedUsers[$uid]; |
|
150
|
|
|
} |
|
151
|
883 |
|
$this->cachedUsers[$uid] = new User($uid, $backend, $this, $this->config); |
|
152
|
883 |
|
return $this->cachedUsers[$uid]; |
|
153
|
|
|
} |
|
154
|
|
|
|
|
155
|
|
|
/** |
|
156
|
|
|
* check if a user exists |
|
157
|
|
|
* |
|
158
|
|
|
* @param string $uid |
|
159
|
|
|
* @return bool |
|
160
|
|
|
*/ |
|
161
|
1245 |
|
public function userExists($uid) { |
|
162
|
1245 |
|
$user = $this->get($uid); |
|
163
|
1244 |
|
return ($user !== null); |
|
164
|
|
|
} |
|
165
|
|
|
|
|
166
|
|
|
/** |
|
167
|
|
|
* Check if the password is valid for the user |
|
168
|
|
|
* |
|
169
|
|
|
* @param string $loginname |
|
170
|
|
|
* @param string $password |
|
171
|
|
|
* @return mixed the User object on success, false otherwise |
|
172
|
|
|
*/ |
|
173
|
269 |
|
public function checkPassword($loginname, $password) { |
|
174
|
269 |
|
$loginname = str_replace("\0", '', $loginname); |
|
175
|
269 |
|
$password = str_replace("\0", '', $password); |
|
176
|
|
|
|
|
177
|
269 |
|
foreach ($this->backends as $backend) { |
|
|
|
|
|
|
178
|
269 |
|
if ($backend->implementsActions(\OC_User_Backend::CHECK_PASSWORD)) { |
|
179
|
268 |
|
$uid = $backend->checkPassword($loginname, $password); |
|
180
|
268 |
|
if ($uid !== false) { |
|
181
|
266 |
|
return $this->getUserObject($uid, $backend); |
|
182
|
|
|
} |
|
183
|
16 |
|
} |
|
184
|
17 |
|
} |
|
185
|
|
|
|
|
186
|
3 |
|
\OC::$server->getLogger()->warning('Login failed: \''. $loginname .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). '\')', ['app' => 'core']); |
|
187
|
3 |
|
return false; |
|
188
|
|
|
} |
|
189
|
|
|
|
|
190
|
|
|
/** |
|
191
|
|
|
* search by user id |
|
192
|
|
|
* |
|
193
|
|
|
* @param string $pattern |
|
194
|
|
|
* @param int $limit |
|
195
|
|
|
* @param int $offset |
|
196
|
|
|
* @return \OC\User\User[] |
|
197
|
|
|
*/ |
|
198
|
21 |
View Code Duplication |
public function search($pattern, $limit = null, $offset = null) { |
|
199
|
21 |
|
$users = array(); |
|
200
|
21 |
|
foreach ($this->backends as $backend) { |
|
|
|
|
|
|
201
|
21 |
|
$backendUsers = $backend->getUsers($pattern, $limit, $offset); |
|
202
|
21 |
|
if (is_array($backendUsers)) { |
|
203
|
21 |
|
foreach ($backendUsers as $uid) { |
|
204
|
20 |
|
$users[$uid] = $this->getUserObject($uid, $backend); |
|
205
|
21 |
|
} |
|
206
|
21 |
|
} |
|
207
|
21 |
|
} |
|
208
|
|
|
|
|
209
|
|
|
uasort($users, function ($a, $b) { |
|
210
|
|
|
/** |
|
211
|
|
|
* @var \OC\User\User $a |
|
212
|
|
|
* @var \OC\User\User $b |
|
213
|
|
|
*/ |
|
214
|
19 |
|
return strcmp($a->getUID(), $b->getUID()); |
|
215
|
21 |
|
}); |
|
216
|
21 |
|
return $users; |
|
217
|
|
|
} |
|
218
|
|
|
|
|
219
|
|
|
/** |
|
220
|
|
|
* search by displayName |
|
221
|
|
|
* |
|
222
|
|
|
* @param string $pattern |
|
223
|
|
|
* @param int $limit |
|
224
|
|
|
* @param int $offset |
|
225
|
|
|
* @return \OC\User\User[] |
|
226
|
|
|
*/ |
|
227
|
|
View Code Duplication |
public function searchDisplayName($pattern, $limit = null, $offset = null) { |
|
228
|
|
|
$users = array(); |
|
229
|
|
|
foreach ($this->backends as $backend) { |
|
|
|
|
|
|
230
|
|
|
$backendUsers = $backend->getDisplayNames($pattern, $limit, $offset); |
|
231
|
|
|
if (is_array($backendUsers)) { |
|
232
|
|
|
foreach ($backendUsers as $uid => $displayName) { |
|
233
|
|
|
$users[] = $this->getUserObject($uid, $backend); |
|
234
|
|
|
} |
|
235
|
|
|
} |
|
236
|
|
|
} |
|
237
|
|
|
|
|
238
|
|
|
usort($users, function ($a, $b) { |
|
239
|
|
|
/** |
|
240
|
|
|
* @var \OC\User\User $a |
|
241
|
|
|
* @var \OC\User\User $b |
|
242
|
|
|
*/ |
|
243
|
|
|
return strcmp($a->getDisplayName(), $b->getDisplayName()); |
|
244
|
|
|
}); |
|
245
|
|
|
return $users; |
|
246
|
|
|
} |
|
247
|
|
|
|
|
248
|
|
|
/** |
|
249
|
|
|
* @param string $uid |
|
250
|
|
|
* @param string $password |
|
251
|
|
|
* @throws \Exception |
|
252
|
|
|
* @return bool|\OC\User\User the created user or false |
|
253
|
|
|
*/ |
|
254
|
613 |
|
public function createUser($uid, $password) { |
|
255
|
613 |
|
$l = \OC::$server->getL10N('lib'); |
|
256
|
|
|
// Check the name for bad characters |
|
257
|
|
|
// Allowed are: "a-z", "A-Z", "0-9" and "_.@-" |
|
258
|
613 |
|
if (preg_match('/[^a-zA-Z0-9 _\.@\-]/', $uid)) { |
|
259
|
|
|
throw new \Exception($l->t('Only the following characters are allowed in a username:' |
|
260
|
|
|
. ' "a-z", "A-Z", "0-9", and "_.@-"')); |
|
261
|
|
|
} |
|
262
|
|
|
// No empty username |
|
263
|
613 |
|
if (trim($uid) == '') { |
|
264
|
|
|
throw new \Exception($l->t('A valid username must be provided')); |
|
265
|
|
|
} |
|
266
|
|
|
// No empty password |
|
267
|
613 |
|
if (trim($password) == '') { |
|
268
|
|
|
throw new \Exception($l->t('A valid password must be provided')); |
|
269
|
|
|
} |
|
270
|
|
|
|
|
271
|
|
|
// Check if user already exists |
|
272
|
613 |
|
if ($this->userExists($uid)) { |
|
273
|
2 |
|
throw new \Exception($l->t('The username is already being used')); |
|
274
|
|
|
} |
|
275
|
|
|
|
|
276
|
611 |
|
$this->emit('\OC\User', 'preCreateUser', array($uid, $password)); |
|
277
|
611 |
|
foreach ($this->backends as $backend) { |
|
|
|
|
|
|
278
|
610 |
|
if ($backend->implementsActions(\OC_User_Backend::CREATE_USER)) { |
|
279
|
609 |
|
$backend->createUser($uid, $password); |
|
280
|
609 |
|
$user = $this->getUserObject($uid, $backend); |
|
281
|
609 |
|
$this->emit('\OC\User', 'postCreateUser', array($user, $password)); |
|
282
|
609 |
|
return $user; |
|
283
|
|
|
} |
|
284
|
2 |
|
} |
|
285
|
2 |
|
return false; |
|
286
|
|
|
} |
|
287
|
|
|
|
|
288
|
|
|
/** |
|
289
|
|
|
* returns how many users per backend exist (if supported by backend) |
|
290
|
|
|
* |
|
291
|
|
|
* @return array an array of backend class as key and count number as value |
|
292
|
|
|
*/ |
|
293
|
3 |
|
public function countUsers() { |
|
294
|
3 |
|
$userCountStatistics = array(); |
|
295
|
3 |
|
foreach ($this->backends as $backend) { |
|
|
|
|
|
|
296
|
2 |
|
if ($backend->implementsActions(\OC_User_Backend::COUNT_USERS)) { |
|
297
|
2 |
|
$backendusers = $backend->countUsers(); |
|
298
|
2 |
|
if($backendusers !== false) { |
|
299
|
2 |
|
if($backend instanceof \OCP\IUserBackend) { |
|
300
|
2 |
|
$name = $backend->getBackendName(); |
|
301
|
2 |
|
} else { |
|
302
|
|
|
$name = get_class($backend); |
|
303
|
|
|
} |
|
304
|
2 |
|
if(isset($userCountStatistics[$name])) { |
|
305
|
1 |
|
$userCountStatistics[$name] += $backendusers; |
|
306
|
1 |
|
} else { |
|
307
|
2 |
|
$userCountStatistics[$name] = $backendusers; |
|
308
|
|
|
} |
|
309
|
2 |
|
} |
|
310
|
2 |
|
} |
|
311
|
3 |
|
} |
|
312
|
3 |
|
return $userCountStatistics; |
|
313
|
|
|
} |
|
314
|
|
|
} |
|
315
|
|
|
|
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:
Our function
my_functionexpects aPostobject, and outputs the author of the post. The base classPostreturns a simple string and outputting a simple string will work just fine. However, the child classBlogPostwhich is a sub-type ofPostinstead decided to return anobject, and is therefore violating the SOLID principles. If aBlogPostwere passed tomy_function, PHP would not complain, but ultimately fail when executing thestrtouppercall in its body.