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

User::__construct()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 27
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 22
nc 3
nop 10
dl 0
loc 27
rs 8.8571
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Morris Jobke <[email protected]>
8
 * @author Thomas Müller <[email protected]>
9
 * @author Jörn Friedrich Dreyer <[email protected]>
10
 * @author Roger Szabo <[email protected]>
11
 *
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
namespace OCA\User_LDAP\User;
29
30
use OCA\User_LDAP\Connection;
31
use OCA\User_LDAP\FilesystemHelper;
32
use OCA\User_LDAP\LogWrapper;
33
use OCP\IAvatarManager;
34
use OCP\IConfig;
35
use OCP\Image;
36
use OCP\IUserManager;
37
use OCP\Util;
38
use OCP\Notification\IManager as INotificationManager;
39
40
/**
41
 * User
42
 *
43
 * represents an LDAP user, gets and holds user-specific information from LDAP
44
 */
45
class User {
46
	/**
47
	 * @var IUserTools
48
	 */
49
	protected $access;
50
	/**
51
	 * @var Connection
52
	 */
53
	protected $connection;
54
	/**
55
	 * @var IConfig
56
	 */
57
	protected $config;
58
	/**
59
	 * @var FilesystemHelper
60
	 */
61
	protected $fs;
62
	/**
63
	 * @var Image
64
	 */
65
	protected $image;
66
	/**
67
	 * @var LogWrapper
68
	 */
69
	protected $log;
70
	/**
71
	 * @var IAvatarManager
72
	 */
73
	protected $avatarManager;
74
	/**
75
	 * @var IUserManager
76
	 */
77
	protected $userManager;
78
	/**
79
	 * @var INotificationManager
80
	 */
81
	protected $notificationManager;
82
	/**
83
	 * @var string
84
	 */
85
	protected $dn;
86
	/**
87
	 * @var string
88
	 */
89
	protected $uid;
90
	/**
91
	 * @var string[]
92
	 */
93
	protected $refreshedFeatures = array();
94
	/**
95
	 * @var string
96
	 */
97
	protected $avatarImage;
98
99
	/**
100
	 * DB config keys for user preferences
101
	 */
102
	const USER_PREFKEY_FIRSTLOGIN  = 'firstLoginAccomplished';
103
	const USER_PREFKEY_LASTREFRESH = 'lastFeatureRefresh';
104
105
	/**
106
	 * @brief constructor, make sure the subclasses call this one!
107
	 * @param string $username the internal username
108
	 * @param string $dn the LDAP DN
109
	 * @param IUserTools $access an instance that implements IUserTools for
110
	 * LDAP interaction
111
	 * @param IConfig $config
112
	 * @param FilesystemHelper $fs
113
	 * @param Image $image any empty instance
114
	 * @param LogWrapper $log
115
	 * @param IAvatarManager $avatarManager
116
	 * @param IUserManager $userManager
117
	 * @param INotificationManager $notificationManager
118
	 */
119
	public function __construct($username, $dn, IUserTools $access,
120
		IConfig $config, FilesystemHelper $fs, Image $image,
121
		LogWrapper $log, IAvatarManager $avatarManager, IUserManager $userManager,
122
		INotificationManager $notificationManager) {
123
	
124
		if ($username === null) {
125
			$log->log("uid for '$dn' must not be null!", Util::ERROR);
126
			throw new \InvalidArgumentException('uid must not be null!');
127
		} else if ($username === '') {
128
			$log->log("uid for '$dn' must not be an empty string", Util::ERROR);
129
			throw new \InvalidArgumentException('uid must not be an empty string!');
130
		}
131
132
		$this->access              = $access;
133
		$this->connection          = $access->getConnection();
134
		$this->config              = $config;
135
		$this->fs                  = $fs;
136
		$this->dn                  = $dn;
137
		$this->uid                 = $username;
138
		$this->image               = $image;
139
		$this->log                 = $log;
140
		$this->avatarManager       = $avatarManager;
141
		$this->userManager         = $userManager;
142
		$this->notificationManager = $notificationManager;
143
144
		\OCP\Util::connectHook('OC_User', 'post_login', $this, 'handlePasswordExpiry');
145
	}
146
147
	/**
148
	 * @brief updates properties like email, quota or avatar provided by LDAP
149
	 * @return null
150
	 */
151
	public function update() {
152
		if(is_null($this->dn)) {
153
			return null;
154
		}
155
156
		$hasLoggedIn = $this->config->getUserValue($this->uid, 'user_ldap',
157
				self::USER_PREFKEY_FIRSTLOGIN, 0);
158
159
		if($this->needsRefresh()) {
160
			$this->updateEmail();
161
			$this->updateQuota();
162
			if($hasLoggedIn !== 0) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $hasLoggedIn (string) and 0 (integer) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
163
				//we do not need to try it, when the user has not been logged in
164
				//before, because the file system will not be ready.
165
				$this->updateAvatar();
166
				//in order to get an avatar as soon as possible, mark the user
167
				//as refreshed only when updating the avatar did happen
168
				$this->markRefreshTime();
169
			}
170
		}
171
	}
172
173
	/**
174
	 * processes results from LDAP for attributes as returned by getAttributesToRead()
175
	 * @param array $ldapEntry the user entry as retrieved from LDAP
176
	 */
177
	public function processAttributes($ldapEntry) {
178
		$this->markRefreshTime();
179
		//Quota
180
		$attr = strtolower($this->connection->ldapQuotaAttribute);
0 ignored issues
show
Documentation introduced by
The property ldapQuotaAttribute does not exist on object<OCA\User_LDAP\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...
181
		if(isset($ldapEntry[$attr])) {
182
			$this->updateQuota($ldapEntry[$attr][0]);
183
		} else {
184
			if ($this->connection->ldapQuotaDefault !== '') {
0 ignored issues
show
Documentation introduced by
The property ldapQuotaDefault does not exist on object<OCA\User_LDAP\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...
185
				$this->updateQuota();
186
			}
187
		}
188
		unset($attr);
189
190
		//Email
191
		$attr = strtolower($this->connection->ldapEmailAttribute);
0 ignored issues
show
Documentation introduced by
The property ldapEmailAttribute does not exist on object<OCA\User_LDAP\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...
192
		if(isset($ldapEntry[$attr])) {
193
			$this->updateEmail($ldapEntry[$attr][0]);
194
		}
195
		unset($attr);
196
197
		//displayName
198
		$displayName = $displayName2 = '';
199
		$attr = strtolower($this->connection->ldapUserDisplayName);
200
		if(isset($ldapEntry[$attr])) {
201
			$displayName = strval($ldapEntry[$attr][0]);
202
		}
203
		$attr = strtolower($this->connection->ldapUserDisplayName2);
204
		if(isset($ldapEntry[$attr])) {
205
			$displayName2 = strval($ldapEntry[$attr][0]);
206
		}
207
		if ($displayName !== '') {
208
			$this->composeAndStoreDisplayName($displayName);
209
			$this->access->cacheUserDisplayName(
210
				$this->getUsername(),
211
				$displayName,
212
				$displayName2
213
			);
214
		}
215
		unset($attr);
216
217
		// LDAP Username, needed for s2s sharing
218
		if(isset($ldapEntry['uid'])) {
219
			$this->storeLDAPUserName($ldapEntry['uid'][0]);
220
		} else if(isset($ldapEntry['samaccountname'])) {
221
			$this->storeLDAPUserName($ldapEntry['samaccountname'][0]);
222
		}
223
224
		//homePath
225
		if(strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) {
0 ignored issues
show
Documentation introduced by
The property homeFolderNamingRule does not exist on object<OCA\User_LDAP\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...
226
			$attr = strtolower(substr($this->connection->homeFolderNamingRule, strlen('attr:')));
0 ignored issues
show
Documentation introduced by
The property homeFolderNamingRule does not exist on object<OCA\User_LDAP\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...
227
			if(isset($ldapEntry[$attr])) {
228
				$this->access->cacheUserHome(
229
					$this->getUsername(), $this->getHomePath($ldapEntry[$attr][0]));
230
			}
231
		}
232
233
		//memberOf groups
234
		$cacheKey = 'getMemberOf'.$this->getUsername();
235
		$groups = false;
236
		if(isset($ldapEntry['memberof'])) {
237
			$groups = $ldapEntry['memberof'];
238
		}
239
		$this->connection->writeToCache($cacheKey, $groups);
240
241
		//Avatar
242
		$attrs = array('jpegphoto', 'thumbnailphoto');
243
		foreach ($attrs as $attr)  {
244
			if(isset($ldapEntry[$attr])) {
245
				$this->avatarImage = $ldapEntry[$attr][0];
246
				// the call to the method that saves the avatar in the file
247
				// system must be postponed after the login. It is to ensure
248
				// external mounts are mounted properly (e.g. with login
249
				// credentials from the session).
250
				\OCP\Util::connectHook('OC_User', 'post_login', $this, 'updateAvatarPostLogin');
251
				break;
252
			}
253
		}
254
	}
255
256
	/**
257
	 * @brief returns the LDAP DN of the user
258
	 * @return string
259
	 */
260
	public function getDN() {
261
		return $this->dn;
262
	}
263
264
	/**
265
	 * @brief returns the Nextcloud internal username of the user
266
	 * @return string
267
	 */
268
	public function getUsername() {
269
		return $this->uid;
270
	}
271
272
	/**
273
	 * returns the home directory of the user if specified by LDAP settings
274
	 * @param string $valueFromLDAP
275
	 * @return bool|string
276
	 * @throws \Exception
277
	 */
278
	public function getHomePath($valueFromLDAP = null) {
279
		$path = strval($valueFromLDAP);
280
		$attr = null;
281
282
		if (is_null($valueFromLDAP)
283
		   && strpos($this->access->connection->homeFolderNamingRule, 'attr:') === 0
0 ignored issues
show
Bug introduced by
Accessing connection on the interface OCA\User_LDAP\User\IUserTools suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
284
		   && $this->access->connection->homeFolderNamingRule !== 'attr:')
0 ignored issues
show
Bug introduced by
Accessing connection on the interface OCA\User_LDAP\User\IUserTools suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
285
		{
286
			$attr = substr($this->access->connection->homeFolderNamingRule, strlen('attr:'));
0 ignored issues
show
Bug introduced by
Accessing connection on the interface OCA\User_LDAP\User\IUserTools suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
287
			$homedir = $this->access->readAttribute(
288
				$this->access->username2dn($this->getUsername()), $attr);
289
			if ($homedir && isset($homedir[0])) {
290
				$path = $homedir[0];
291
			}
292
		}
293
294
		if ($path !== '') {
295
			//if attribute's value is an absolute path take this, otherwise append it to data dir
296
			//check for / at the beginning or pattern c:\ resp. c:/
297
			if(   '/' !== $path[0]
298
			   && !(3 < strlen($path) && ctype_alpha($path[0])
299
			       && $path[1] === ':' && ('\\' === $path[2] || '/' === $path[2]))
300
			) {
301
				$path = $this->config->getSystemValue('datadirectory',
302
						\OC::$SERVERROOT.'/data' ) . '/' . $path;
303
			}
304
			//we need it to store it in the DB as well in case a user gets
305
			//deleted so we can clean up afterwards
306
			$this->config->setUserValue(
307
				$this->getUsername(), 'user_ldap', 'homePath', $path
308
			);
309
			return $path;
310
		}
311
312
		if(    !is_null($attr)
313
			&& $this->config->getAppValue('user_ldap', 'enforce_home_folder_naming_rule', true)
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
314
		) {
315
			// a naming rule attribute is defined, but it doesn't exist for that LDAP user
316
			throw new \Exception('Home dir attribute can\'t be read from LDAP for uid: ' . $this->getUsername());
317
		}
318
319
		//false will apply default behaviour as defined and done by OC_User
320
		$this->config->setUserValue($this->getUsername(), 'user_ldap', 'homePath', '');
321
		return false;
322
	}
323
324
	public function getMemberOfGroups() {
325
		$cacheKey = 'getMemberOf'.$this->getUsername();
326
		$memberOfGroups = $this->connection->getFromCache($cacheKey);
327
		if(!is_null($memberOfGroups)) {
328
			return $memberOfGroups;
329
		}
330
		$groupDNs = $this->access->readAttribute($this->getDN(), 'memberOf');
331
		$this->connection->writeToCache($cacheKey, $groupDNs);
332
		return $groupDNs;
333
	}
334
335
	/**
336
	 * @brief reads the image from LDAP that shall be used as Avatar
337
	 * @return string data (provided by LDAP) | false
338
	 */
339
	public function getAvatarImage() {
340
		if(!is_null($this->avatarImage)) {
341
			return $this->avatarImage;
342
		}
343
344
		$this->avatarImage = false;
0 ignored issues
show
Documentation Bug introduced by
The property $avatarImage was declared of type string, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
345
		$attributes = array('jpegPhoto', 'thumbnailPhoto');
346
		foreach($attributes as $attribute) {
347
			$result = $this->access->readAttribute($this->dn, $attribute);
348
			if($result !== false && is_array($result) && isset($result[0])) {
349
				$this->avatarImage = $result[0];
350
				break;
351
			}
352
		}
353
354
		return $this->avatarImage;
355
	}
356
357
	/**
358
	 * @brief marks the user as having logged in at least once
359
	 * @return null
360
	 */
361
	public function markLogin() {
362
		$this->config->setUserValue(
363
			$this->uid, 'user_ldap', self::USER_PREFKEY_FIRSTLOGIN, 1);
364
	}
365
366
	/**
367
	 * @brief marks the time when user features like email have been updated
368
	 * @return null
369
	 */
370
	public function markRefreshTime() {
371
		$this->config->setUserValue(
372
			$this->uid, 'user_ldap', self::USER_PREFKEY_LASTREFRESH, time());
373
	}
374
375
	/**
376
	 * @brief checks whether user features needs to be updated again by
377
	 * comparing the difference of time of the last refresh to now with the
378
	 * desired interval
379
	 * @return bool
380
	 */
381
	private function needsRefresh() {
382
		$lastChecked = $this->config->getUserValue($this->uid, 'user_ldap',
383
			self::USER_PREFKEY_LASTREFRESH, 0);
384
385
		//TODO make interval configurable
386
		if((time() - intval($lastChecked)) < 86400 ) {
387
			return false;
388
		}
389
		return  true;
390
	}
391
392
	/**
393
	 * Stores a key-value pair in relation to this user
394
	 *
395
	 * @param string $key
396
	 * @param string $value
397
	 */
398
	private function store($key, $value) {
399
		$this->config->setUserValue($this->uid, 'user_ldap', $key, $value);
400
	}
401
402
	/**
403
	 * Composes the display name and stores it in the database. The final
404
	 * display name is returned.
405
	 *
406
	 * @param string $displayName
407
	 * @param string $displayName2
408
	 * @returns string the effective display name
409
	 */
410
	public function composeAndStoreDisplayName($displayName, $displayName2 = '') {
411
		$displayName2 = strval($displayName2);
412
		if($displayName2 !== '') {
413
			$displayName .= ' (' . $displayName2 . ')';
414
		}
415
		$this->store('displayName', $displayName);
416
		return $displayName;
417
	}
418
419
	/**
420
	 * Stores the LDAP Username in the Database
421
	 * @param string $userName
422
	 */
423
	public function storeLDAPUserName($userName) {
424
		$this->store('uid', $userName);
425
	}
426
427
	/**
428
	 * @brief checks whether an update method specified by feature was run
429
	 * already. If not, it will marked like this, because it is expected that
430
	 * the method will be run, when false is returned.
431
	 * @param string $feature email | quota | avatar (can be extended)
432
	 * @return bool
433
	 */
434
	private function wasRefreshed($feature) {
435
		if(isset($this->refreshedFeatures[$feature])) {
436
			return true;
437
		}
438
		$this->refreshedFeatures[$feature] = 1;
439
		return false;
440
	}
441
442
	/**
443
	 * fetches the email from LDAP and stores it as Nextcloud user value
444
	 * @param string $valueFromLDAP if known, to save an LDAP read request
445
	 * @return null
446
	 */
447
	public function updateEmail($valueFromLDAP = null) {
448
		if($this->wasRefreshed('email')) {
449
			return;
450
		}
451
		$email = strval($valueFromLDAP);
452
		if(is_null($valueFromLDAP)) {
453
			$emailAttribute = $this->connection->ldapEmailAttribute;
0 ignored issues
show
Documentation introduced by
The property ldapEmailAttribute 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...
454
			if ($emailAttribute !== '') {
455
				$aEmail = $this->access->readAttribute($this->dn, $emailAttribute);
456
				if(is_array($aEmail) && (count($aEmail) > 0)) {
457
					$email = strval($aEmail[0]);
458
				}
459
			}
460
		}
461
		if ($email !== '') {
462
			$user = $this->userManager->get($this->uid);
463
			if (!is_null($user)) {
464
				$currentEmail = strval($user->getEMailAddress());
465
				if ($currentEmail !== $email) {
466
					$user->setEMailAddress($email);
467
				}
468
			}
469
		}
470
	}
471
472
	/**
473
	 * Overall process goes as follow:
474
	 * 1. fetch the quota from LDAP and check if it's parseable with the "verifyQuotaValue" function
475
	 * 2. if the value can't be fetched, is empty or not parseable, use the default LDAP quota
476
	 * 3. if the default LDAP quota can't be parsed, use the Nextcloud's default quota (use 'default')
477
	 * 4. check if the target user exists and set the quota for the user.
478
	 *
479
	 * In order to improve performance and prevent an unwanted extra LDAP call, the $valueFromLDAP
480
	 * parameter can be passed with the value of the attribute. This value will be considered as the
481
	 * quota for the user coming from the LDAP server (step 1 of the process) It can be useful to
482
	 * fetch all the user's attributes in one call and use the fetched values in this function.
483
	 * The expected value for that parameter is a string describing the quota for the user. Valid
484
	 * values are 'none' (unlimited), 'default' (the Nextcloud's default quota), '1234' (quota in
485
	 * bytes), '1234 MB' (quota in MB - check the \OC_Helper::computerFileSize method for more info)
486
	 *
487
	 * fetches the quota from LDAP and stores it as Nextcloud user value
488
	 * @param string $valueFromLDAP the quota attribute's value can be passed,
489
	 * to save the readAttribute request
490
	 * @return null
491
	 */
492
	public function updateQuota($valueFromLDAP = null) {
493
		if($this->wasRefreshed('quota')) {
494
			return;
495
		}
496
497
		$quota = false;
498
		if(is_null($valueFromLDAP)) {
499
			$quotaAttribute = $this->connection->ldapQuotaAttribute;
0 ignored issues
show
Documentation introduced by
The property ldapQuotaAttribute 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...
500
			if ($quotaAttribute !== '') {
501
				$aQuota = $this->access->readAttribute($this->dn, $quotaAttribute);
502
				if($aQuota && (count($aQuota) > 0)) {
503
					if ($this->verifyQuotaValue($aQuota[0])) {
504
						$quota = $aQuota[0];
505
					} else {
506
						$this->log->log('not suitable LDAP quota found for user ' . $this->uid . ': [' . $aQuota[0] . ']', \OCP\Util::WARN);
507
					}
508
				}
509
			}
510
		} else {
511
			if ($this->verifyQuotaValue($valueFromLDAP)) {
512
				$quota = $valueFromLDAP;
513
			} else {
514
				$this->log->log('not suitable LDAP quota found for user ' . $this->uid . ': [' . $valueFromLDAP . ']', \OCP\Util::WARN);
515
			}
516
		}
517
518
		if ($quota === false) {
519
			// quota not found using the LDAP attribute (or not parseable). Try the default quota
520
			$defaultQuota = $this->connection->ldapQuotaDefault;
0 ignored issues
show
Documentation introduced by
The property ldapQuotaDefault 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...
521
			if ($this->verifyQuotaValue($defaultQuota)) {
522
				$quota = $defaultQuota;
523
			}
524
		}
525
526
		$targetUser = $this->userManager->get($this->uid);
527
		if ($targetUser) {
528
			if($quota !== false) {
529
				$targetUser->setQuota($quota);
530
			} else {
531
				$this->log->log('not suitable default quota found for user ' . $this->uid . ': [' . $defaultQuota . ']', \OCP\Util::WARN);
0 ignored issues
show
Bug introduced by
The variable $defaultQuota does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
532
				$targetUser->setQuota('default');
533
			}
534
		} else {
535
			$this->log->log('trying to set a quota for user ' . $this->uid . ' but the user is missing', \OCP\Util::ERROR);
536
		}
537
	}
538
539
	private function verifyQuotaValue($quotaValue) {
540
		return $quotaValue === 'none' || $quotaValue === 'default' || \OC_Helper::computerFileSize($quotaValue) !== false;
541
	}
542
543
	/**
544
	 * called by a post_login hook to save the avatar picture
545
	 *
546
	 * @param array $params
547
	 */
548
	public function updateAvatarPostLogin($params) {
549
		if(isset($params['uid']) && $params['uid'] === $this->getUsername()) {
550
			$this->updateAvatar();
551
		}
552
	}
553
554
	/**
555
	 * @brief attempts to get an image from LDAP and sets it as Nextcloud avatar
556
	 * @return null
557
	 */
558
	public function updateAvatar() {
559
		if($this->wasRefreshed('avatar')) {
560
			return;
561
		}
562
		$avatarImage = $this->getAvatarImage();
563
		if($avatarImage === false) {
564
			//not set, nothing left to do;
565
			return;
566
		}
567
		$this->image->loadFromBase64(base64_encode($avatarImage));
568
		$this->setOwnCloudAvatar();
569
	}
570
571
	/**
572
	 * @brief sets an image as Nextcloud avatar
573
	 * @return null
574
	 */
575
	private function setOwnCloudAvatar() {
576
		if(!$this->image->valid()) {
577
			$this->log->log('jpegPhoto data invalid for '.$this->dn, \OCP\Util::ERROR);
578
			return;
579
		}
580
		//make sure it is a square and not bigger than 128x128
581
		$size = min(array($this->image->width(), $this->image->height(), 128));
582
		if(!$this->image->centerCrop($size)) {
583
			$this->log->log('croping image for avatar failed for '.$this->dn, \OCP\Util::ERROR);
584
			return;
585
		}
586
587
		if(!$this->fs->isLoaded()) {
588
			$this->fs->setup($this->uid);
589
		}
590
591
		try {
592
			$avatar = $this->avatarManager->getAvatar($this->uid);
593
			$avatar->set($this->image);
594
		} catch (\Exception $e) {
595
			\OC::$server->getLogger()->notice(
596
				'Could not set avatar for ' . $this->dn	. ', because: ' . $e->getMessage(),
597
				['app' => 'user_ldap']);
598
		}
599
	}
600
601
	/**
602
	 * called by a post_login hook to handle password expiry
603
	 *
604
	 * @param array $params
605
	 */
606
	public function handlePasswordExpiry($params) {
607
		$ppolicyDN = $this->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...
608
		if (empty($ppolicyDN) || (intval($this->connection->turnOnPasswordChange) !== 1)) {
609
			return;//password expiry handling disabled
610
		}
611
		$uid = $params['uid'];
612
		if(isset($uid) && $uid === $this->getUsername()) {
613
			//retrieve relevant user attributes
614
			$result = $this->access->search('objectclass=*', $this->dn, ['pwdpolicysubentry', 'pwdgraceusetime', 'pwdreset', 'pwdchangedtime']);
615
			
616
			if(array_key_exists('pwdpolicysubentry', $result[0])) {
617
				$pwdPolicySubentry = $result[0]['pwdpolicysubentry'];
618
				if($pwdPolicySubentry && (count($pwdPolicySubentry) > 0)){
619
					$ppolicyDN = $pwdPolicySubentry[0];//custom ppolicy DN
620
				}
621
			}
622
			
623
			$pwdGraceUseTime = array_key_exists('pwdgraceusetime', $result[0]) ? $result[0]['pwdgraceusetime'] : null;
624
			$pwdReset = array_key_exists('pwdreset', $result[0]) ? $result[0]['pwdreset'] : null;
625
			$pwdChangedTime = array_key_exists('pwdchangedtime', $result[0]) ? $result[0]['pwdchangedtime'] : null;
626
			
627
			//retrieve relevant password policy attributes
628
			$cacheKey = 'ppolicyAttributes' . $ppolicyDN;
629
			$result = $this->connection->getFromCache($cacheKey);
630
			if(is_null($result)) {
631
				$result = $this->access->search('objectclass=*', $ppolicyDN, ['pwdgraceauthnlimit', 'pwdmaxage', 'pwdexpirewarning']);
632
				$this->connection->writeToCache($cacheKey, $result);
633
			}
634
			
635
			$pwdGraceAuthNLimit = array_key_exists('pwdgraceauthnlimit', $result[0]) ? $result[0]['pwdgraceauthnlimit'] : null;
636
			$pwdMaxAge = array_key_exists('pwdmaxage', $result[0]) ? $result[0]['pwdmaxage'] : null;
637
			$pwdExpireWarning = array_key_exists('pwdexpirewarning', $result[0]) ? $result[0]['pwdexpirewarning'] : null;
638
			
639
			//handle grace login
640
			$pwdGraceUseTimeCount = count($pwdGraceUseTime);
641
			if($pwdGraceUseTime && $pwdGraceUseTimeCount > 0) { //was this a grace login?
642
				if($pwdGraceAuthNLimit 
643
					&& (count($pwdGraceAuthNLimit) > 0)
644
					&&($pwdGraceUseTimeCount < intval($pwdGraceAuthNLimit[0]))) { //at least one more grace login available?
645
					$this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
646
					header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
647
					'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid)));
648
				} else { //no more grace login available
649
					header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
650
					'user_ldap.renewPassword.showLoginFormInvalidPassword', array('user' => $uid)));
651
				}
652
				exit();
653
			}
654
			//handle pwdReset attribute
655
			if($pwdReset && (count($pwdReset) > 0) && $pwdReset[0] === 'TRUE') { //user must change his password
656
				$this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
657
				header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
658
				'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid)));
659
				exit();
660
			}
661
			//handle password expiry warning
662
			if($pwdChangedTime && (count($pwdChangedTime) > 0)) {
663
				if($pwdMaxAge && (count($pwdMaxAge) > 0)
664
					&& $pwdExpireWarning && (count($pwdExpireWarning) > 0)) {
665
					$pwdMaxAgeInt = intval($pwdMaxAge[0]);
666
					$pwdExpireWarningInt = intval($pwdExpireWarning[0]);
667
					if($pwdMaxAgeInt > 0 && $pwdExpireWarningInt > 0){
668
						$pwdChangedTimeDt = \DateTime::createFromFormat('YmdHisZ', $pwdChangedTime[0]);
669
						$pwdChangedTimeDt->add(new \DateInterval('PT'.$pwdMaxAgeInt.'S'));
670
						$currentDateTime = new \DateTime();
671
						$secondsToExpiry = $pwdChangedTimeDt->getTimestamp() - $currentDateTime->getTimestamp();
672
						if($secondsToExpiry <= $pwdExpireWarningInt) {
673
							//remove last password expiry warning if any
674
							$notification = $this->notificationManager->createNotification();
675
							$notification->setApp('user_ldap')
676
								->setUser($uid)
677
								->setObject('pwd_exp_warn', $uid)
678
							;
679
							$this->notificationManager->markProcessed($notification);
680
							//create new password expiry warning
681
							$notification = $this->notificationManager->createNotification();
682
							$notification->setApp('user_ldap')
683
								->setUser($uid)
684
								->setDateTime($currentDateTime)
685
								->setObject('pwd_exp_warn', $uid) 
686
								->setSubject('pwd_exp_warn_days', [strval(ceil($secondsToExpiry / 60 / 60 / 24))])
687
							;
688
							$this->notificationManager->notify($notification);
689
						}
690
					}
691
				}
692
			}
693
		}
694
	}
695
}
696