Completed
Push — stable9 ( e5645a...980d90 )
by Blizzz
14:13 queued 13:48
created

USER_LDAP::userExistsOnLDAP()   D

Complexity

Conditions 9
Paths 22

Size

Total Lines 40
Code Lines 24

Duplication

Lines 3
Ratio 7.5 %

Importance

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

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
104
		if(count($users) < 1) {
105
			throw new \Exception('No user available for the given login name.');
106
		}
107
		return $users[0];
108
	}
109
110
	/**
111
	 * Check if the password is correct
112
	 * @param string $uid The username
113
	 * @param string $password The password
114
	 * @return false|string
115
	 *
116
	 * Check if the password is correct without logging in the user
117
	 */
118
	public function checkPassword($uid, $password) {
119
		try {
120
			$ldapRecord = $this->getLDAPUserByLoginName($uid);
121
		} catch(\Exception $e) {
122
			\OC::$server->getLogger()->logException($e, ['app' => 'user_ldap']);
123
			return false;
124
		}
125
		$dn = $ldapRecord['dn'][0];
126
		$user = $this->access->userManager->get($dn);
127
128
		if(!$user instanceof User) {
129
			\OCP\Util::writeLog('user_ldap',
130
				'LDAP Login: Could not get user object for DN ' . $dn .
131
				'. Maybe the LDAP entry has no set display name attribute?',
132
				\OCP\Util::WARN);
133
			return false;
134
		}
135
		if($user->getUsername() !== false) {
136
			//are the credentials OK?
137
			if(!$this->access->areCredentialsValid($dn, $password)) {
138
				return false;
139
			}
140
141
			$this->access->cacheUserExists($user->getUsername());
142
			$user->processAttributes($ldapRecord);
143
			$user->markLogin();
144
145
			return $user->getUsername();
146
		}
147
148
		return false;
149
	}
150
151
	/**
152
	 * Get a list of all users
153
	 *
154
	 * @param string $search
155
	 * @param integer $limit
156
	 * @param integer $offset
157
	 * @return string[] an array of all uids
158
	 */
159
	public function getUsers($search = '', $limit = 10, $offset = 0) {
160
		$search = $this->access->escapeFilterPart($search, true);
161
		$cachekey = 'getUsers-'.$search.'-'.$limit.'-'.$offset;
162
163
		//check if users are cached, if so return
164
		$ldap_users = $this->access->connection->getFromCache($cachekey);
165
		if(!is_null($ldap_users)) {
166
			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...
167
		}
168
169
		// if we'd pass -1 to LDAP search, we'd end up in a Protocol
170
		// error. With a limit of 0, we get 0 results. So we pass null.
171
		if($limit <= 0) {
172
			$limit = null;
173
		}
174
		$filter = $this->access->combineFilterWithAnd(array(
175
			$this->access->connection->ldapUserFilter,
176
			$this->access->connection->ldapUserDisplayName . '=*',
177
			$this->access->getFilterPartForUserSearch($search)
178
		));
179
		$attrs = array($this->access->connection->ldapUserDisplayName, 'dn');
180
		$additionalAttribute = $this->access->connection->ldapUserDisplayName2;
181
		if(!empty($additionalAttribute)) {
182
			$attrs[] = $additionalAttribute;
183
		}
184
185
		\OCP\Util::writeLog('user_ldap',
186
			'getUsers: Options: search '.$search.' limit '.$limit.' offset '.$offset.' Filter: '.$filter,
187
			\OCP\Util::DEBUG);
188
		//do the search and translate results to owncloud names
189
		$ldap_users = $this->access->fetchListOfUsers(
190
			$filter,
191
			$this->access->userManager->getAttributes(true),
192
			$limit, $offset);
193
		$ldap_users = $this->access->ownCloudUserNames($ldap_users);
194
		\OCP\Util::writeLog('user_ldap', 'getUsers: '.count($ldap_users). ' Users found', \OCP\Util::DEBUG);
195
196
		$this->access->connection->writeToCache($cachekey, $ldap_users);
197
		return $ldap_users;
198
	}
199
200
	/**
201
	 * checks whether a user is still available on LDAP
202
	 *
203
	 * @param string|\OCA\User_LDAP\lib\user\User $user either the ownCloud user
204
	 * name or an instance of that user
205
	 * @return bool
206
	 * @throws \Exception
207
	 * @throws \OC\ServerNotAvailableException
208
	 */
209
	public function userExistsOnLDAP($user) {
210
		if(is_string($user)) {
211
			$user = $this->access->userManager->get($user);
212
		}
213
		if(is_null($user)) {
214
			return false;
215
		}
216
217
		$dn = $user->getDN();
218
		//check if user really still exists by reading its entry
219
		if(!is_array($this->access->readAttribute($dn, '', $this->access->connection->ldapUserFilter))) {
220
			$lcr = $this->access->connection->getConnectionResource();
221
			if(is_null($lcr)) {
222
				throw new \Exception('No LDAP Connection to server ' . $this->access->connection->ldapHost);
0 ignored issues
show
Documentation introduced by
The property ldapHost does not exist on object<OCA\user_ldap\lib\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
223
			}
224
225
			try {
226
				$uuid = $this->access->getUserMapper()->getUUIDByDN($dn);
227
				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...
228
					return false;
229
				}
230
				$newDn = $this->access->getUserDnByUuid($uuid);
231
				//check if renamed user is still valid by reapplying the ldap filter
232 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...
233
					return false;
234
				}
235
236
				$this->access->getUserMapper()->setDNbyUUID($newDn, $uuid);
237
				return true;
238
			} catch (\Exception $e) {
239
				return false;
240
			}
241
		}
242
243
		if($user instanceof OfflineUser) {
244
			$user->unmark();
245
		}
246
247
		return true;
248
	}
249
250
	/**
251
	 * check if a user exists
252
	 * @param string $uid the username
253
	 * @return boolean
254
	 * @throws \Exception when connection could not be established
255
	 */
256
	public function userExists($uid) {
257
		$userExists = $this->access->connection->getFromCache('userExists'.$uid);
258
		if(!is_null($userExists)) {
259
			return (bool)$userExists;
260
		}
261
		//getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking.
262
		$user = $this->access->userManager->get($uid);
263
264
		if(is_null($user)) {
265
			\OCP\Util::writeLog('user_ldap', 'No DN found for '.$uid.' on '.
266
				$this->access->connection->ldapHost, \OCP\Util::DEBUG);
0 ignored issues
show
Documentation introduced by
The property ldapHost does not exist on object<OCA\user_ldap\lib\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
267
			$this->access->connection->writeToCache('userExists'.$uid, false);
268
			return false;
269
		} else if($user instanceof OfflineUser) {
270
			//express check for users marked as deleted. Returning true is
271
			//necessary for cleanup
272
			return true;
273
		}
274
275
		$result = $this->userExistsOnLDAP($user);
276
		$this->access->connection->writeToCache('userExists'.$uid, $result);
277
		if($result === true) {
278
			$user->update();
279
		}
280
		return $result;
281
	}
282
283
	/**
284
	* returns whether a user was deleted in LDAP
285
	*
286
	* @param string $uid The username of the user to delete
287
	* @return bool
288
	*/
289
	public function deleteUser($uid) {
290
		$marked = $this->ocConfig->getUserValue($uid, 'user_ldap', 'isDeleted', 0);
291 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...
292
			\OC::$server->getLogger()->notice(
293
				'User '.$uid . ' is not marked as deleted, not cleaning up.',
294
				array('app' => 'user_ldap'));
295
			return false;
296
		}
297
		\OC::$server->getLogger()->info('Cleaning up after user ' . $uid,
298
			array('app' => 'user_ldap'));
299
300
		//Get Home Directory out of user preferences so we can return it later,
301
		//necessary for removing directories as done by OC_User.
302
		$home = $this->ocConfig->getUserValue($uid, 'user_ldap', 'homePath', '');
303
		$this->homesToKill[$uid] = $home;
304
		$this->access->getUserMapper()->unmap($uid);
305
306
		return true;
307
	}
308
309
	/**
310
	 * get the user's home directory
311
	 *
312
	 * @param string $uid the username
313
	 * @return bool|string
314
	 * @throws NoUserException
315
	 * @throws \Exception
316
	 */
317
	public function getHome($uid) {
318
		if(isset($this->homesToKill[$uid]) && !empty($this->homesToKill[$uid])) {
319
			//a deleted user who needs some clean up
320
			return $this->homesToKill[$uid];
321
		}
322
323
		// user Exists check required as it is not done in user proxy!
324
		if(!$this->userExists($uid)) {
325
			return false;
326
		}
327
328
		$cacheKey = 'getHome'.$uid;
329
		$path = $this->access->connection->getFromCache($cacheKey);
330
		if(!is_null($path)) {
331
			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...
332
		}
333
334
		$user = $this->access->userManager->get($uid);
335
		if(is_null($user) || ($user instanceof OfflineUser && !$this->userExistsOnLDAP($user->getOCName()))) {
336
			throw new NoUserException($uid . ' is not a valid user anymore');
337
		}
338
		if($user instanceof OfflineUser) {
339
			// apparently this user survived the userExistsOnLDAP check,
340
			// we request the user instance again in order to retrieve a User
341
			// instance instead
342
			$user = $this->access->userManager->get($uid);
343
		}
344
		$path = $user->getHomePath();
345
		$this->access->cacheUserHome($uid, $path);
346
347
		return $path;
348
	}
349
350
	/**
351
	 * get display name of the user
352
	 * @param string $uid user ID of the user
353
	 * @return string|false display name
354
	 */
355
	public function getDisplayName($uid) {
356
		if(!$this->userExists($uid)) {
357
			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...
358
		}
359
360
		$cacheKey = 'getDisplayName'.$uid;
361
		if(!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) {
362
			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...
363
		}
364
365
		//Check whether the display name is configured to have a 2nd feature
366
		$additionalAttribute = $this->access->connection->ldapUserDisplayName2;
367
		$displayName2 = '';
368
		if(!empty($additionalAttribute)) {
369
			$displayName2 = $this->access->readAttribute(
370
				$this->access->username2dn($uid),
0 ignored issues
show
Security Bug introduced by
It seems like $this->access->username2dn($uid) targeting OCA\user_ldap\lib\Access::username2dn() can also be of type false; however, OCA\user_ldap\lib\Access::readAttribute() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
371
				$additionalAttribute);
372
		}
373
374
		$displayName = $this->access->readAttribute(
375
			$this->access->username2dn($uid),
0 ignored issues
show
Security Bug introduced by
It seems like $this->access->username2dn($uid) targeting OCA\user_ldap\lib\Access::username2dn() can also be of type false; however, OCA\user_ldap\lib\Access::readAttribute() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
376
			$this->access->connection->ldapUserDisplayName);
377
378
		if($displayName && (count($displayName) > 0)) {
379
			$displayName = $displayName[0];
380
381
			if(is_array($displayName2) && (count($displayName2) > 0)) {
382
				$displayName2 = $displayName2[0];
383
			}
384
385
			$user = $this->access->userManager->get($uid);
386
			$displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
0 ignored issues
show
Bug introduced by
The method composeAndStoreDisplayName does only exist in OCA\user_ldap\lib\user\User, but not in OCA\user_ldap\lib\user\OfflineUser.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
387
			$this->access->connection->writeToCache($cacheKey, $displayName);
388
			return $displayName;
389
		}
390
391
		return null;
392
	}
393
394
	/**
395
	 * Get a list of all display names
396
	 *
397
	 * @param string $search
398
	 * @param string|null $limit
399
	 * @param string|null $offset
400
	 * @return array an array of all displayNames (value) and the corresponding uids (key)
401
	 */
402
	public function getDisplayNames($search = '', $limit = null, $offset = null) {
403
		$cacheKey = 'getDisplayNames-'.$search.'-'.$limit.'-'.$offset;
404
		if(!is_null($displayNames = $this->access->connection->getFromCache($cacheKey))) {
405
			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...
406
		}
407
408
		$displayNames = array();
409
		$users = $this->getUsers($search, $limit, $offset);
410
		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...
411
			$displayNames[$user] = $this->getDisplayName($user);
412
		}
413
		$this->access->connection->writeToCache($cacheKey, $displayNames);
414
		return $displayNames;
415
	}
416
417
	/**
418
	* Check if backend implements actions
419
	* @param int $actions bitwise-or'ed actions
420
	* @return boolean
421
	*
422
	* Returns the supported actions as int to be
423
	* compared with OC_USER_BACKEND_CREATE_USER etc.
424
	*/
425
	public function implementsActions($actions) {
426
		return (bool)((\OC_User_Backend::CHECK_PASSWORD
427
			| \OC_User_Backend::GET_HOME
428
			| \OC_User_Backend::GET_DISPLAYNAME
429
			| \OC_User_Backend::PROVIDE_AVATAR
430
			| \OC_User_Backend::COUNT_USERS)
431
			& $actions);
432
	}
433
434
	/**
435
	 * @return bool
436
	 */
437
	public function hasUserListings() {
438
		return true;
439
	}
440
441
	/**
442
	 * counts the users in LDAP
443
	 *
444
	 * @return int|bool
445
	 */
446
	public function countUsers() {
447
		$filter = $this->access->getFilterForUserCount();
448
		$cacheKey = 'countUsers-'.$filter;
449
		if(!is_null($entries = $this->access->connection->getFromCache($cacheKey))) {
450
			return $entries;
451
		}
452
		$entries = $this->access->countUsers($filter);
453
		$this->access->connection->writeToCache($cacheKey, $entries);
454
		return $entries;
455
	}
456
457
	/**
458
	 * Backend name to be shown in user management
459
	 * @return string the name of the backend to be shown
460
	 */
461
	public function getBackendName(){
462
		return 'LDAP';
463
	}
464
465
}
466