Completed
Push — master ( 3d671c...42e805 )
by Blizzz
48:26 queued 33:21
created

User_LDAP::setPassword()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 16
nc 4
nop 2
dl 0
loc 24
rs 8.5125
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
use OCP\Notification\IManager as INotificationManager;
45
use OCP\Util;
46
47
class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserInterface, IUserLDAP {
48
	/** @var string[] $homesToKill */
49
	protected $homesToKill = array();
50
51
	/** @var \OCP\IConfig */
52
	protected $ocConfig;
53
54
	/** @var INotificationManager */
55
	protected $notificationManager;
56
57
	/**
58
	 * @param Access $access
59
	 * @param \OCP\IConfig $ocConfig
60
	 * @param \OCP\Notification\IManager $notificationManager
61
	 */
62
	public function __construct(Access $access, IConfig $ocConfig, INotificationManager $notificationManager) {
63
		parent::__construct($access);
64
		$this->ocConfig = $ocConfig;
65
		$this->notificationManager = $notificationManager;
66
	}
67
68
	/**
69
	 * checks whether the user is allowed to change his avatar in Nextcloud
70
	 * @param string $uid the Nextcloud user name
71
	 * @return boolean either the user can or cannot
72
	 */
73
	public function canChangeAvatar($uid) {
74
		$user = $this->access->userManager->get($uid);
75
		if(!$user instanceof User) {
76
			return false;
77
		}
78
		if($user->getAvatarImage() === false) {
79
			return true;
80
		}
81
82
		return false;
83
	}
84
85
	/**
86
	 * returns the username for the given login name, if available
87
	 *
88
	 * @param string $loginName
89
	 * @return string|false
90
	 */
91
	public function loginName2UserName($loginName) {
92
		$cacheKey = 'loginName2UserName-'.$loginName;
93
		$username = $this->access->connection->getFromCache($cacheKey);
94
		if(!is_null($username)) {
95
			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...
96
		}
97
98
		try {
99
			$ldapRecord = $this->getLDAPUserByLoginName($loginName);
100
			$user = $this->access->userManager->get($ldapRecord['dn'][0]);
101
			if($user instanceof OfflineUser) {
102
				// this path is not really possible, however get() is documented
103
				// to return User or OfflineUser so we are very defensive here.
104
				$this->access->connection->writeToCache($cacheKey, false);
105
				return false;
106
			}
107
			$username = $user->getUsername();
108
			$this->access->connection->writeToCache($cacheKey, $username);
109
			return $username;
110
		} catch (NotOnLDAP $e) {
111
			$this->access->connection->writeToCache($cacheKey, false);
112
			return false;
113
		}
114
	}
115
	
116
	/**
117
	 * returns the username for the given LDAP DN, if available
118
	 *
119
	 * @param string $dn
120
	 * @return string|false with the username
121
	 */
122
	public function dn2UserName($dn) {
123
		return $this->access->dn2username($dn);
124
	}
125
126
	/**
127
	 * returns an LDAP record based on a given login name
128
	 *
129
	 * @param string $loginName
130
	 * @return array
131
	 * @throws NotOnLDAP
132
	 */
133
	public function getLDAPUserByLoginName($loginName) {
134
		//find out dn of the user name
135
		$attrs = $this->access->userManager->getAttributes();
136
		$users = $this->access->fetchUsersByLoginName($loginName, $attrs);
137
		if(count($users) < 1) {
138
			throw new NotOnLDAP('No user available for the given login name on ' .
139
				$this->access->connection->ldapHost . ':' . $this->access->connection->ldapPort);
140
		}
141
		return $users[0];
142
	}
143
144
	/**
145
	 * Check if the password is correct without logging in the user
146
	 *
147
	 * @param string $uid The username
148
	 * @param string $password The password
149
	 * @return false|string
150
	 */
151
	public function checkPassword($uid, $password) {
152
		try {
153
			$ldapRecord = $this->getLDAPUserByLoginName($uid);
154
		} catch(NotOnLDAP $e) {
155
			if($this->ocConfig->getSystemValue('loglevel', Util::WARN) === Util::DEBUG) {
156
				\OC::$server->getLogger()->logException($e, ['app' => 'user_ldap']);
157
			}
158
			return false;
159
		}
160
		$dn = $ldapRecord['dn'][0];
161
		$user = $this->access->userManager->get($dn);
162
163
		if(!$user instanceof User) {
164
			Util::writeLog('user_ldap',
165
				'LDAP Login: Could not get user object for DN ' . $dn .
166
				'. Maybe the LDAP entry has no set display name attribute?',
167
				Util::WARN);
168
			return false;
169
		}
170
		if($user->getUsername() !== false) {
171
			//are the credentials OK?
172
			if(!$this->access->areCredentialsValid($dn, $password)) {
173
				return false;
174
			}
175
176
			$this->access->cacheUserExists($user->getUsername());
177
			$user->processAttributes($ldapRecord);
178
			$user->markLogin();
179
180
			return $user->getUsername();
181
		}
182
183
		return false;
184
	}
185
186
	/**
187
	 * Set password
188
	 * @param string $uid The username
189
	 * @param string $password The new password
190
	 * @return bool
191
	 */
192
	public function setPassword($uid, $password) {
193
		$user = $this->access->userManager->get($uid);
194
195
		if(!$user instanceof User) {
196
			throw new \Exception('LDAP setPassword: Could not get user object for uid ' . $uid .
197
				'. Maybe the LDAP entry has no set display name attribute?');
198
		}
199
		if($user->getUsername() !== false && $this->access->setPassword($user->getDN(), $password)) {
200
			$ldapDefaultPPolicyDN = $this->access->connection->ldapDefaultPPolicyDN;
0 ignored issues
show
Documentation introduced by
The property ldapDefaultPPolicyDN does not exist on object<OCA\User_LDAP\Connection>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write 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.");
        }
    }

}

Since the property has write access only, you can use the @property-write 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...
201
			$turnOnPasswordChange = $this->access->connection->turnOnPasswordChange;
202
			if (!empty($ldapDefaultPPolicyDN) && (intval($turnOnPasswordChange) === 1)) {
203
				//remove last password expiry warning if any
204
				$notification = $this->notificationManager->createNotification();
205
				$notification->setApp('user_ldap')
206
					->setUser($uid)
207
					->setObject('pwd_exp_warn', $uid)
208
				;
209
				$this->notificationManager->markProcessed($notification);
210
			}
211
			return true;
212
		}
213
214
		return false;
215
	}
216
217
	/**
218
	 * Get a list of all users
219
	 *
220
	 * @param string $search
221
	 * @param integer $limit
222
	 * @param integer $offset
223
	 * @return string[] an array of all uids
224
	 */
225
	public function getUsers($search = '', $limit = 10, $offset = 0) {
226
		$search = $this->access->escapeFilterPart($search, true);
227
		$cachekey = 'getUsers-'.$search.'-'.$limit.'-'.$offset;
228
229
		//check if users are cached, if so return
230
		$ldap_users = $this->access->connection->getFromCache($cachekey);
231
		if(!is_null($ldap_users)) {
232
			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...
233
		}
234
235
		// if we'd pass -1 to LDAP search, we'd end up in a Protocol
236
		// error. With a limit of 0, we get 0 results. So we pass null.
237
		if($limit <= 0) {
238
			$limit = null;
239
		}
240
		$filter = $this->access->combineFilterWithAnd(array(
241
			$this->access->connection->ldapUserFilter,
242
			$this->access->connection->ldapUserDisplayName . '=*',
243
			$this->access->getFilterPartForUserSearch($search)
244
		));
245
246
		Util::writeLog('user_ldap',
247
			'getUsers: Options: search '.$search.' limit '.$limit.' offset '.$offset.' Filter: '.$filter,
248
			Util::DEBUG);
249
		//do the search and translate results to owncloud names
250
		$ldap_users = $this->access->fetchListOfUsers(
251
			$filter,
252
			$this->access->userManager->getAttributes(true),
253
			$limit, $offset);
254
		$ldap_users = $this->access->nextcloudUserNames($ldap_users);
255
		Util::writeLog('user_ldap', 'getUsers: '.count($ldap_users). ' Users found', Util::DEBUG);
256
257
		$this->access->connection->writeToCache($cachekey, $ldap_users);
258
		return $ldap_users;
259
	}
260
261
	/**
262
	 * checks whether a user is still available on LDAP
263
	 *
264
	 * @param string|\OCA\User_LDAP\User\User $user either the Nextcloud user
265
	 * name or an instance of that user
266
	 * @return bool
267
	 * @throws \Exception
268
	 * @throws \OC\ServerNotAvailableException
269
	 */
270
	public function userExistsOnLDAP($user) {
271
		if(is_string($user)) {
272
			$user = $this->access->userManager->get($user);
273
		}
274
		if(is_null($user)) {
275
			return false;
276
		}
277
278
		$dn = $user->getDN();
279
		//check if user really still exists by reading its entry
280
		if(!is_array($this->access->readAttribute($dn, '', $this->access->connection->ldapUserFilter))) {
281
			$lcr = $this->access->connection->getConnectionResource();
282
			if(is_null($lcr)) {
283
				throw new \Exception('No LDAP Connection to server ' . $this->access->connection->ldapHost);
284
			}
285
286
			try {
287
				$uuid = $this->access->getUserMapper()->getUUIDByDN($dn);
288
				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...
289
					return false;
290
				}
291
				$newDn = $this->access->getUserDnByUuid($uuid);
292
				//check if renamed user is still valid by reapplying the ldap filter
293 View Code Duplication
				if(!is_array($this->access->readAttribute($newDn, '', $this->access->connection->ldapUserFilter))) {
294
					return false;
295
				}
296
				$this->access->getUserMapper()->setDNbyUUID($newDn, $uuid);
297
				return true;
298
			} catch (\Exception $e) {
299
				return false;
300
			}
301
		}
302
303
		if($user instanceof OfflineUser) {
304
			$user->unmark();
305
		}
306
307
		return true;
308
	}
309
310
	/**
311
	 * check if a user exists
312
	 * @param string $uid the username
313
	 * @return boolean
314
	 * @throws \Exception when connection could not be established
315
	 */
316
	public function userExists($uid) {
317
		$userExists = $this->access->connection->getFromCache('userExists'.$uid);
318
		if(!is_null($userExists)) {
319
			return (bool)$userExists;
320
		}
321
		//getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking.
322
		$user = $this->access->userManager->get($uid);
323
324
		if(is_null($user)) {
325
			Util::writeLog('user_ldap', 'No DN found for '.$uid.' on '.
326
				$this->access->connection->ldapHost, Util::DEBUG);
327
			$this->access->connection->writeToCache('userExists'.$uid, false);
328
			return false;
329
		} else if($user instanceof OfflineUser) {
330
			//express check for users marked as deleted. Returning true is
331
			//necessary for cleanup
332
			return true;
333
		}
334
335
		$result = $this->userExistsOnLDAP($user);
336
		$this->access->connection->writeToCache('userExists'.$uid, $result);
337
		if($result === true) {
338
			$user->update();
339
		}
340
		return $result;
341
	}
342
343
	/**
344
	* returns whether a user was deleted in LDAP
345
	*
346
	* @param string $uid The username of the user to delete
347
	* @return bool
348
	*/
349
	public function deleteUser($uid) {
350
		$marked = $this->ocConfig->getUserValue($uid, 'user_ldap', 'isDeleted', 0);
351 View Code Duplication
		if(intval($marked) === 0) {
352
			\OC::$server->getLogger()->notice(
353
				'User '.$uid . ' is not marked as deleted, not cleaning up.',
354
				array('app' => 'user_ldap'));
355
			return false;
356
		}
357
		\OC::$server->getLogger()->info('Cleaning up after user ' . $uid,
358
			array('app' => 'user_ldap'));
359
360
		//Get Home Directory out of user preferences so we can return it later,
361
		//necessary for removing directories as done by OC_User.
362
		$home = $this->ocConfig->getUserValue($uid, 'user_ldap', 'homePath', '');
363
		$this->homesToKill[$uid] = $home;
364
		$this->access->getUserMapper()->unmap($uid);
365
366
		return true;
367
	}
368
369
	/**
370
	 * get the user's home directory
371
	 *
372
	 * @param string $uid the username
373
	 * @return bool|string
374
	 * @throws NoUserException
375
	 * @throws \Exception
376
	 */
377
	public function getHome($uid) {
378
		if(isset($this->homesToKill[$uid]) && !empty($this->homesToKill[$uid])) {
379
			//a deleted user who needs some clean up
380
			return $this->homesToKill[$uid];
381
		}
382
383
		// user Exists check required as it is not done in user proxy!
384
		if(!$this->userExists($uid)) {
385
			return false;
386
		}
387
388
		$cacheKey = 'getHome'.$uid;
389
		$path = $this->access->connection->getFromCache($cacheKey);
390
		if(!is_null($path)) {
391
			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...
392
		}
393
394
		$user = $this->access->userManager->get($uid);
395
		if(is_null($user) || ($user instanceof OfflineUser && !$this->userExistsOnLDAP($user->getOCName()))) {
396
			throw new NoUserException($uid . ' is not a valid user anymore');
397
		}
398
		if($user instanceof OfflineUser) {
399
			// apparently this user survived the userExistsOnLDAP check,
400
			// we request the user instance again in order to retrieve a User
401
			// instance instead
402
			$user = $this->access->userManager->get($uid);
403
		}
404
		$path = $user->getHomePath();
405
		$this->access->cacheUserHome($uid, $path);
406
407
		return $path;
408
	}
409
410
	/**
411
	 * get display name of the user
412
	 * @param string $uid user ID of the user
413
	 * @return string|false display name
414
	 */
415
	public function getDisplayName($uid) {
416
		if(!$this->userExists($uid)) {
417
			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...
418
		}
419
420
		$cacheKey = 'getDisplayName'.$uid;
421
		if(!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) {
422
			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...
423
		}
424
425
		//Check whether the display name is configured to have a 2nd feature
426
		$additionalAttribute = $this->access->connection->ldapUserDisplayName2;
427
		$displayName2 = '';
428
		if ($additionalAttribute !== '') {
429
			$displayName2 = $this->access->readAttribute(
430
				$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...
431
				$additionalAttribute);
432
		}
433
434
		$displayName = $this->access->readAttribute(
435
			$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...
436
			$this->access->connection->ldapUserDisplayName);
437
438
		if($displayName && (count($displayName) > 0)) {
439
			$displayName = $displayName[0];
440
441
			if (is_array($displayName2)){
442
				$displayName2 = count($displayName2) > 0 ? $displayName2[0] : '';
443
			}
444
445
			$user = $this->access->userManager->get($uid);
446
			if ($user instanceof User) {
447
				$displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
448
				$this->access->connection->writeToCache($cacheKey, $displayName);
449
			}
450
			if ($user instanceof OfflineUser) {
451
				/** @var OfflineUser $user*/
452
				$displayName = $user->getDisplayName();
453
			}
454
			return $displayName;
455
		}
456
457
		return null;
458
	}
459
460
	/**
461
	 * Get a list of all display names
462
	 *
463
	 * @param string $search
464
	 * @param string|null $limit
465
	 * @param string|null $offset
466
	 * @return array an array of all displayNames (value) and the corresponding uids (key)
467
	 */
468
	public function getDisplayNames($search = '', $limit = null, $offset = null) {
469
		$cacheKey = 'getDisplayNames-'.$search.'-'.$limit.'-'.$offset;
470
		if(!is_null($displayNames = $this->access->connection->getFromCache($cacheKey))) {
471
			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...
472
		}
473
474
		$displayNames = array();
475
		$users = $this->getUsers($search, $limit, $offset);
476
		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...
477
			$displayNames[$user] = $this->getDisplayName($user);
478
		}
479
		$this->access->connection->writeToCache($cacheKey, $displayNames);
480
		return $displayNames;
481
	}
482
483
	/**
484
	* Check if backend implements actions
485
	* @param int $actions bitwise-or'ed actions
486
	* @return boolean
487
	*
488
	* Returns the supported actions as int to be
489
	* compared with OC_USER_BACKEND_CREATE_USER etc.
490
	*/
491
	public function implementsActions($actions) {
492
		return (bool)((Backend::CHECK_PASSWORD
493
			| Backend::GET_HOME
494
			| Backend::GET_DISPLAYNAME
495
			| Backend::PROVIDE_AVATAR
496
			| Backend::COUNT_USERS
497
			| ((intval($this->access->connection->turnOnPasswordChange) === 1)?(Backend::SET_PASSWORD):0))
498
			& $actions);
499
	}
500
501
	/**
502
	 * @return bool
503
	 */
504
	public function hasUserListings() {
505
		return true;
506
	}
507
508
	/**
509
	 * counts the users in LDAP
510
	 *
511
	 * @return int|bool
512
	 */
513
	public function countUsers() {
514
		$filter = $this->access->getFilterForUserCount();
515
		$cacheKey = 'countUsers-'.$filter;
516
		if(!is_null($entries = $this->access->connection->getFromCache($cacheKey))) {
517
			return $entries;
518
		}
519
		$entries = $this->access->countUsers($filter);
520
		$this->access->connection->writeToCache($cacheKey, $entries);
521
		return $entries;
522
	}
523
524
	/**
525
	 * Backend name to be shown in user management
526
	 * @return string the name of the backend to be shown
527
	 */
528
	public function getBackendName(){
529
		return 'LDAP';
530
	}
531
	
532
	/**
533
	 * Return access for LDAP interaction.
534
	 * @param string $uid
535
	 * @return Access instance of Access for LDAP interaction
536
	 */
537
	public function getLDAPAccess($uid) {
538
		return $this->access;
539
	}
540
	
541
	/**
542
	 * Return LDAP connection resource from a cloned connection.
543
	 * The cloned connection needs to be closed manually.
544
	 * of the current access.
545
	 * @param string $uid
546
	 * @return resource of the LDAP connection
547
	 */
548
	public function getNewLDAPConnection($uid) {
549
		$connection = clone $this->access->getConnection();
550
		return $connection->getConnectionResource();
551
	}
552
}
553