Completed
Push — stable8.2 ( 36aa4e...013031 )
by
unknown
12:55
created

OC_User_Database::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
rs 10
1
<?php
2
/**
3
 * @author adrien <[email protected]>
4
 * @author Aldo "xoen" Giambelluca <[email protected]>
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Bart Visscher <[email protected]>
7
 * @author Björn Schießle <[email protected]>
8
 * @author fabian <[email protected]>
9
 * @author Georg Ehrke <[email protected]>
10
 * @author Jakob Sack <[email protected]>
11
 * @author Joas Schilling <[email protected]>
12
 * @author Jörn Friedrich Dreyer <[email protected]>
13
 * @author Lukas Reschke <[email protected]>
14
 * @author Michael Gapczynski <[email protected]>
15
 * @author Morris Jobke <[email protected]>
16
 * @author nishiki <[email protected]>
17
 * @author Robin Appelman <[email protected]>
18
 * @author Robin McCorkell <[email protected]>
19
 * @author Thomas Müller <[email protected]>
20
 * @author Victor Dubiniuk <[email protected]>
21
 *
22
 * @copyright Copyright (c) 2015, ownCloud, Inc.
23
 * @license AGPL-3.0
24
 *
25
 * This code is free software: you can redistribute it and/or modify
26
 * it under the terms of the GNU Affero General Public License, version 3,
27
 * as published by the Free Software Foundation.
28
 *
29
 * This program is distributed in the hope that it will be useful,
30
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
 * GNU Affero General Public License for more details.
33
 *
34
 * You should have received a copy of the GNU Affero General Public License, version 3,
35
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
36
 *
37
 */
38
/*
39
 *
40
 * The following SQL statement is just a help for developers and will not be
41
 * executed!
42
 *
43
 * CREATE TABLE `users` (
44
 *   `uid` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
45
 *   `password` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
46
 *   PRIMARY KEY (`uid`)
47
 * ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
48
 *
49
 */
50
51
use OC\Cache\CappedMemoryCache;
52
53
/**
54
 * Class for user management in a SQL Database (e.g. MySQL, SQLite)
55
 */
56
class OC_User_Database extends OC_User_Backend implements \OCP\IUserBackend {
57
	/** @var CappedMemoryCache */
58
	private $cache;
59
60
	/**
61
	 * OC_User_Database constructor.
62
	 */
63
	public function __construct() {
64
		$this->cache = new CappedMemoryCache();
65
	}
66 103
67 103
	/**
68 103
	 * Create a new user
69 103
	 * @param string $uid The username of the user to create
70
	 * @param string $password The password of the new user
71 103
	 * @return bool
72
	 *
73
	 * Creates a new user. Basic checking of username is done in OC_User
74
	 * itself, not in its subclasses.
75
	 */
76 View Code Duplication
	public function createUser($uid, $password) {
77
		if (!$this->userExists($uid)) {
78
			$query = OC_DB::prepare('INSERT INTO `*PREFIX*users` ( `uid`, `password` ) VALUES( ?, ? )');
79
			$result = $query->execute(array($uid, \OC::$server->getHasher()->hash($password)));
80
81
			return $result ? true : false;
82
		}
83
84 101
		return false;
85
	}
86 101
87 101
	/**
88
	 * delete a user
89 101
	 * @param string $uid The username of the user to delete
90 73
	 * @return bool
91 73
	 *
92
	 * Deletes a user
93 101
	 */
94 View Code Duplication
	public function deleteUser($uid) {
95
		// Delete user-group-relation
96
		$query = OC_DB::prepare('DELETE FROM `*PREFIX*users` WHERE `uid` = ?');
97
		$result = $query->execute(array($uid));
98
99
		if (isset($this->cache[$uid])) {
100
			unset($this->cache[$uid]);
101
		}
102
103
		return $result ? true : false;
104 3
	}
105 3
106 3
	/**
107 3
	 * Set password
108
	 * @param string $uid The username
109 3
	 * @param string $password The new password
110
	 * @return bool
111
	 *
112
	 * Change the password of a user
113
	 */
114 View Code Duplication
	public function setPassword($uid, $password) {
115
		if ($this->userExists($uid)) {
116
			$query = OC_DB::prepare('UPDATE `*PREFIX*users` SET `password` = ? WHERE `uid` = ?');
117
			$result = $query->execute(array(\OC::$server->getHasher()->hash($password), $uid));
118
119
			return $result ? true : false;
120
		}
121
122
		return false;
123 3
	}
124 3
125 3
	/**
126 3
	 * Set display name
127 3
	 * @param string $uid The username
128
	 * @param string $displayName The new display name
129 3
	 * @return bool
130
	 *
131
	 * Change the display name of a user
132
	 */
133 View Code Duplication
	public function setDisplayName($uid, $displayName) {
134
		if ($this->userExists($uid)) {
135
			$query = OC_DB::prepare('UPDATE `*PREFIX*users` SET `displayname` = ? WHERE LOWER(`uid`) = LOWER(?)');
136
			$query->execute(array($displayName, $uid));
137
			$this->cache[$uid]['displayname'] = $displayName;
138
139
			return true;
140 5
		}
141 5
142 5
		return false;
143
	}
144
145
	/**
146
	 * get display name of the user
147
	 * @param string $uid user ID of the user
148
	 * @return string display name
149
	 */
150
	public function getDisplayName($uid) {
151
		$this->loadUser($uid);
152
		return empty($this->cache[$uid]['displayname']) ? $uid : $this->cache[$uid]['displayname'];
153 1
	}
154 1
155 1
	/**
156 1
	 * Get a list of all display names and user ids.
157 1
	 *
158 1
	 * @param string $search
159
	 * @param string|null $limit
160 1
	 * @param string|null $offset
161 1
	 * @return array an array of all displayNames (value) and the corresponding uids (key)
162
	 */
163 1
	public function getDisplayNames($search = '', $limit = null, $offset = null) {
164 1
		$parameters = [];
165 1
		$searchLike = '';
166 1
		if ($search !== '') {
167 1
			$parameters[] = '%' . $search . '%';
168 1
			$parameters[] = '%' . $search . '%';
169 1
			$searchLike = ' WHERE LOWER(`displayname`) LIKE LOWER(?) OR '
170
				. 'LOWER(`uid`) LIKE LOWER(?)';
171 1
		}
172
173
		$displayNames = array();
174
		$query = OC_DB::prepare('SELECT `uid`, `displayname` FROM `*PREFIX*users`'
175
			. $searchLike .' ORDER BY `uid` ASC', $limit, $offset);
176
		$result = $query->execute($parameters);
177
		while ($row = $result->fetchRow()) {
178
			$displayNames[$row['uid']] = $row['displayname'];
179
		}
180
181
		return $displayNames;
182
	}
183 3
184 3
	/**
185 3
	 * Check if the password is correct
186
	 * @param string $uid The username
187 3
	 * @param string $password The password
188 3
	 * @return string
189 2
	 *
190 2
	 * Check if the password is correct without logging in the user
191 2
	 * returns the user id or false
192 2
	 */
193
	public function checkPassword($uid, $password) {
194
		$query = OC_DB::prepare('SELECT `uid`, `password` FROM `*PREFIX*users` WHERE LOWER(`uid`) = LOWER(?)');
195 2
		$result = $query->execute(array($uid));
196
197
		$row = $result->fetchRow();
198 1
		if ($row) {
199
			$storedHash = $row['password'];
200 2
			$newHash = '';
201
			if(\OC::$server->getHasher()->verify($password, $storedHash, $newHash)) {
202
				if(!empty($newHash)) {
203
					$this->setPassword($uid, $password);
204
				}
205
				return $row['uid'];
206
			}
207
208 142
		}
209 142
210 142
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by OC_User_Database::checkPassword of type string.

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...
211 142
	}
212
213 142
	/**
214
	 * Load an user in the cache
215
	 * @param string $uid the username
216
	 * @return boolean
217
	 */
218 142
	private function loadUser($uid) {
219 73
		if (empty($this->cache[$uid])) {
220 73
			$query = OC_DB::prepare('SELECT `uid`, `displayname` FROM `*PREFIX*users` WHERE LOWER(`uid`) = LOWER(?)');
221 73
			$result = $query->execute(array($uid));
222 142
223
			if (OC_DB::isError($result)) {
224 142
				\OCP\Util::writeLog('core', OC_DB::getErrorMessage(), \OCP\Util::ERROR);
225
				return false;
226
			}
227
228
			while ($row = $result->fetchRow()) {
229
				$this->cache[$uid]['uid'] = $row['uid'];
230
				$this->cache[$uid]['displayname'] = $row['displayname'];
231
			}
232
		}
233
234
		return true;
235 3
	}
236 3
237 3
	/**
238 3
	 * Get a list of all users
239 2
	 *
240 2
	 * @param string $search
241 2
	 * @param null|int $limit
242
	 * @param null|int $offset
243 3
	 * @return string[] an array of all uids
244 3
	 */
245 3 View Code Duplication
	public function getUsers($search = '', $limit = null, $offset = null) {
246 3
		$parameters = [];
247 3
		$searchLike = '';
248 3
		if ($search !== '') {
249 3
			$parameters[] = '%' . $search . '%';
250
			$searchLike = ' WHERE LOWER(`uid`) LIKE LOWER(?)';
251
		}
252
253
		$query = OC_DB::prepare('SELECT `uid` FROM `*PREFIX*users`' . $searchLike . ' ORDER BY `uid` ASC', $limit, $offset);
254
		$result = $query->execute($parameters);
255
		$users = array();
256
		while ($row = $result->fetchRow()) {
257 142
			$users[] = $row['uid'];
258 142
		}
259 142
		return $users;
260
	}
261
262
	/**
263
	 * check if a user exists
264
	 * @param string $uid the username
265
	 * @return boolean
266
	 */
267 99
	public function userExists($uid) {
268 99
		$this->loadUser($uid);
269 70
		return !empty($this->cache[$uid]);
270
	}
271
272 44
	/**
273
	 * get the user's home directory
274
	 * @param string $uid the username
275
	 * @return string|false
276
	 */
277
	public function getHome($uid) {
278 View Code Duplication
		if ($this->userExists($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...
279
			return OC_Config::getValue("datadirectory", OC::$SERVERROOT . "/data") . '/' . $uid;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return \OC_Config::getVa... '/data') . '/' . $uid; (string) is incompatible with the return type of the parent method OC_User_Backend::getHome of type boolean.

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...
280
		}
281
282
		return false;
283
	}
284
285
	/**
286
	 * @return bool
287
	 */
288
	public function hasUserListings() {
289
		return true;
290
	}
291
292
	/**
293
	 * counts the users in the database
294
	 *
295
	 * @return int|bool
296
	 */
297
	public function countUsers() {
298
		$query = OC_DB::prepare('SELECT COUNT(*) FROM `*PREFIX*users`');
299
		$result = $query->execute();
300
		if (OC_DB::isError($result)) {
301
			\OCP\Util::writeLog('core', OC_DB::getErrorMessage(), \OCP\Util::ERROR);
302
			return false;
303
		}
304
		return $result->fetchOne();
305
	}
306
307
	/**
308
	 * returns the username for the given login name in the correct casing
309
	 *
310
	 * @param string $loginName
311
	 * @return string|false
312
	 */
313
	public function loginName2UserName($loginName) {
314
		if ($this->userExists($loginName)) {
315 1
			return $this->cache[$loginName]['uid'];
316 1
		}
317
318
		return false;
319
	}
320
321
	/**
322
	 * Backend name to be shown in user management
323
	 * @return string the name of the backend to be shown
324
	 */
325
	public function getBackendName(){
326
		return 'Database';
327
	}
328
329
	public static function preLoginNameUsedAsUserName($param) {
330
		if(!isset($param['uid'])) {
331
			throw new \Exception('key uid is expected to be set in $param');
332
		}
333
334
		$backends = \OC::$server->getUserManager()->getBackends();
335
		foreach ($backends as $backend) {
0 ignored issues
show
Bug introduced by
The expression $backends of type object<OCP\UserInterface> is not traversable.
Loading history...
336
			if ($backend instanceof \OC_User_Database) {
337
				/** @var \OC_User_Database $backend */
338
				$uid = $backend->loginName2UserName($param['uid']);
339
				if ($uid !== false) {
340
					$param['uid'] = $uid;
341
					return;
342
				}
343
			}
344
		}
345
346
	}
347
}
348