Completed
Pull Request — master (#2286)
by Lukas
14:18 queued 06:33
created

User_LDAP::hasUserListings()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Bart Visscher <[email protected]>
7
 * @author Dominik Schmidt <[email protected]>
8
 * @author felixboehm <[email protected]>
9
 * @author Joas Schilling <[email protected]>
10
 * @author Jörn Friedrich Dreyer <[email protected]>
11
 * @author Lukas Reschke <[email protected]>
12
 * @author Morris Jobke <[email protected]>
13
 * @author Renaud Fortier <[email protected]>
14
 * @author Robin Appelman <[email protected]>
15
 * @author Robin McCorkell <[email protected]>
16
 * @author Thomas Müller <[email protected]>
17
 * @author Tom Needham <[email protected]>
18
 * @author Roger Szabo <[email protected]>
19
 *
20
 * @license AGPL-3.0
21
 *
22
 * This code is free software: you can redistribute it and/or modify
23
 * it under the terms of the GNU Affero General Public License, version 3,
24
 * as published by the Free Software Foundation.
25
 *
26
 * This program is distributed in the hope that it will be useful,
27
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29
 * GNU Affero General Public License for more details.
30
 *
31
 * You should have received a copy of the GNU Affero General Public License, version 3,
32
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
33
 *
34
 */
35
36
namespace OCA\User_LDAP;
37
38
use OC\User\Backend;
39
use OC\User\NoUserException;
40
use OCA\User_LDAP\Exceptions\NotOnLDAP;
41
use OCA\User_LDAP\User\OfflineUser;
42
use OCA\User_LDAP\User\User;
43
use OCP\IConfig;
44
45
class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserInterface, IUserLDAP {
46
	/** @var string[] $homesToKill */
47
	protected $homesToKill = array();
48
49
	/** @var \OCP\IConfig */
50
	protected $ocConfig;
51
52
	/**
53
	 * @param Access $access
54
	 * @param \OCP\IConfig $ocConfig
55
	 */
56
	public function __construct(Access $access, IConfig $ocConfig) {
57
		parent::__construct($access);
58
		$this->ocConfig = $ocConfig;
59
	}
60
61
	/**
62
	 * checks whether the user is allowed to change his avatar in ownCloud
63
	 * @param string $uid the ownCloud user name
64
	 * @return boolean either the user can or cannot
65
	 */
66
	public function canChangeAvatar($uid) {
67
		$user = $this->access->userManager->get($uid);
68
		if(!$user instanceof User) {
69
			return false;
70
		}
71
		if($user->getAvatarImage() === false) {
72
			return true;
73
		}
74
75
		return false;
76
	}
77
78
	/**
79
	 * returns the username for the given login name, if available
80
	 *
81
	 * @param string $loginName
82
	 * @return string|false
83
	 */
84
	public function loginName2UserName($loginName) {
85
		$cacheKey = 'loginName2UserName-'.$loginName;
86
		$username = $this->access->connection->getFromCache($cacheKey);
87
		if(!is_null($username)) {
88
			return $username;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $username; (object|integer|double|string|array|boolean) is incompatible with the return type documented by OCA\User_LDAP\User_LDAP::loginName2UserName of type string|false.

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...
89
		}
90
91
		try {
92
			$ldapRecord = $this->getLDAPUserByLoginName($loginName);
93
			$user = $this->access->userManager->get($ldapRecord['dn'][0]);
94
			if($user instanceof OfflineUser) {
95
				// this path is not really possible, however get() is documented
96
				// to return User or OfflineUser so we are very defensive here.
97
				$this->access->connection->writeToCache($cacheKey, false);
98
				return false;
99
			}
100
			$username = $user->getUsername();
101
			$this->access->connection->writeToCache($cacheKey, $username);
102
			return $username;
103
		} catch (NotOnLDAP $e) {
104
			$this->access->connection->writeToCache($cacheKey, false);
105
			return false;
106
		}
107
	}
108
	
109
	/**
110
	 * returns the username for the given LDAP DN, if available
111
	 *
112
	 * @param string $dn
113
	 * @return string|false with the username
114
	 */
115
	public function dn2UserName($dn) {
116
		return $this->access->dn2username($dn);
117
	}
118
119
	/**
120
	 * returns an LDAP record based on a given login name
121
	 *
122
	 * @param string $loginName
123
	 * @return array
124
	 * @throws NotOnLDAP
125
	 */
126
	public function getLDAPUserByLoginName($loginName) {
127
		//find out dn of the user name
128
		$attrs = $this->access->userManager->getAttributes();
129
		$users = $this->access->fetchUsersByLoginName($loginName, $attrs);
130
		if(count($users) < 1) {
131
			throw new NotOnLDAP('No user available for the given login name on ' .
132
				$this->access->connection->ldapHost . ':' . $this->access->connection->ldapPort);
133
		}
134
		return $users[0];
135
	}
136
137
	/**
138
	 * Check if the password is correct
139
	 * @param string $uid The username
140
	 * @param string $password The password
141
	 * @return false|string
142
	 *
143
	 * Check if the password is correct without logging in the user
144
	 */
145
	public function checkPassword($uid, $password) {
146
		try {
147
			$ldapRecord = $this->getLDAPUserByLoginName($uid);
148
		} catch(\Exception $e) {
149
			\OC::$server->getLogger()->logException($e, ['app' => 'user_ldap']);
150
			return false;
151
		}
152
		$dn = $ldapRecord['dn'][0];
153
		$user = $this->access->userManager->get($dn);
154
155
		if(!$user instanceof User) {
156
			\OCP\Util::writeLog('user_ldap',
157
				'LDAP Login: Could not get user object for DN ' . $dn .
158
				'. Maybe the LDAP entry has no set display name attribute?',
159
				\OCP\Util::WARN);
160
			return false;
161
		}
162
		if($user->getUsername() !== false) {
163
			//are the credentials OK?
164
			if(!$this->access->areCredentialsValid($dn, $password)) {
165
				return false;
166
			}
167
168
			$this->access->cacheUserExists($user->getUsername());
169
			$user->processAttributes($ldapRecord);
170
			$user->markLogin();
171
172
			return $user->getUsername();
173
		}
174
175
		return false;
176
	}
177
178
	/**
179
	 * Set password
180
	 * @param string $uid The username
181
	 * @param string $password The new password
182
	 * @return bool
183
	 */
184
	public function setPassword($uid, $password) {
185
		$user = $this->access->userManager->get($uid);
186
187
		if(!$user instanceof User) {
188
			throw new \Exception('LDAP setPassword: Could not get user object for uid ' . $uid .
189
				'. Maybe the LDAP entry has no set display name attribute?');
190
		}
191
		if($user->getUsername() !== false) {
192
			return $this->access->setPassword($user->getDN(), $password);
193
		}
194
195
		return false;
196
	}
197
198
	/**
199
	 * Get a list of all users
200
	 *
201
	 * @param string $search
202
	 * @param integer $limit
203
	 * @param integer $offset
204
	 * @return string[] an array of all uids
205
	 */
206
	public function getUsers($search = '', $limit = 10, $offset = 0) {
207
		$search = $this->access->escapeFilterPart($search, true);
208
		$cachekey = 'getUsers-'.$search.'-'.$limit.'-'.$offset;
209
210
		//check if users are cached, if so return
211
		$ldap_users = $this->access->connection->getFromCache($cachekey);
212
		if(!is_null($ldap_users)) {
213
			return $ldap_users;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $ldap_users; (object|integer|double|string|array|boolean) is incompatible with the return type declared by the interface OCP\UserInterface::getUsers 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...
214
		}
215
216
		// if we'd pass -1 to LDAP search, we'd end up in a Protocol
217
		// error. With a limit of 0, we get 0 results. So we pass null.
218
		if($limit <= 0) {
219
			$limit = null;
220
		}
221
		$filter = $this->access->combineFilterWithAnd(array(
222
			$this->access->connection->ldapUserFilter,
223
			$this->access->connection->ldapUserDisplayName . '=*',
224
			$this->access->getFilterPartForUserSearch($search)
225
		));
226
227
		\OCP\Util::writeLog('user_ldap',
228
			'getUsers: Options: search '.$search.' limit '.$limit.' offset '.$offset.' Filter: '.$filter,
229
			\OCP\Util::DEBUG);
230
		//do the search and translate results to owncloud names
231
		$ldap_users = $this->access->fetchListOfUsers(
232
			$filter,
233
			$this->access->userManager->getAttributes(true),
234
			$limit, $offset);
235
		$ldap_users = $this->access->ownCloudUserNames($ldap_users);
236
		\OCP\Util::writeLog('user_ldap', 'getUsers: '.count($ldap_users). ' Users found', \OCP\Util::DEBUG);
237
238
		$this->access->connection->writeToCache($cachekey, $ldap_users);
239
		return $ldap_users;
240
	}
241
242
	/**
243
	 * checks whether a user is still available on LDAP
244
	 *
245
	 * @param string|\OCA\User_LDAP\User\User $user either the ownCloud user
246
	 * name or an instance of that user
247
	 * @return bool
248
	 * @throws \Exception
249
	 * @throws \OC\ServerNotAvailableException
250
	 */
251
	public function userExistsOnLDAP($user) {
252
		if(is_string($user)) {
253
			$user = $this->access->userManager->get($user);
254
		}
255
		if(is_null($user)) {
256
			return false;
257
		}
258
259
		$dn = $user->getDN();
260
		//check if user really still exists by reading its entry
261
		if(!is_array($this->access->readAttribute($dn, '', $this->access->connection->ldapUserFilter))) {
262
			$lcr = $this->access->connection->getConnectionResource();
263
			if(is_null($lcr)) {
264
				throw new \Exception('No LDAP Connection to server ' . $this->access->connection->ldapHost);
265
			}
266
267
			try {
268
				$uuid = $this->access->getUserMapper()->getUUIDByDN($dn);
269
				if(!$uuid) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $uuid of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
270
					return false;
271
				}
272
				$newDn = $this->access->getUserDnByUuid($uuid);
273
				//check if renamed user is still valid by reapplying the ldap filter
274 View Code Duplication
				if(!is_array($this->access->readAttribute($newDn, '', $this->access->connection->ldapUserFilter))) {
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...
275
					return false;
276
				}
277
				$this->access->getUserMapper()->setDNbyUUID($newDn, $uuid);
278
				return true;
279
			} catch (\Exception $e) {
280
				return false;
281
			}
282
		}
283
284
		if($user instanceof OfflineUser) {
285
			$user->unmark();
286
		}
287
288
		return true;
289
	}
290
291
	/**
292
	 * check if a user exists
293
	 * @param string $uid the username
294
	 * @return boolean
295
	 * @throws \Exception when connection could not be established
296
	 */
297
	public function userExists($uid) {
298
		$userExists = $this->access->connection->getFromCache('userExists'.$uid);
299
		if(!is_null($userExists)) {
300
			return (bool)$userExists;
301
		}
302
		//getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking.
303
		$user = $this->access->userManager->get($uid);
304
305
		if(is_null($user)) {
306
			\OCP\Util::writeLog('user_ldap', 'No DN found for '.$uid.' on '.
307
				$this->access->connection->ldapHost, \OCP\Util::DEBUG);
308
			$this->access->connection->writeToCache('userExists'.$uid, false);
309
			return false;
310
		} else if($user instanceof OfflineUser) {
311
			//express check for users marked as deleted. Returning true is
312
			//necessary for cleanup
313
			return true;
314
		}
315
316
		$result = $this->userExistsOnLDAP($user);
317
		$this->access->connection->writeToCache('userExists'.$uid, $result);
318
		if($result === true) {
319
			$user->update();
320
		}
321
		return $result;
322
	}
323
324
	/**
325
	* returns whether a user was deleted in LDAP
326
	*
327
	* @param string $uid The username of the user to delete
328
	* @return bool
329
	*/
330
	public function deleteUser($uid) {
331
		$marked = $this->ocConfig->getUserValue($uid, 'user_ldap', 'isDeleted', 0);
332 View Code Duplication
		if(intval($marked) === 0) {
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...
333
			\OC::$server->getLogger()->notice(
334
				'User '.$uid . ' is not marked as deleted, not cleaning up.',
335
				array('app' => 'user_ldap'));
336
			return false;
337
		}
338
		\OC::$server->getLogger()->info('Cleaning up after user ' . $uid,
339
			array('app' => 'user_ldap'));
340
341
		//Get Home Directory out of user preferences so we can return it later,
342
		//necessary for removing directories as done by OC_User.
343
		$home = $this->ocConfig->getUserValue($uid, 'user_ldap', 'homePath', '');
344
		$this->homesToKill[$uid] = $home;
345
		$this->access->getUserMapper()->unmap($uid);
346
347
		return true;
348
	}
349
350
	/**
351
	 * get the user's home directory
352
	 *
353
	 * @param string $uid the username
354
	 * @return bool|string
355
	 * @throws NoUserException
356
	 * @throws \Exception
357
	 */
358
	public function getHome($uid) {
359
		if(isset($this->homesToKill[$uid]) && !empty($this->homesToKill[$uid])) {
360
			//a deleted user who needs some clean up
361
			return $this->homesToKill[$uid];
362
		}
363
364
		// user Exists check required as it is not done in user proxy!
365
		if(!$this->userExists($uid)) {
366
			return false;
367
		}
368
369
		$cacheKey = 'getHome'.$uid;
370
		$path = $this->access->connection->getFromCache($cacheKey);
371
		if(!is_null($path)) {
372
			return $path;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $path; (object|integer|double|string|array|boolean) is incompatible with the return type documented by OCA\User_LDAP\User_LDAP::getHome of type boolean|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...
373
		}
374
375
		$user = $this->access->userManager->get($uid);
376
		if(is_null($user) || ($user instanceof OfflineUser && !$this->userExistsOnLDAP($user->getOCName()))) {
377
			throw new NoUserException($uid . ' is not a valid user anymore');
378
		}
379
		if($user instanceof OfflineUser) {
380
			// apparently this user survived the userExistsOnLDAP check,
381
			// we request the user instance again in order to retrieve a User
382
			// instance instead
383
			$user = $this->access->userManager->get($uid);
384
		}
385
		$path = $user->getHomePath();
386
		$this->access->cacheUserHome($uid, $path);
387
388
		return $path;
389
	}
390
391
	/**
392
	 * get display name of the user
393
	 * @param string $uid user ID of the user
394
	 * @return string|false display name
395
	 */
396
	public function getDisplayName($uid) {
397
		if(!$this->userExists($uid)) {
398
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type declared by the interface OCP\UserInterface::getDisplayName 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...
399
		}
400
401
		$cacheKey = 'getDisplayName'.$uid;
402
		if(!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) {
403
			return $displayName;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $displayName; (object|integer|double|string|array|boolean) is incompatible with the return type declared by the interface OCP\UserInterface::getDisplayName 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...
404
		}
405
406
		//Check whether the display name is configured to have a 2nd feature
407
		$additionalAttribute = $this->access->connection->ldapUserDisplayName2;
408
		$displayName2 = '';
409
		if ($additionalAttribute !== '') {
410
			$displayName2 = $this->access->readAttribute(
411
				$this->access->username2dn($uid),
0 ignored issues
show
Security Bug introduced by
It seems like $this->access->username2dn($uid) targeting OCA\User_LDAP\Access::username2dn() can also be of type false; however, OCA\User_LDAP\Access::readAttribute() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
412
				$additionalAttribute);
413
		}
414
415
		$displayName = $this->access->readAttribute(
416
			$this->access->username2dn($uid),
0 ignored issues
show
Security Bug introduced by
It seems like $this->access->username2dn($uid) targeting OCA\User_LDAP\Access::username2dn() can also be of type false; however, OCA\User_LDAP\Access::readAttribute() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
417
			$this->access->connection->ldapUserDisplayName);
418
419
		if($displayName && (count($displayName) > 0)) {
420
			$displayName = $displayName[0];
421
422
			if (is_array($displayName2)){
423
				$displayName2 = count($displayName2) > 0 ? $displayName2[0] : '';
424
			}
425
426
			$user = $this->access->userManager->get($uid);
427
			if ($user instanceof User) {
428
				$displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
429
				$this->access->connection->writeToCache($cacheKey, $displayName);
430
			}
431
			if ($user instanceof OfflineUser) {
432
				/** @var OfflineUser $user*/
433
				$displayName = $user->getDisplayName();
434
			}
435
			return $displayName;
436
		}
437
438
		return null;
439
	}
440
441
	/**
442
	 * Get a list of all display names
443
	 *
444
	 * @param string $search
445
	 * @param string|null $limit
446
	 * @param string|null $offset
447
	 * @return array an array of all displayNames (value) and the corresponding uids (key)
448
	 */
449
	public function getDisplayNames($search = '', $limit = null, $offset = null) {
450
		$cacheKey = 'getDisplayNames-'.$search.'-'.$limit.'-'.$offset;
451
		if(!is_null($displayNames = $this->access->connection->getFromCache($cacheKey))) {
452
			return $displayNames;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $displayNames; (object|integer|double|string|array|boolean) is incompatible with the return type declared by the interface OCP\UserInterface::getDisplayNames 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...
453
		}
454
455
		$displayNames = array();
456
		$users = $this->getUsers($search, $limit, $offset);
457
		foreach ($users as $user) {
0 ignored issues
show
Bug introduced by
The expression $users of type object|integer|double|string|array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
458
			$displayNames[$user] = $this->getDisplayName($user);
459
		}
460
		$this->access->connection->writeToCache($cacheKey, $displayNames);
461
		return $displayNames;
462
	}
463
464
	/**
465
	* Check if backend implements actions
466
	* @param int $actions bitwise-or'ed actions
467
	* @return boolean
468
	*
469
	* Returns the supported actions as int to be
470
	* compared with OC_USER_BACKEND_CREATE_USER etc.
471
	*/
472
	public function implementsActions($actions) {
473
		return (bool)((Backend::CHECK_PASSWORD
474
			| Backend::GET_HOME
475
			| Backend::GET_DISPLAYNAME
476
			| Backend::PROVIDE_AVATAR
477
			| Backend::COUNT_USERS
478
			| ((intval($this->access->connection->turnOnPasswordChange) === 1)?(Backend::SET_PASSWORD):0))
479
			& $actions);
480
	}
481
482
	/**
483
	 * @return bool
484
	 */
485
	public function hasUserListings() {
486
		return true;
487
	}
488
489
	/**
490
	 * counts the users in LDAP
491
	 *
492
	 * @return int|bool
493
	 */
494
	public function countUsers() {
495
		$filter = $this->access->getFilterForUserCount();
496
		$cacheKey = 'countUsers-'.$filter;
497
		if(!is_null($entries = $this->access->connection->getFromCache($cacheKey))) {
498
			return $entries;
499
		}
500
		$entries = $this->access->countUsers($filter);
501
		$this->access->connection->writeToCache($cacheKey, $entries);
502
		return $entries;
503
	}
504
505
	/**
506
	 * Backend name to be shown in user management
507
	 * @return string the name of the backend to be shown
508
	 */
509
	public function getBackendName(){
510
		return 'LDAP';
511
	}
512
	
513
	/**
514
	 * Return access for LDAP interaction.
515
	 * @param string $uid
516
	 * @return Access instance of Access for LDAP interaction
517
	 */
518
	public function getLDAPAccess($uid) {
519
		return $this->access;
520
	}
521
	
522
	/**
523
	 * Return LDAP connection resource from a cloned connection.
524
	 * The cloned connection needs to be closed manually.
525
	 * of the current access.
526
	 * @param string $uid
527
	 * @return resource of the LDAP connection
528
	 */
529
	public function getNewLDAPConnection($uid) {
530
		$connection = clone $this->access->getConnection();
531
		return $connection->getConnectionResource();
532
	}
533
}
534