Completed
Push — stable9 ( fe2ca8...0bda5d )
by Lukas
34:35 queued 23:21
created

USER_LDAP::getDisplayName()   D

Complexity

Conditions 10
Paths 20

Size

Total Lines 44
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 10
eloc 27
c 1
b 1
f 0
nc 20
nop 1
dl 0
loc 44
rs 4.8196

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 Jörn Friedrich Dreyer <[email protected]>
10
 * @author Lukas Reschke <[email protected]>
11
 * @author Morris Jobke <[email protected]>
12
 * @author Renaud Fortier <[email protected]>
13
 * @author Robin Appelman <[email protected]>
14
 * @author Robin McCorkell <[email protected]>
15
 * @author Thomas Müller <[email protected]>
16
 * @author Tom Needham <[email protected]>
17
 *
18
 * @license AGPL-3.0
19
 *
20
 * This code is free software: you can redistribute it and/or modify
21
 * it under the terms of the GNU Affero General Public License, version 3,
22
 * as published by the Free Software Foundation.
23
 *
24
 * This program is distributed in the hope that it will be useful,
25
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
 * GNU Affero General Public License for more details.
28
 *
29
 * You should have received a copy of the GNU Affero General Public License, version 3,
30
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
31
 *
32
 */
33
34
namespace OCA\user_ldap;
35
36
use OC\User\NoUserException;
37
use OCA\user_ldap\lib\BackendUtility;
38
use OCA\user_ldap\lib\Access;
39
use OCA\user_ldap\lib\user\OfflineUser;
40
use OCA\User_LDAP\lib\User\User;
41
use OCP\IConfig;
42
43
class USER_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserInterface {
44
	/** @var string[] $homesToKill */
45
	protected $homesToKill = array();
46
47
	/** @var \OCP\IConfig */
48
	protected $ocConfig;
49
50
	/**
51
	 * @param \OCA\user_ldap\lib\Access $access
52
	 * @param \OCP\IConfig $ocConfig
53
	 */
54
	public function __construct(Access $access, IConfig $ocConfig) {
55
		parent::__construct($access);
56
		$this->ocConfig = $ocConfig;
57
	}
58
59
	/**
60
	 * checks whether the user is allowed to change his avatar in ownCloud
61
	 * @param string $uid the ownCloud user name
62
	 * @return boolean either the user can or cannot
63
	 */
64
	public function canChangeAvatar($uid) {
65
		$user = $this->access->userManager->get($uid);
66
		if(!$user instanceof User) {
67
			return false;
68
		}
69
		if($user->getAvatarImage() === false) {
70
			return true;
71
		}
72
73
		return false;
74
	}
75
76
	/**
77
	 * returns the username for the given login name, if available
78
	 *
79
	 * @param string $loginName
80
	 * @return string|false
81
	 */
82
	public function loginName2UserName($loginName) {
83
		try {
84
			$ldapRecord = $this->getLDAPUserByLoginName($loginName);
85
			$user = $this->access->userManager->get($ldapRecord['dn'][0]);
86
			if($user instanceof OfflineUser) {
87
				return false;
88
			}
89
			return $user->getUsername();
90
		} catch (\Exception $e) {
91
			return false;
92
		}
93
	}
94
95
	/**
96
	 * returns an LDAP record based on a given login name
97
	 *
98
	 * @param string $loginName
99
	 * @return array
100
	 * @throws \Exception
101
	 */
102
	public function getLDAPUserByLoginName($loginName) {
103
		//find out dn of the user name
104
		$attrs = $this->access->userManager->getAttributes();
105
		$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...
106
		if(count($users) < 1) {
107
			throw new \Exception('No user available for the given login name.');
108
		}
109
		return $users[0];
110
	}
111
112
	/**
113
	 * Check if the password is correct
114
	 * @param string $uid The username
115
	 * @param string $password The password
116
	 * @return false|string
117
	 *
118
	 * Check if the password is correct without logging in the user
119
	 */
120
	public function checkPassword($uid, $password) {
121
		try {
122
			$ldapRecord = $this->getLDAPUserByLoginName($uid);
123
		} catch(\Exception $e) {
124
			\OC::$server->getLogger()->logException($e, ['app' => 'user_ldap']);
125
			return false;
126
		}
127
		$dn = $ldapRecord['dn'][0];
128
		$user = $this->access->userManager->get($dn);
129
130
		if(!$user instanceof User) {
131
			\OCP\Util::writeLog('user_ldap',
132
				'LDAP Login: Could not get user object for DN ' . $dn .
133
				'. Maybe the LDAP entry has no set display name attribute?',
134
				\OCP\Util::WARN);
135
			return false;
136
		}
137
		if($user->getUsername() !== false) {
138
			//are the credentials OK?
139
			if(!$this->access->areCredentialsValid($dn, $password)) {
140
				return false;
141
			}
142
143
			$this->access->cacheUserExists($user->getUsername());
144
			$user->processAttributes($ldapRecord);
145
			$user->markLogin();
146
147
			return $user->getUsername();
148
		}
149
150
		return false;
151
	}
152
153
	/**
154
	 * Get a list of all users
155
	 *
156
	 * @param string $search
157
	 * @param integer $limit
158
	 * @param integer $offset
159
	 * @return string[] an array of all uids
160
	 */
161
	public function getUsers($search = '', $limit = 10, $offset = 0) {
162
		$search = $this->access->escapeFilterPart($search, true);
163
		$cachekey = 'getUsers-'.$search.'-'.$limit.'-'.$offset;
164
165
		//check if users are cached, if so return
166
		$ldap_users = $this->access->connection->getFromCache($cachekey);
167
		if(!is_null($ldap_users)) {
168
			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...
169
		}
170
171
		// if we'd pass -1 to LDAP search, we'd end up in a Protocol
172
		// error. With a limit of 0, we get 0 results. So we pass null.
173
		if($limit <= 0) {
174
			$limit = null;
175
		}
176
		$filter = $this->access->combineFilterWithAnd(array(
177
			$this->access->connection->ldapUserFilter,
178
			$this->access->connection->ldapUserDisplayName . '=*',
179
			$this->access->getFilterPartForUserSearch($search)
180
		));
181
		$attrs = array($this->access->connection->ldapUserDisplayName, 'dn');
182
		$additionalAttribute = $this->access->connection->ldapUserDisplayName2;
183
		if(!empty($additionalAttribute)) {
184
			$attrs[] = $additionalAttribute;
185
		}
186
187
		\OCP\Util::writeLog('user_ldap',
188
			'getUsers: Options: search '.$search.' limit '.$limit.' offset '.$offset.' Filter: '.$filter,
189
			\OCP\Util::DEBUG);
190
		//do the search and translate results to owncloud names
191
		$ldap_users = $this->access->fetchListOfUsers(
192
			$filter,
193
			$this->access->userManager->getAttributes(true),
194
			$limit, $offset);
195
		$ldap_users = $this->access->ownCloudUserNames($ldap_users);
196
		\OCP\Util::writeLog('user_ldap', 'getUsers: '.count($ldap_users). ' Users found', \OCP\Util::DEBUG);
197
198
		$this->access->connection->writeToCache($cachekey, $ldap_users);
199
		return $ldap_users;
200
	}
201
202
	/**
203
	 * checks whether a user is still available on LDAP
204
	 *
205
	 * @param string|\OCA\User_LDAP\lib\user\User $user either the ownCloud user
206
	 * name or an instance of that user
207
	 * @return bool
208
	 * @throws \Exception
209
	 * @throws \OC\ServerNotAvailableException
210
	 */
211
	public function userExistsOnLDAP($user) {
212
		if(is_string($user)) {
213
			$user = $this->access->userManager->get($user);
214
		}
215
		if(is_null($user)) {
216
			return false;
217
		}
218
219
		$dn = $user->getDN();
220
		//check if user really still exists by reading its entry
221
		if(!is_array($this->access->readAttribute($dn, '', $this->access->connection->ldapUserFilter))) {
222
			$lcr = $this->access->connection->getConnectionResource();
223
			if(is_null($lcr)) {
224
				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...
225
			}
226
227
			try {
228
				$uuid = $this->access->getUserMapper()->getUUIDByDN($dn);
229
				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...
230
					return false;
231
				}
232
				$newDn = $this->access->getUserDnByUuid($uuid);
233
				//check if renamed user is still valid by reapplying the ldap filter
234 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...
235
					return false;
236
				}
237
238
				$this->access->getUserMapper()->setDNbyUUID($newDn, $uuid);
239
				return true;
240
			} catch (\Exception $e) {
241
				return false;
242
			}
243
		}
244
245
		if($user instanceof OfflineUser) {
246
			$user->unmark();
247
		}
248
249
		return true;
250
	}
251
252
	/**
253
	 * check if a user exists
254
	 * @param string $uid the username
255
	 * @return boolean
256
	 * @throws \Exception when connection could not be established
257
	 */
258
	public function userExists($uid) {
259
		$userExists = $this->access->connection->getFromCache('userExists'.$uid);
260
		if(!is_null($userExists)) {
261
			return (bool)$userExists;
262
		}
263
		//getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking.
264
		$user = $this->access->userManager->get($uid);
265
266
		if(is_null($user)) {
267
			\OCP\Util::writeLog('user_ldap', 'No DN found for '.$uid.' on '.
268
				$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...
269
			$this->access->connection->writeToCache('userExists'.$uid, false);
270
			return false;
271
		} else if($user instanceof OfflineUser) {
272
			//express check for users marked as deleted. Returning true is
273
			//necessary for cleanup
274
			return true;
275
		}
276
277
		$result = $this->userExistsOnLDAP($user);
278
		$this->access->connection->writeToCache('userExists'.$uid, $result);
279
		if($result === true) {
280
			$user->update();
281
		}
282
		return $result;
283
	}
284
285
	/**
286
	* returns whether a user was deleted in LDAP
287
	*
288
	* @param string $uid The username of the user to delete
289
	* @return bool
290
	*/
291
	public function deleteUser($uid) {
292
		$marked = $this->ocConfig->getUserValue($uid, 'user_ldap', 'isDeleted', 0);
293 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...
294
			\OC::$server->getLogger()->notice(
295
				'User '.$uid . ' is not marked as deleted, not cleaning up.',
296
				array('app' => 'user_ldap'));
297
			return false;
298
		}
299
		\OC::$server->getLogger()->info('Cleaning up after user ' . $uid,
300
			array('app' => 'user_ldap'));
301
302
		//Get Home Directory out of user preferences so we can return it later,
303
		//necessary for removing directories as done by OC_User.
304
		$home = $this->ocConfig->getUserValue($uid, 'user_ldap', 'homePath', '');
305
		$this->homesToKill[$uid] = $home;
306
		$this->access->getUserMapper()->unmap($uid);
307
308
		return true;
309
	}
310
311
	/**
312
	 * get the user's home directory
313
	 *
314
	 * @param string $uid the username
315
	 * @return bool|string
316
	 * @throws NoUserException
317
	 * @throws \Exception
318
	 */
319
	public function getHome($uid) {
320
		if(isset($this->homesToKill[$uid]) && !empty($this->homesToKill[$uid])) {
321
			//a deleted user who needs some clean up
322
			return $this->homesToKill[$uid];
323
		}
324
325
		// user Exists check required as it is not done in user proxy!
326
		if(!$this->userExists($uid)) {
327
			return false;
328
		}
329
330
		$cacheKey = 'getHome'.$uid;
331
		$path = $this->access->connection->getFromCache($cacheKey);
332
		if(!is_null($path)) {
333
			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...
334
		}
335
336
		$user = $this->access->userManager->get($uid);
337
		if(is_null($user) || ($user instanceof OfflineUser && !$this->userExistsOnLDAP($user->getOCName()))) {
338
			throw new NoUserException($uid . ' is not a valid user anymore');
339
		}
340
		if($user instanceof OfflineUser) {
341
			// apparently this user survived the userExistsOnLDAP check,
342
			// we request the user instance again in order to retrieve a User
343
			// instance instead
344
			$user = $this->access->userManager->get($uid);
345
		}
346
		$path = $user->getHomePath();
347
		$this->access->cacheUserHome($uid, $path);
348
349
		return $path;
350
	}
351
352
	/**
353
	 * get display name of the user
354
	 * @param string $uid user ID of the user
355
	 * @return string|false display name
356
	 */
357
	public function getDisplayName($uid) {
358
		if(!$this->userExists($uid)) {
359
			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...
360
		}
361
362
		$cacheKey = 'getDisplayName'.$uid;
363
		if(!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) {
364
			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...
365
		}
366
367
		//Check whether the display name is configured to have a 2nd feature
368
		$additionalAttribute = $this->access->connection->ldapUserDisplayName2;
369
		$displayName2 = '';
370
		if(!empty($additionalAttribute)) {
371
			$displayName2 = $this->access->readAttribute(
372
				$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...
373
				$additionalAttribute);
374
		}
375
376
		$displayName = $this->access->readAttribute(
377
			$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...
378
			$this->access->connection->ldapUserDisplayName);
379
380
		if($displayName && (count($displayName) > 0)) {
381
			$displayName = $displayName[0];
382
383
			if(is_array($displayName2) && (count($displayName2) > 0)) {
384
				$displayName2 = $displayName2[0];
385
			}
386
387
			$user = $this->access->userManager->get($uid);
388
			if ($user instanceof User) {
389
				$displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
390
				$this->access->connection->writeToCache($cacheKey, $displayName);
391
			}
392
			if ($user instanceof OfflineUser) {
393
				/** @var OfflineUser $user*/
394
				$displayName = $user->getDisplayName();
395
			}
396
			return $displayName;
397
		}
398
399
		return null;
400
	}
401
402
	/**
403
	 * Get a list of all display names
404
	 *
405
	 * @param string $search
406
	 * @param string|null $limit
407
	 * @param string|null $offset
408
	 * @return array an array of all displayNames (value) and the corresponding uids (key)
409
	 */
410
	public function getDisplayNames($search = '', $limit = null, $offset = null) {
411
		$cacheKey = 'getDisplayNames-'.$search.'-'.$limit.'-'.$offset;
412
		if(!is_null($displayNames = $this->access->connection->getFromCache($cacheKey))) {
413
			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...
414
		}
415
416
		$displayNames = array();
417
		$users = $this->getUsers($search, $limit, $offset);
418
		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...
419
			$displayNames[$user] = $this->getDisplayName($user);
420
		}
421
		$this->access->connection->writeToCache($cacheKey, $displayNames);
422
		return $displayNames;
423
	}
424
425
	/**
426
	* Check if backend implements actions
427
	* @param int $actions bitwise-or'ed actions
428
	* @return boolean
429
	*
430
	* Returns the supported actions as int to be
431
	* compared with OC_USER_BACKEND_CREATE_USER etc.
432
	*/
433
	public function implementsActions($actions) {
434
		return (bool)((\OC_User_Backend::CHECK_PASSWORD
435
			| \OC_User_Backend::GET_HOME
436
			| \OC_User_Backend::GET_DISPLAYNAME
437
			| \OC_User_Backend::PROVIDE_AVATAR
438
			| \OC_User_Backend::COUNT_USERS)
439
			& $actions);
440
	}
441
442
	/**
443
	 * @return bool
444
	 */
445
	public function hasUserListings() {
446
		return true;
447
	}
448
449
	/**
450
	 * counts the users in LDAP
451
	 *
452
	 * @return int|bool
453
	 */
454
	public function countUsers() {
455
		$filter = $this->access->getFilterForUserCount();
456
		$cacheKey = 'countUsers-'.$filter;
457
		if(!is_null($entries = $this->access->connection->getFromCache($cacheKey))) {
458
			return $entries;
459
		}
460
		$entries = $this->access->countUsers($filter);
461
		$this->access->connection->writeToCache($cacheKey, $entries);
462
		return $entries;
463
	}
464
465
	/**
466
	 * Backend name to be shown in user management
467
	 * @return string the name of the backend to be shown
468
	 */
469
	public function getBackendName(){
470
		return 'LDAP';
471
	}
472
473
}
474