Passed
Push — master ( 25ab12...62403d )
by Blizzz
12:17 queued 22s
created
apps/user_ldap/lib/User/User.php 2 patches
Indentation   +741 added lines, -741 removed lines patch added patch discarded remove patch
@@ -49,745 +49,745 @@
 block discarded – undo
49 49
  * represents an LDAP user, gets and holds user-specific information from LDAP
50 50
  */
51 51
 class User {
52
-	/**
53
-	 * @var Access
54
-	 */
55
-	protected $access;
56
-	/**
57
-	 * @var Connection
58
-	 */
59
-	protected $connection;
60
-	/**
61
-	 * @var IConfig
62
-	 */
63
-	protected $config;
64
-	/**
65
-	 * @var FilesystemHelper
66
-	 */
67
-	protected $fs;
68
-	/**
69
-	 * @var Image
70
-	 */
71
-	protected $image;
72
-	/**
73
-	 * @var LogWrapper
74
-	 */
75
-	protected $log;
76
-	/**
77
-	 * @var IAvatarManager
78
-	 */
79
-	protected $avatarManager;
80
-	/**
81
-	 * @var IUserManager
82
-	 */
83
-	protected $userManager;
84
-	/**
85
-	 * @var INotificationManager
86
-	 */
87
-	protected $notificationManager;
88
-	/**
89
-	 * @var string
90
-	 */
91
-	protected $dn;
92
-	/**
93
-	 * @var string
94
-	 */
95
-	protected $uid;
96
-	/**
97
-	 * @var string[]
98
-	 */
99
-	protected $refreshedFeatures = array();
100
-	/**
101
-	 * @var string
102
-	 */
103
-	protected $avatarImage;
104
-
105
-	/**
106
-	 * DB config keys for user preferences
107
-	 */
108
-	const USER_PREFKEY_FIRSTLOGIN  = 'firstLoginAccomplished';
109
-	const USER_PREFKEY_LASTREFRESH = 'lastFeatureRefresh';
110
-
111
-	/**
112
-	 * @brief constructor, make sure the subclasses call this one!
113
-	 * @param string $username the internal username
114
-	 * @param string $dn the LDAP DN
115
-	 * @param Access $access
116
-	 * @param IConfig $config
117
-	 * @param FilesystemHelper $fs
118
-	 * @param Image $image any empty instance
119
-	 * @param LogWrapper $log
120
-	 * @param IAvatarManager $avatarManager
121
-	 * @param IUserManager $userManager
122
-	 * @param INotificationManager $notificationManager
123
-	 */
124
-	public function __construct($username, $dn, Access $access,
125
-		IConfig $config, FilesystemHelper $fs, Image $image,
126
-		LogWrapper $log, IAvatarManager $avatarManager, IUserManager $userManager,
127
-		INotificationManager $notificationManager) {
128
-
129
-		if ($username === null) {
130
-			$log->log("uid for '$dn' must not be null!", ILogger::ERROR);
131
-			throw new \InvalidArgumentException('uid must not be null!');
132
-		} else if ($username === '') {
133
-			$log->log("uid for '$dn' must not be an empty string", ILogger::ERROR);
134
-			throw new \InvalidArgumentException('uid must not be an empty string!');
135
-		}
136
-
137
-		$this->access              = $access;
138
-		$this->connection          = $access->getConnection();
139
-		$this->config              = $config;
140
-		$this->fs                  = $fs;
141
-		$this->dn                  = $dn;
142
-		$this->uid                 = $username;
143
-		$this->image               = $image;
144
-		$this->log                 = $log;
145
-		$this->avatarManager       = $avatarManager;
146
-		$this->userManager         = $userManager;
147
-		$this->notificationManager = $notificationManager;
148
-
149
-		\OCP\Util::connectHook('OC_User', 'post_login', $this, 'handlePasswordExpiry');
150
-	}
151
-
152
-	/**
153
-	 * @brief updates properties like email, quota or avatar provided by LDAP
154
-	 * @return null
155
-	 */
156
-	public function update() {
157
-		if(is_null($this->dn)) {
158
-			return null;
159
-		}
160
-
161
-		$hasLoggedIn = $this->config->getUserValue($this->uid, 'user_ldap',
162
-				self::USER_PREFKEY_FIRSTLOGIN, 0);
163
-
164
-		if($this->needsRefresh()) {
165
-			$this->updateEmail();
166
-			$this->updateQuota();
167
-			if($hasLoggedIn !== 0) {
168
-				//we do not need to try it, when the user has not been logged in
169
-				//before, because the file system will not be ready.
170
-				$this->updateAvatar();
171
-				//in order to get an avatar as soon as possible, mark the user
172
-				//as refreshed only when updating the avatar did happen
173
-				$this->markRefreshTime();
174
-			}
175
-		}
176
-	}
177
-
178
-	/**
179
-	 * marks a user as deleted
180
-	 *
181
-	 * @throws \OCP\PreConditionNotMetException
182
-	 */
183
-	public function markUser() {
184
-		$curValue = $this->config->getUserValue($this->getUsername(), 'user_ldap', 'isDeleted', '0');
185
-		if($curValue === '1') {
186
-			// the user is already marked, do not write to DB again
187
-			return;
188
-		}
189
-		$this->config->setUserValue($this->getUsername(), 'user_ldap', 'isDeleted', '1');
190
-		$this->config->setUserValue($this->getUsername(), 'user_ldap', 'foundDeleted', (string)time());
191
-	}
192
-
193
-	/**
194
-	 * processes results from LDAP for attributes as returned by getAttributesToRead()
195
-	 * @param array $ldapEntry the user entry as retrieved from LDAP
196
-	 */
197
-	public function processAttributes($ldapEntry) {
198
-		$this->markRefreshTime();
199
-		//Quota
200
-		$attr = strtolower($this->connection->ldapQuotaAttribute);
201
-		if(isset($ldapEntry[$attr])) {
202
-			$this->updateQuota($ldapEntry[$attr][0]);
203
-		} else {
204
-			if ($this->connection->ldapQuotaDefault !== '') {
205
-				$this->updateQuota();
206
-			}
207
-		}
208
-		unset($attr);
209
-
210
-		//displayName
211
-		$displayName = $displayName2 = '';
212
-		$attr = strtolower($this->connection->ldapUserDisplayName);
213
-		if(isset($ldapEntry[$attr])) {
214
-			$displayName = (string)$ldapEntry[$attr][0];
215
-		}
216
-		$attr = strtolower($this->connection->ldapUserDisplayName2);
217
-		if(isset($ldapEntry[$attr])) {
218
-			$displayName2 = (string)$ldapEntry[$attr][0];
219
-		}
220
-		if ($displayName !== '') {
221
-			$this->composeAndStoreDisplayName($displayName, $displayName2);
222
-			$this->access->cacheUserDisplayName(
223
-				$this->getUsername(),
224
-				$displayName,
225
-				$displayName2
226
-			);
227
-		}
228
-		unset($attr);
229
-
230
-		//Email
231
-		//email must be stored after displayname, because it would cause a user
232
-		//change event that will trigger fetching the display name again
233
-		$attr = strtolower($this->connection->ldapEmailAttribute);
234
-		if(isset($ldapEntry[$attr])) {
235
-			$this->updateEmail($ldapEntry[$attr][0]);
236
-		}
237
-		unset($attr);
238
-
239
-		// LDAP Username, needed for s2s sharing
240
-		if(isset($ldapEntry['uid'])) {
241
-			$this->storeLDAPUserName($ldapEntry['uid'][0]);
242
-		} else if(isset($ldapEntry['samaccountname'])) {
243
-			$this->storeLDAPUserName($ldapEntry['samaccountname'][0]);
244
-		}
245
-
246
-		//homePath
247
-		if(strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) {
248
-			$attr = strtolower(substr($this->connection->homeFolderNamingRule, strlen('attr:')));
249
-			if(isset($ldapEntry[$attr])) {
250
-				$this->access->cacheUserHome(
251
-					$this->getUsername(), $this->getHomePath($ldapEntry[$attr][0]));
252
-			}
253
-		}
254
-
255
-		//memberOf groups
256
-		$cacheKey = 'getMemberOf'.$this->getUsername();
257
-		$groups = false;
258
-		if(isset($ldapEntry['memberof'])) {
259
-			$groups = $ldapEntry['memberof'];
260
-		}
261
-		$this->connection->writeToCache($cacheKey, $groups);
262
-
263
-		//external storage var
264
-		$attr = strtolower($this->connection->ldapExtStorageHomeAttribute);
265
-		if(isset($ldapEntry[$attr])) {
266
-			$this->updateExtStorageHome($ldapEntry[$attr][0]);
267
-		}
268
-		unset($attr);
269
-
270
-		//Avatar
271
-		/** @var Connection $connection */
272
-		$connection = $this->access->getConnection();
273
-		$attributes = $connection->resolveRule('avatar');
274
-		foreach ($attributes as $attribute)  {
275
-			if(isset($ldapEntry[$attribute])) {
276
-				$this->avatarImage = $ldapEntry[$attribute][0];
277
-				// the call to the method that saves the avatar in the file
278
-				// system must be postponed after the login. It is to ensure
279
-				// external mounts are mounted properly (e.g. with login
280
-				// credentials from the session).
281
-				\OCP\Util::connectHook('OC_User', 'post_login', $this, 'updateAvatarPostLogin');
282
-				break;
283
-			}
284
-		}
285
-	}
286
-
287
-	/**
288
-	 * @brief returns the LDAP DN of the user
289
-	 * @return string
290
-	 */
291
-	public function getDN() {
292
-		return $this->dn;
293
-	}
294
-
295
-	/**
296
-	 * @brief returns the Nextcloud internal username of the user
297
-	 * @return string
298
-	 */
299
-	public function getUsername() {
300
-		return $this->uid;
301
-	}
302
-
303
-	/**
304
-	 * returns the home directory of the user if specified by LDAP settings
305
-	 * @param string $valueFromLDAP
306
-	 * @return bool|string
307
-	 * @throws \Exception
308
-	 */
309
-	public function getHomePath($valueFromLDAP = null) {
310
-		$path = (string)$valueFromLDAP;
311
-		$attr = null;
312
-
313
-		if (is_null($valueFromLDAP)
314
-		   && strpos($this->access->connection->homeFolderNamingRule, 'attr:') === 0
315
-		   && $this->access->connection->homeFolderNamingRule !== 'attr:')
316
-		{
317
-			$attr = substr($this->access->connection->homeFolderNamingRule, strlen('attr:'));
318
-			$homedir = $this->access->readAttribute(
319
-				$this->access->username2dn($this->getUsername()), $attr);
320
-			if ($homedir && isset($homedir[0])) {
321
-				$path = $homedir[0];
322
-			}
323
-		}
324
-
325
-		if ($path !== '') {
326
-			//if attribute's value is an absolute path take this, otherwise append it to data dir
327
-			//check for / at the beginning or pattern c:\ resp. c:/
328
-			if(   '/' !== $path[0]
329
-			   && !(3 < strlen($path) && ctype_alpha($path[0])
330
-			       && $path[1] === ':' && ('\\' === $path[2] || '/' === $path[2]))
331
-			) {
332
-				$path = $this->config->getSystemValue('datadirectory',
333
-						\OC::$SERVERROOT.'/data' ) . '/' . $path;
334
-			}
335
-			//we need it to store it in the DB as well in case a user gets
336
-			//deleted so we can clean up afterwards
337
-			$this->config->setUserValue(
338
-				$this->getUsername(), 'user_ldap', 'homePath', $path
339
-			);
340
-			return $path;
341
-		}
342
-
343
-		if(    !is_null($attr)
344
-			&& $this->config->getAppValue('user_ldap', 'enforce_home_folder_naming_rule', true)
345
-		) {
346
-			// a naming rule attribute is defined, but it doesn't exist for that LDAP user
347
-			throw new \Exception('Home dir attribute can\'t be read from LDAP for uid: ' . $this->getUsername());
348
-		}
349
-
350
-		//false will apply default behaviour as defined and done by OC_User
351
-		$this->config->setUserValue($this->getUsername(), 'user_ldap', 'homePath', '');
352
-		return false;
353
-	}
354
-
355
-	public function getMemberOfGroups() {
356
-		$cacheKey = 'getMemberOf'.$this->getUsername();
357
-		$memberOfGroups = $this->connection->getFromCache($cacheKey);
358
-		if(!is_null($memberOfGroups)) {
359
-			return $memberOfGroups;
360
-		}
361
-		$groupDNs = $this->access->readAttribute($this->getDN(), 'memberOf');
362
-		$this->connection->writeToCache($cacheKey, $groupDNs);
363
-		return $groupDNs;
364
-	}
365
-
366
-	/**
367
-	 * @brief reads the image from LDAP that shall be used as Avatar
368
-	 * @return string data (provided by LDAP) | false
369
-	 */
370
-	public function getAvatarImage() {
371
-		if(!is_null($this->avatarImage)) {
372
-			return $this->avatarImage;
373
-		}
374
-
375
-		$this->avatarImage = false;
376
-		/** @var Connection $connection */
377
-		$connection = $this->access->getConnection();
378
-		$attributes = $connection->resolveRule('avatar');
379
-		foreach($attributes as $attribute) {
380
-			$result = $this->access->readAttribute($this->dn, $attribute);
381
-			if($result !== false && is_array($result) && isset($result[0])) {
382
-				$this->avatarImage = $result[0];
383
-				break;
384
-			}
385
-		}
386
-
387
-		return $this->avatarImage;
388
-	}
389
-
390
-	/**
391
-	 * @brief marks the user as having logged in at least once
392
-	 * @return null
393
-	 */
394
-	public function markLogin() {
395
-		$this->config->setUserValue(
396
-			$this->uid, 'user_ldap', self::USER_PREFKEY_FIRSTLOGIN, 1);
397
-	}
398
-
399
-	/**
400
-	 * @brief marks the time when user features like email have been updated
401
-	 * @return null
402
-	 */
403
-	public function markRefreshTime() {
404
-		$this->config->setUserValue(
405
-			$this->uid, 'user_ldap', self::USER_PREFKEY_LASTREFRESH, time());
406
-	}
407
-
408
-	/**
409
-	 * @brief checks whether user features needs to be updated again by
410
-	 * comparing the difference of time of the last refresh to now with the
411
-	 * desired interval
412
-	 * @return bool
413
-	 */
414
-	private function needsRefresh() {
415
-		$lastChecked = $this->config->getUserValue($this->uid, 'user_ldap',
416
-			self::USER_PREFKEY_LASTREFRESH, 0);
417
-
418
-		if((time() - (int)$lastChecked) < (int)$this->config->getAppValue('user_ldap', 'updateAttributesInterval', 86400)) {
419
-			return false;
420
-		}
421
-		return  true;
422
-	}
423
-
424
-	/**
425
-	 * Stores a key-value pair in relation to this user
426
-	 *
427
-	 * @param string $key
428
-	 * @param string $value
429
-	 */
430
-	private function store($key, $value) {
431
-		$this->config->setUserValue($this->uid, 'user_ldap', $key, $value);
432
-	}
433
-
434
-	/**
435
-	 * Composes the display name and stores it in the database. The final
436
-	 * display name is returned.
437
-	 *
438
-	 * @param string $displayName
439
-	 * @param string $displayName2
440
-	 * @return string the effective display name
441
-	 */
442
-	public function composeAndStoreDisplayName($displayName, $displayName2 = '') {
443
-		$displayName2 = (string)$displayName2;
444
-		if($displayName2 !== '') {
445
-			$displayName .= ' (' . $displayName2 . ')';
446
-		}
447
-		$oldName = $this->config->getUserValue($this->uid, 'user_ldap', 'displayName', null);
448
-		if ($oldName !== $displayName)  {
449
-			$this->store('displayName', $displayName);
450
-			$user = $this->userManager->get($this->getUsername());
451
-			if (!empty($oldName) && $user instanceof \OC\User\User) {
452
-				// if it was empty, it would be a new record, not a change emitting the trigger could
453
-				// potentially cause a UniqueConstraintViolationException, depending on some factors.
454
-				$user->triggerChange('displayName', $displayName, $oldName);
455
-			}
456
-		}
457
-		return $displayName;
458
-	}
459
-
460
-	/**
461
-	 * Stores the LDAP Username in the Database
462
-	 * @param string $userName
463
-	 */
464
-	public function storeLDAPUserName($userName) {
465
-		$this->store('uid', $userName);
466
-	}
467
-
468
-	/**
469
-	 * @brief checks whether an update method specified by feature was run
470
-	 * already. If not, it will marked like this, because it is expected that
471
-	 * the method will be run, when false is returned.
472
-	 * @param string $feature email | quota | avatar (can be extended)
473
-	 * @return bool
474
-	 */
475
-	private function wasRefreshed($feature) {
476
-		if(isset($this->refreshedFeatures[$feature])) {
477
-			return true;
478
-		}
479
-		$this->refreshedFeatures[$feature] = 1;
480
-		return false;
481
-	}
482
-
483
-	/**
484
-	 * fetches the email from LDAP and stores it as Nextcloud user value
485
-	 * @param string $valueFromLDAP if known, to save an LDAP read request
486
-	 * @return null
487
-	 */
488
-	public function updateEmail($valueFromLDAP = null) {
489
-		if($this->wasRefreshed('email')) {
490
-			return;
491
-		}
492
-		$email = (string)$valueFromLDAP;
493
-		if(is_null($valueFromLDAP)) {
494
-			$emailAttribute = $this->connection->ldapEmailAttribute;
495
-			if ($emailAttribute !== '') {
496
-				$aEmail = $this->access->readAttribute($this->dn, $emailAttribute);
497
-				if(is_array($aEmail) && (count($aEmail) > 0)) {
498
-					$email = (string)$aEmail[0];
499
-				}
500
-			}
501
-		}
502
-		if ($email !== '') {
503
-			$user = $this->userManager->get($this->uid);
504
-			if (!is_null($user)) {
505
-				$currentEmail = (string)$user->getEMailAddress();
506
-				if ($currentEmail !== $email) {
507
-					$user->setEMailAddress($email);
508
-				}
509
-			}
510
-		}
511
-	}
512
-
513
-	/**
514
-	 * Overall process goes as follow:
515
-	 * 1. fetch the quota from LDAP and check if it's parseable with the "verifyQuotaValue" function
516
-	 * 2. if the value can't be fetched, is empty or not parseable, use the default LDAP quota
517
-	 * 3. if the default LDAP quota can't be parsed, use the Nextcloud's default quota (use 'default')
518
-	 * 4. check if the target user exists and set the quota for the user.
519
-	 *
520
-	 * In order to improve performance and prevent an unwanted extra LDAP call, the $valueFromLDAP
521
-	 * parameter can be passed with the value of the attribute. This value will be considered as the
522
-	 * quota for the user coming from the LDAP server (step 1 of the process) It can be useful to
523
-	 * fetch all the user's attributes in one call and use the fetched values in this function.
524
-	 * The expected value for that parameter is a string describing the quota for the user. Valid
525
-	 * values are 'none' (unlimited), 'default' (the Nextcloud's default quota), '1234' (quota in
526
-	 * bytes), '1234 MB' (quota in MB - check the \OC_Helper::computerFileSize method for more info)
527
-	 *
528
-	 * fetches the quota from LDAP and stores it as Nextcloud user value
529
-	 * @param string $valueFromLDAP the quota attribute's value can be passed,
530
-	 * to save the readAttribute request
531
-	 * @return null
532
-	 */
533
-	public function updateQuota($valueFromLDAP = null) {
534
-		if($this->wasRefreshed('quota')) {
535
-			return;
536
-		}
537
-
538
-		$quotaAttribute = $this->connection->ldapQuotaAttribute;
539
-		$defaultQuota = $this->connection->ldapQuotaDefault;
540
-		if($quotaAttribute === '' && $defaultQuota === '') {
541
-			return;
542
-		}
543
-
544
-		$quota = false;
545
-		if(is_null($valueFromLDAP) && $quotaAttribute !== '') {
546
-			$aQuota = $this->access->readAttribute($this->dn, $quotaAttribute);
547
-			if($aQuota && (count($aQuota) > 0) && $this->verifyQuotaValue($aQuota[0])) {
548
-				$quota = $aQuota[0];
549
-			} else if(is_array($aQuota) && isset($aQuota[0])) {
550
-				$this->log->log('no suitable LDAP quota found for user ' . $this->uid . ': [' . $aQuota[0] . ']', ILogger::DEBUG);
551
-			}
552
-		} else if ($this->verifyQuotaValue($valueFromLDAP)) {
553
-			$quota = $valueFromLDAP;
554
-		} else {
555
-			$this->log->log('no suitable LDAP quota found for user ' . $this->uid . ': [' . $valueFromLDAP . ']', ILogger::DEBUG);
556
-		}
557
-
558
-		if ($quota === false && $this->verifyQuotaValue($defaultQuota)) {
559
-			// quota not found using the LDAP attribute (or not parseable). Try the default quota
560
-			$quota = $defaultQuota;
561
-		} else if($quota === false) {
562
-			$this->log->log('no suitable default quota found for user ' . $this->uid . ': [' . $defaultQuota . ']', ILogger::DEBUG);
563
-			return;
564
-		}
565
-
566
-		$targetUser = $this->userManager->get($this->uid);
567
-		if ($targetUser instanceof IUser) {
568
-			$targetUser->setQuota($quota);
569
-		} else {
570
-			$this->log->log('trying to set a quota for user ' . $this->uid . ' but the user is missing', ILogger::INFO);
571
-		}
572
-	}
573
-
574
-	private function verifyQuotaValue($quotaValue) {
575
-		return $quotaValue === 'none' || $quotaValue === 'default' || \OC_Helper::computerFileSize($quotaValue) !== false;
576
-	}
577
-
578
-	/**
579
-	 * called by a post_login hook to save the avatar picture
580
-	 *
581
-	 * @param array $params
582
-	 */
583
-	public function updateAvatarPostLogin($params) {
584
-		if(isset($params['uid']) && $params['uid'] === $this->getUsername()) {
585
-			$this->updateAvatar();
586
-		}
587
-	}
588
-
589
-	/**
590
-	 * @brief attempts to get an image from LDAP and sets it as Nextcloud avatar
591
-	 * @return bool
592
-	 */
593
-	public function updateAvatar($force = false) {
594
-		if(!$force && $this->wasRefreshed('avatar')) {
595
-			return false;
596
-		}
597
-		$avatarImage = $this->getAvatarImage();
598
-		if($avatarImage === false) {
599
-			//not set, nothing left to do;
600
-			return false;
601
-		}
602
-
603
-		if(!$this->image->loadFromBase64(base64_encode($avatarImage))) {
604
-			return false;
605
-		}
606
-
607
-		// use the checksum before modifications
608
-		$checksum = md5($this->image->data());
609
-
610
-		if($checksum === $this->config->getUserValue($this->uid, 'user_ldap', 'lastAvatarChecksum', '')) {
611
-			return true;
612
-		}
613
-
614
-		$isSet = $this->setOwnCloudAvatar();
615
-
616
-		if($isSet) {
617
-			// save checksum only after successful setting
618
-			$this->config->setUserValue($this->uid, 'user_ldap', 'lastAvatarChecksum', $checksum);
619
-		}
620
-
621
-		return $isSet;
622
-	}
623
-
624
-	/**
625
-	 * @brief sets an image as Nextcloud avatar
626
-	 * @return bool
627
-	 */
628
-	private function setOwnCloudAvatar() {
629
-		if(!$this->image->valid()) {
630
-			$this->log->log('avatar image data from LDAP invalid for '.$this->dn, ILogger::ERROR);
631
-			return false;
632
-		}
633
-
634
-
635
-		//make sure it is a square and not bigger than 128x128
636
-		$size = min([$this->image->width(), $this->image->height(), 128]);
637
-		if(!$this->image->centerCrop($size)) {
638
-			$this->log->log('croping image for avatar failed for '.$this->dn, ILogger::ERROR);
639
-			return false;
640
-		}
641
-
642
-		if(!$this->fs->isLoaded()) {
643
-			$this->fs->setup($this->uid);
644
-		}
645
-
646
-		try {
647
-			$avatar = $this->avatarManager->getAvatar($this->uid);
648
-			$avatar->set($this->image);
649
-			return true;
650
-		} catch (\Exception $e) {
651
-			\OC::$server->getLogger()->logException($e, [
652
-				'message' => 'Could not set avatar for ' . $this->dn,
653
-				'level' => ILogger::INFO,
654
-				'app' => 'user_ldap',
655
-			]);
656
-		}
657
-		return false;
658
-	}
659
-
660
-	/**
661
-	 * @throws AttributeNotSet
662
-	 * @throws \OC\ServerNotAvailableException
663
-	 * @throws \OCP\PreConditionNotMetException
664
-	 */
665
-	public function getExtStorageHome():string {
666
-		$value = $this->config->getUserValue($this->getUsername(), 'user_ldap', 'extStorageHome', '');
667
-		if ($value !== '') {
668
-			return $value;
669
-		}
670
-
671
-		$value = $this->updateExtStorageHome();
672
-		if ($value !== '') {
673
-			return $value;
674
-		}
675
-
676
-		throw new AttributeNotSet(sprintf(
677
-			'external home storage attribute yield no value for %s', $this->getUsername()
678
-		));
679
-	}
680
-
681
-	/**
682
-	 * @throws \OCP\PreConditionNotMetException
683
-	 * @throws \OC\ServerNotAvailableException
684
-	 */
685
-	public function updateExtStorageHome(string $valueFromLDAP = null):string {
686
-		if ($valueFromLDAP === null) {
687
-			$extHomeValues = $this->access->readAttribute($this->getDN(), $this->connection->ldapExtStorageHomeAttribute);
688
-		} else {
689
-			$extHomeValues = [$valueFromLDAP];
690
-		}
691
-		if ($extHomeValues && isset($extHomeValues[0])) {
692
-			$extHome = $extHomeValues[0];
693
-			$this->config->setUserValue($this->getUsername(), 'user_ldap', 'extStorageHome', $extHome);
694
-			return $extHome;
695
-		} else {
696
-			$this->config->deleteUserValue($this->getUsername(), 'user_ldap', 'extStorageHome');
697
-			return '';
698
-		}
699
-	}
700
-
701
-	/**
702
-	 * called by a post_login hook to handle password expiry
703
-	 *
704
-	 * @param array $params
705
-	 */
706
-	public function handlePasswordExpiry($params) {
707
-		$ppolicyDN = $this->connection->ldapDefaultPPolicyDN;
708
-		if (empty($ppolicyDN) || ((int)$this->connection->turnOnPasswordChange !== 1)) {
709
-			return;//password expiry handling disabled
710
-		}
711
-		$uid = $params['uid'];
712
-		if (isset($uid) && $uid === $this->getUsername()) {
713
-			//retrieve relevant user attributes
714
-			$result = $this->access->search('objectclass=*', array($this->dn), ['pwdpolicysubentry', 'pwdgraceusetime', 'pwdreset', 'pwdchangedtime']);
715
-
716
-			if (array_key_exists('pwdpolicysubentry', $result[0])) {
717
-				$pwdPolicySubentry = $result[0]['pwdpolicysubentry'];
718
-				if ($pwdPolicySubentry && (count($pwdPolicySubentry) > 0)){
719
-					$ppolicyDN = $pwdPolicySubentry[0];//custom ppolicy DN
720
-				}
721
-			}
722
-
723
-			$pwdGraceUseTime = array_key_exists('pwdgraceusetime', $result[0]) ? $result[0]['pwdgraceusetime'] : [];
724
-			$pwdReset = array_key_exists('pwdreset', $result[0]) ? $result[0]['pwdreset'] : [];
725
-			$pwdChangedTime = array_key_exists('pwdchangedtime', $result[0]) ? $result[0]['pwdchangedtime'] : [];
726
-
727
-			//retrieve relevant password policy attributes
728
-			$cacheKey = 'ppolicyAttributes' . $ppolicyDN;
729
-			$result = $this->connection->getFromCache($cacheKey);
730
-			if(is_null($result)) {
731
-				$result = $this->access->search('objectclass=*', array($ppolicyDN), ['pwdgraceauthnlimit', 'pwdmaxage', 'pwdexpirewarning']);
732
-				$this->connection->writeToCache($cacheKey, $result);
733
-			}
734
-
735
-			$pwdGraceAuthNLimit = array_key_exists('pwdgraceauthnlimit', $result[0]) ? $result[0]['pwdgraceauthnlimit'] : [];
736
-			$pwdMaxAge = array_key_exists('pwdmaxage', $result[0]) ? $result[0]['pwdmaxage'] : [];
737
-			$pwdExpireWarning = array_key_exists('pwdexpirewarning', $result[0]) ? $result[0]['pwdexpirewarning'] : [];
738
-
739
-			//handle grace login
740
-			if (!empty($pwdGraceUseTime)) { //was this a grace login?
741
-				if (!empty($pwdGraceAuthNLimit)
742
-					&& count($pwdGraceUseTime) < (int)$pwdGraceAuthNLimit[0]) { //at least one more grace login available?
743
-					$this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
744
-					header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
745
-					'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid)));
746
-				} else { //no more grace login available
747
-					header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
748
-					'user_ldap.renewPassword.showLoginFormInvalidPassword', array('user' => $uid)));
749
-				}
750
-				exit();
751
-			}
752
-			//handle pwdReset attribute
753
-			if (!empty($pwdReset) && $pwdReset[0] === 'TRUE') { //user must change his password
754
-				$this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
755
-				header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
756
-				'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid)));
757
-				exit();
758
-			}
759
-			//handle password expiry warning
760
-			if (!empty($pwdChangedTime)) {
761
-				if (!empty($pwdMaxAge)
762
-					&& !empty($pwdExpireWarning)) {
763
-					$pwdMaxAgeInt = (int)$pwdMaxAge[0];
764
-					$pwdExpireWarningInt = (int)$pwdExpireWarning[0];
765
-					if ($pwdMaxAgeInt > 0 && $pwdExpireWarningInt > 0){
766
-						$pwdChangedTimeDt = \DateTime::createFromFormat('YmdHisZ', $pwdChangedTime[0]);
767
-						$pwdChangedTimeDt->add(new \DateInterval('PT'.$pwdMaxAgeInt.'S'));
768
-						$currentDateTime = new \DateTime();
769
-						$secondsToExpiry = $pwdChangedTimeDt->getTimestamp() - $currentDateTime->getTimestamp();
770
-						if ($secondsToExpiry <= $pwdExpireWarningInt) {
771
-							//remove last password expiry warning if any
772
-							$notification = $this->notificationManager->createNotification();
773
-							$notification->setApp('user_ldap')
774
-								->setUser($uid)
775
-								->setObject('pwd_exp_warn', $uid)
776
-							;
777
-							$this->notificationManager->markProcessed($notification);
778
-							//create new password expiry warning
779
-							$notification = $this->notificationManager->createNotification();
780
-							$notification->setApp('user_ldap')
781
-								->setUser($uid)
782
-								->setDateTime($currentDateTime)
783
-								->setObject('pwd_exp_warn', $uid)
784
-								->setSubject('pwd_exp_warn_days', [(int) ceil($secondsToExpiry / 60 / 60 / 24)])
785
-							;
786
-							$this->notificationManager->notify($notification);
787
-						}
788
-					}
789
-				}
790
-			}
791
-		}
792
-	}
52
+    /**
53
+     * @var Access
54
+     */
55
+    protected $access;
56
+    /**
57
+     * @var Connection
58
+     */
59
+    protected $connection;
60
+    /**
61
+     * @var IConfig
62
+     */
63
+    protected $config;
64
+    /**
65
+     * @var FilesystemHelper
66
+     */
67
+    protected $fs;
68
+    /**
69
+     * @var Image
70
+     */
71
+    protected $image;
72
+    /**
73
+     * @var LogWrapper
74
+     */
75
+    protected $log;
76
+    /**
77
+     * @var IAvatarManager
78
+     */
79
+    protected $avatarManager;
80
+    /**
81
+     * @var IUserManager
82
+     */
83
+    protected $userManager;
84
+    /**
85
+     * @var INotificationManager
86
+     */
87
+    protected $notificationManager;
88
+    /**
89
+     * @var string
90
+     */
91
+    protected $dn;
92
+    /**
93
+     * @var string
94
+     */
95
+    protected $uid;
96
+    /**
97
+     * @var string[]
98
+     */
99
+    protected $refreshedFeatures = array();
100
+    /**
101
+     * @var string
102
+     */
103
+    protected $avatarImage;
104
+
105
+    /**
106
+     * DB config keys for user preferences
107
+     */
108
+    const USER_PREFKEY_FIRSTLOGIN  = 'firstLoginAccomplished';
109
+    const USER_PREFKEY_LASTREFRESH = 'lastFeatureRefresh';
110
+
111
+    /**
112
+     * @brief constructor, make sure the subclasses call this one!
113
+     * @param string $username the internal username
114
+     * @param string $dn the LDAP DN
115
+     * @param Access $access
116
+     * @param IConfig $config
117
+     * @param FilesystemHelper $fs
118
+     * @param Image $image any empty instance
119
+     * @param LogWrapper $log
120
+     * @param IAvatarManager $avatarManager
121
+     * @param IUserManager $userManager
122
+     * @param INotificationManager $notificationManager
123
+     */
124
+    public function __construct($username, $dn, Access $access,
125
+        IConfig $config, FilesystemHelper $fs, Image $image,
126
+        LogWrapper $log, IAvatarManager $avatarManager, IUserManager $userManager,
127
+        INotificationManager $notificationManager) {
128
+
129
+        if ($username === null) {
130
+            $log->log("uid for '$dn' must not be null!", ILogger::ERROR);
131
+            throw new \InvalidArgumentException('uid must not be null!');
132
+        } else if ($username === '') {
133
+            $log->log("uid for '$dn' must not be an empty string", ILogger::ERROR);
134
+            throw new \InvalidArgumentException('uid must not be an empty string!');
135
+        }
136
+
137
+        $this->access              = $access;
138
+        $this->connection          = $access->getConnection();
139
+        $this->config              = $config;
140
+        $this->fs                  = $fs;
141
+        $this->dn                  = $dn;
142
+        $this->uid                 = $username;
143
+        $this->image               = $image;
144
+        $this->log                 = $log;
145
+        $this->avatarManager       = $avatarManager;
146
+        $this->userManager         = $userManager;
147
+        $this->notificationManager = $notificationManager;
148
+
149
+        \OCP\Util::connectHook('OC_User', 'post_login', $this, 'handlePasswordExpiry');
150
+    }
151
+
152
+    /**
153
+     * @brief updates properties like email, quota or avatar provided by LDAP
154
+     * @return null
155
+     */
156
+    public function update() {
157
+        if(is_null($this->dn)) {
158
+            return null;
159
+        }
160
+
161
+        $hasLoggedIn = $this->config->getUserValue($this->uid, 'user_ldap',
162
+                self::USER_PREFKEY_FIRSTLOGIN, 0);
163
+
164
+        if($this->needsRefresh()) {
165
+            $this->updateEmail();
166
+            $this->updateQuota();
167
+            if($hasLoggedIn !== 0) {
168
+                //we do not need to try it, when the user has not been logged in
169
+                //before, because the file system will not be ready.
170
+                $this->updateAvatar();
171
+                //in order to get an avatar as soon as possible, mark the user
172
+                //as refreshed only when updating the avatar did happen
173
+                $this->markRefreshTime();
174
+            }
175
+        }
176
+    }
177
+
178
+    /**
179
+     * marks a user as deleted
180
+     *
181
+     * @throws \OCP\PreConditionNotMetException
182
+     */
183
+    public function markUser() {
184
+        $curValue = $this->config->getUserValue($this->getUsername(), 'user_ldap', 'isDeleted', '0');
185
+        if($curValue === '1') {
186
+            // the user is already marked, do not write to DB again
187
+            return;
188
+        }
189
+        $this->config->setUserValue($this->getUsername(), 'user_ldap', 'isDeleted', '1');
190
+        $this->config->setUserValue($this->getUsername(), 'user_ldap', 'foundDeleted', (string)time());
191
+    }
192
+
193
+    /**
194
+     * processes results from LDAP for attributes as returned by getAttributesToRead()
195
+     * @param array $ldapEntry the user entry as retrieved from LDAP
196
+     */
197
+    public function processAttributes($ldapEntry) {
198
+        $this->markRefreshTime();
199
+        //Quota
200
+        $attr = strtolower($this->connection->ldapQuotaAttribute);
201
+        if(isset($ldapEntry[$attr])) {
202
+            $this->updateQuota($ldapEntry[$attr][0]);
203
+        } else {
204
+            if ($this->connection->ldapQuotaDefault !== '') {
205
+                $this->updateQuota();
206
+            }
207
+        }
208
+        unset($attr);
209
+
210
+        //displayName
211
+        $displayName = $displayName2 = '';
212
+        $attr = strtolower($this->connection->ldapUserDisplayName);
213
+        if(isset($ldapEntry[$attr])) {
214
+            $displayName = (string)$ldapEntry[$attr][0];
215
+        }
216
+        $attr = strtolower($this->connection->ldapUserDisplayName2);
217
+        if(isset($ldapEntry[$attr])) {
218
+            $displayName2 = (string)$ldapEntry[$attr][0];
219
+        }
220
+        if ($displayName !== '') {
221
+            $this->composeAndStoreDisplayName($displayName, $displayName2);
222
+            $this->access->cacheUserDisplayName(
223
+                $this->getUsername(),
224
+                $displayName,
225
+                $displayName2
226
+            );
227
+        }
228
+        unset($attr);
229
+
230
+        //Email
231
+        //email must be stored after displayname, because it would cause a user
232
+        //change event that will trigger fetching the display name again
233
+        $attr = strtolower($this->connection->ldapEmailAttribute);
234
+        if(isset($ldapEntry[$attr])) {
235
+            $this->updateEmail($ldapEntry[$attr][0]);
236
+        }
237
+        unset($attr);
238
+
239
+        // LDAP Username, needed for s2s sharing
240
+        if(isset($ldapEntry['uid'])) {
241
+            $this->storeLDAPUserName($ldapEntry['uid'][0]);
242
+        } else if(isset($ldapEntry['samaccountname'])) {
243
+            $this->storeLDAPUserName($ldapEntry['samaccountname'][0]);
244
+        }
245
+
246
+        //homePath
247
+        if(strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) {
248
+            $attr = strtolower(substr($this->connection->homeFolderNamingRule, strlen('attr:')));
249
+            if(isset($ldapEntry[$attr])) {
250
+                $this->access->cacheUserHome(
251
+                    $this->getUsername(), $this->getHomePath($ldapEntry[$attr][0]));
252
+            }
253
+        }
254
+
255
+        //memberOf groups
256
+        $cacheKey = 'getMemberOf'.$this->getUsername();
257
+        $groups = false;
258
+        if(isset($ldapEntry['memberof'])) {
259
+            $groups = $ldapEntry['memberof'];
260
+        }
261
+        $this->connection->writeToCache($cacheKey, $groups);
262
+
263
+        //external storage var
264
+        $attr = strtolower($this->connection->ldapExtStorageHomeAttribute);
265
+        if(isset($ldapEntry[$attr])) {
266
+            $this->updateExtStorageHome($ldapEntry[$attr][0]);
267
+        }
268
+        unset($attr);
269
+
270
+        //Avatar
271
+        /** @var Connection $connection */
272
+        $connection = $this->access->getConnection();
273
+        $attributes = $connection->resolveRule('avatar');
274
+        foreach ($attributes as $attribute)  {
275
+            if(isset($ldapEntry[$attribute])) {
276
+                $this->avatarImage = $ldapEntry[$attribute][0];
277
+                // the call to the method that saves the avatar in the file
278
+                // system must be postponed after the login. It is to ensure
279
+                // external mounts are mounted properly (e.g. with login
280
+                // credentials from the session).
281
+                \OCP\Util::connectHook('OC_User', 'post_login', $this, 'updateAvatarPostLogin');
282
+                break;
283
+            }
284
+        }
285
+    }
286
+
287
+    /**
288
+     * @brief returns the LDAP DN of the user
289
+     * @return string
290
+     */
291
+    public function getDN() {
292
+        return $this->dn;
293
+    }
294
+
295
+    /**
296
+     * @brief returns the Nextcloud internal username of the user
297
+     * @return string
298
+     */
299
+    public function getUsername() {
300
+        return $this->uid;
301
+    }
302
+
303
+    /**
304
+     * returns the home directory of the user if specified by LDAP settings
305
+     * @param string $valueFromLDAP
306
+     * @return bool|string
307
+     * @throws \Exception
308
+     */
309
+    public function getHomePath($valueFromLDAP = null) {
310
+        $path = (string)$valueFromLDAP;
311
+        $attr = null;
312
+
313
+        if (is_null($valueFromLDAP)
314
+           && strpos($this->access->connection->homeFolderNamingRule, 'attr:') === 0
315
+           && $this->access->connection->homeFolderNamingRule !== 'attr:')
316
+        {
317
+            $attr = substr($this->access->connection->homeFolderNamingRule, strlen('attr:'));
318
+            $homedir = $this->access->readAttribute(
319
+                $this->access->username2dn($this->getUsername()), $attr);
320
+            if ($homedir && isset($homedir[0])) {
321
+                $path = $homedir[0];
322
+            }
323
+        }
324
+
325
+        if ($path !== '') {
326
+            //if attribute's value is an absolute path take this, otherwise append it to data dir
327
+            //check for / at the beginning or pattern c:\ resp. c:/
328
+            if(   '/' !== $path[0]
329
+               && !(3 < strlen($path) && ctype_alpha($path[0])
330
+                   && $path[1] === ':' && ('\\' === $path[2] || '/' === $path[2]))
331
+            ) {
332
+                $path = $this->config->getSystemValue('datadirectory',
333
+                        \OC::$SERVERROOT.'/data' ) . '/' . $path;
334
+            }
335
+            //we need it to store it in the DB as well in case a user gets
336
+            //deleted so we can clean up afterwards
337
+            $this->config->setUserValue(
338
+                $this->getUsername(), 'user_ldap', 'homePath', $path
339
+            );
340
+            return $path;
341
+        }
342
+
343
+        if(    !is_null($attr)
344
+            && $this->config->getAppValue('user_ldap', 'enforce_home_folder_naming_rule', true)
345
+        ) {
346
+            // a naming rule attribute is defined, but it doesn't exist for that LDAP user
347
+            throw new \Exception('Home dir attribute can\'t be read from LDAP for uid: ' . $this->getUsername());
348
+        }
349
+
350
+        //false will apply default behaviour as defined and done by OC_User
351
+        $this->config->setUserValue($this->getUsername(), 'user_ldap', 'homePath', '');
352
+        return false;
353
+    }
354
+
355
+    public function getMemberOfGroups() {
356
+        $cacheKey = 'getMemberOf'.$this->getUsername();
357
+        $memberOfGroups = $this->connection->getFromCache($cacheKey);
358
+        if(!is_null($memberOfGroups)) {
359
+            return $memberOfGroups;
360
+        }
361
+        $groupDNs = $this->access->readAttribute($this->getDN(), 'memberOf');
362
+        $this->connection->writeToCache($cacheKey, $groupDNs);
363
+        return $groupDNs;
364
+    }
365
+
366
+    /**
367
+     * @brief reads the image from LDAP that shall be used as Avatar
368
+     * @return string data (provided by LDAP) | false
369
+     */
370
+    public function getAvatarImage() {
371
+        if(!is_null($this->avatarImage)) {
372
+            return $this->avatarImage;
373
+        }
374
+
375
+        $this->avatarImage = false;
376
+        /** @var Connection $connection */
377
+        $connection = $this->access->getConnection();
378
+        $attributes = $connection->resolveRule('avatar');
379
+        foreach($attributes as $attribute) {
380
+            $result = $this->access->readAttribute($this->dn, $attribute);
381
+            if($result !== false && is_array($result) && isset($result[0])) {
382
+                $this->avatarImage = $result[0];
383
+                break;
384
+            }
385
+        }
386
+
387
+        return $this->avatarImage;
388
+    }
389
+
390
+    /**
391
+     * @brief marks the user as having logged in at least once
392
+     * @return null
393
+     */
394
+    public function markLogin() {
395
+        $this->config->setUserValue(
396
+            $this->uid, 'user_ldap', self::USER_PREFKEY_FIRSTLOGIN, 1);
397
+    }
398
+
399
+    /**
400
+     * @brief marks the time when user features like email have been updated
401
+     * @return null
402
+     */
403
+    public function markRefreshTime() {
404
+        $this->config->setUserValue(
405
+            $this->uid, 'user_ldap', self::USER_PREFKEY_LASTREFRESH, time());
406
+    }
407
+
408
+    /**
409
+     * @brief checks whether user features needs to be updated again by
410
+     * comparing the difference of time of the last refresh to now with the
411
+     * desired interval
412
+     * @return bool
413
+     */
414
+    private function needsRefresh() {
415
+        $lastChecked = $this->config->getUserValue($this->uid, 'user_ldap',
416
+            self::USER_PREFKEY_LASTREFRESH, 0);
417
+
418
+        if((time() - (int)$lastChecked) < (int)$this->config->getAppValue('user_ldap', 'updateAttributesInterval', 86400)) {
419
+            return false;
420
+        }
421
+        return  true;
422
+    }
423
+
424
+    /**
425
+     * Stores a key-value pair in relation to this user
426
+     *
427
+     * @param string $key
428
+     * @param string $value
429
+     */
430
+    private function store($key, $value) {
431
+        $this->config->setUserValue($this->uid, 'user_ldap', $key, $value);
432
+    }
433
+
434
+    /**
435
+     * Composes the display name and stores it in the database. The final
436
+     * display name is returned.
437
+     *
438
+     * @param string $displayName
439
+     * @param string $displayName2
440
+     * @return string the effective display name
441
+     */
442
+    public function composeAndStoreDisplayName($displayName, $displayName2 = '') {
443
+        $displayName2 = (string)$displayName2;
444
+        if($displayName2 !== '') {
445
+            $displayName .= ' (' . $displayName2 . ')';
446
+        }
447
+        $oldName = $this->config->getUserValue($this->uid, 'user_ldap', 'displayName', null);
448
+        if ($oldName !== $displayName)  {
449
+            $this->store('displayName', $displayName);
450
+            $user = $this->userManager->get($this->getUsername());
451
+            if (!empty($oldName) && $user instanceof \OC\User\User) {
452
+                // if it was empty, it would be a new record, not a change emitting the trigger could
453
+                // potentially cause a UniqueConstraintViolationException, depending on some factors.
454
+                $user->triggerChange('displayName', $displayName, $oldName);
455
+            }
456
+        }
457
+        return $displayName;
458
+    }
459
+
460
+    /**
461
+     * Stores the LDAP Username in the Database
462
+     * @param string $userName
463
+     */
464
+    public function storeLDAPUserName($userName) {
465
+        $this->store('uid', $userName);
466
+    }
467
+
468
+    /**
469
+     * @brief checks whether an update method specified by feature was run
470
+     * already. If not, it will marked like this, because it is expected that
471
+     * the method will be run, when false is returned.
472
+     * @param string $feature email | quota | avatar (can be extended)
473
+     * @return bool
474
+     */
475
+    private function wasRefreshed($feature) {
476
+        if(isset($this->refreshedFeatures[$feature])) {
477
+            return true;
478
+        }
479
+        $this->refreshedFeatures[$feature] = 1;
480
+        return false;
481
+    }
482
+
483
+    /**
484
+     * fetches the email from LDAP and stores it as Nextcloud user value
485
+     * @param string $valueFromLDAP if known, to save an LDAP read request
486
+     * @return null
487
+     */
488
+    public function updateEmail($valueFromLDAP = null) {
489
+        if($this->wasRefreshed('email')) {
490
+            return;
491
+        }
492
+        $email = (string)$valueFromLDAP;
493
+        if(is_null($valueFromLDAP)) {
494
+            $emailAttribute = $this->connection->ldapEmailAttribute;
495
+            if ($emailAttribute !== '') {
496
+                $aEmail = $this->access->readAttribute($this->dn, $emailAttribute);
497
+                if(is_array($aEmail) && (count($aEmail) > 0)) {
498
+                    $email = (string)$aEmail[0];
499
+                }
500
+            }
501
+        }
502
+        if ($email !== '') {
503
+            $user = $this->userManager->get($this->uid);
504
+            if (!is_null($user)) {
505
+                $currentEmail = (string)$user->getEMailAddress();
506
+                if ($currentEmail !== $email) {
507
+                    $user->setEMailAddress($email);
508
+                }
509
+            }
510
+        }
511
+    }
512
+
513
+    /**
514
+     * Overall process goes as follow:
515
+     * 1. fetch the quota from LDAP and check if it's parseable with the "verifyQuotaValue" function
516
+     * 2. if the value can't be fetched, is empty or not parseable, use the default LDAP quota
517
+     * 3. if the default LDAP quota can't be parsed, use the Nextcloud's default quota (use 'default')
518
+     * 4. check if the target user exists and set the quota for the user.
519
+     *
520
+     * In order to improve performance and prevent an unwanted extra LDAP call, the $valueFromLDAP
521
+     * parameter can be passed with the value of the attribute. This value will be considered as the
522
+     * quota for the user coming from the LDAP server (step 1 of the process) It can be useful to
523
+     * fetch all the user's attributes in one call and use the fetched values in this function.
524
+     * The expected value for that parameter is a string describing the quota for the user. Valid
525
+     * values are 'none' (unlimited), 'default' (the Nextcloud's default quota), '1234' (quota in
526
+     * bytes), '1234 MB' (quota in MB - check the \OC_Helper::computerFileSize method for more info)
527
+     *
528
+     * fetches the quota from LDAP and stores it as Nextcloud user value
529
+     * @param string $valueFromLDAP the quota attribute's value can be passed,
530
+     * to save the readAttribute request
531
+     * @return null
532
+     */
533
+    public function updateQuota($valueFromLDAP = null) {
534
+        if($this->wasRefreshed('quota')) {
535
+            return;
536
+        }
537
+
538
+        $quotaAttribute = $this->connection->ldapQuotaAttribute;
539
+        $defaultQuota = $this->connection->ldapQuotaDefault;
540
+        if($quotaAttribute === '' && $defaultQuota === '') {
541
+            return;
542
+        }
543
+
544
+        $quota = false;
545
+        if(is_null($valueFromLDAP) && $quotaAttribute !== '') {
546
+            $aQuota = $this->access->readAttribute($this->dn, $quotaAttribute);
547
+            if($aQuota && (count($aQuota) > 0) && $this->verifyQuotaValue($aQuota[0])) {
548
+                $quota = $aQuota[0];
549
+            } else if(is_array($aQuota) && isset($aQuota[0])) {
550
+                $this->log->log('no suitable LDAP quota found for user ' . $this->uid . ': [' . $aQuota[0] . ']', ILogger::DEBUG);
551
+            }
552
+        } else if ($this->verifyQuotaValue($valueFromLDAP)) {
553
+            $quota = $valueFromLDAP;
554
+        } else {
555
+            $this->log->log('no suitable LDAP quota found for user ' . $this->uid . ': [' . $valueFromLDAP . ']', ILogger::DEBUG);
556
+        }
557
+
558
+        if ($quota === false && $this->verifyQuotaValue($defaultQuota)) {
559
+            // quota not found using the LDAP attribute (or not parseable). Try the default quota
560
+            $quota = $defaultQuota;
561
+        } else if($quota === false) {
562
+            $this->log->log('no suitable default quota found for user ' . $this->uid . ': [' . $defaultQuota . ']', ILogger::DEBUG);
563
+            return;
564
+        }
565
+
566
+        $targetUser = $this->userManager->get($this->uid);
567
+        if ($targetUser instanceof IUser) {
568
+            $targetUser->setQuota($quota);
569
+        } else {
570
+            $this->log->log('trying to set a quota for user ' . $this->uid . ' but the user is missing', ILogger::INFO);
571
+        }
572
+    }
573
+
574
+    private function verifyQuotaValue($quotaValue) {
575
+        return $quotaValue === 'none' || $quotaValue === 'default' || \OC_Helper::computerFileSize($quotaValue) !== false;
576
+    }
577
+
578
+    /**
579
+     * called by a post_login hook to save the avatar picture
580
+     *
581
+     * @param array $params
582
+     */
583
+    public function updateAvatarPostLogin($params) {
584
+        if(isset($params['uid']) && $params['uid'] === $this->getUsername()) {
585
+            $this->updateAvatar();
586
+        }
587
+    }
588
+
589
+    /**
590
+     * @brief attempts to get an image from LDAP and sets it as Nextcloud avatar
591
+     * @return bool
592
+     */
593
+    public function updateAvatar($force = false) {
594
+        if(!$force && $this->wasRefreshed('avatar')) {
595
+            return false;
596
+        }
597
+        $avatarImage = $this->getAvatarImage();
598
+        if($avatarImage === false) {
599
+            //not set, nothing left to do;
600
+            return false;
601
+        }
602
+
603
+        if(!$this->image->loadFromBase64(base64_encode($avatarImage))) {
604
+            return false;
605
+        }
606
+
607
+        // use the checksum before modifications
608
+        $checksum = md5($this->image->data());
609
+
610
+        if($checksum === $this->config->getUserValue($this->uid, 'user_ldap', 'lastAvatarChecksum', '')) {
611
+            return true;
612
+        }
613
+
614
+        $isSet = $this->setOwnCloudAvatar();
615
+
616
+        if($isSet) {
617
+            // save checksum only after successful setting
618
+            $this->config->setUserValue($this->uid, 'user_ldap', 'lastAvatarChecksum', $checksum);
619
+        }
620
+
621
+        return $isSet;
622
+    }
623
+
624
+    /**
625
+     * @brief sets an image as Nextcloud avatar
626
+     * @return bool
627
+     */
628
+    private function setOwnCloudAvatar() {
629
+        if(!$this->image->valid()) {
630
+            $this->log->log('avatar image data from LDAP invalid for '.$this->dn, ILogger::ERROR);
631
+            return false;
632
+        }
633
+
634
+
635
+        //make sure it is a square and not bigger than 128x128
636
+        $size = min([$this->image->width(), $this->image->height(), 128]);
637
+        if(!$this->image->centerCrop($size)) {
638
+            $this->log->log('croping image for avatar failed for '.$this->dn, ILogger::ERROR);
639
+            return false;
640
+        }
641
+
642
+        if(!$this->fs->isLoaded()) {
643
+            $this->fs->setup($this->uid);
644
+        }
645
+
646
+        try {
647
+            $avatar = $this->avatarManager->getAvatar($this->uid);
648
+            $avatar->set($this->image);
649
+            return true;
650
+        } catch (\Exception $e) {
651
+            \OC::$server->getLogger()->logException($e, [
652
+                'message' => 'Could not set avatar for ' . $this->dn,
653
+                'level' => ILogger::INFO,
654
+                'app' => 'user_ldap',
655
+            ]);
656
+        }
657
+        return false;
658
+    }
659
+
660
+    /**
661
+     * @throws AttributeNotSet
662
+     * @throws \OC\ServerNotAvailableException
663
+     * @throws \OCP\PreConditionNotMetException
664
+     */
665
+    public function getExtStorageHome():string {
666
+        $value = $this->config->getUserValue($this->getUsername(), 'user_ldap', 'extStorageHome', '');
667
+        if ($value !== '') {
668
+            return $value;
669
+        }
670
+
671
+        $value = $this->updateExtStorageHome();
672
+        if ($value !== '') {
673
+            return $value;
674
+        }
675
+
676
+        throw new AttributeNotSet(sprintf(
677
+            'external home storage attribute yield no value for %s', $this->getUsername()
678
+        ));
679
+    }
680
+
681
+    /**
682
+     * @throws \OCP\PreConditionNotMetException
683
+     * @throws \OC\ServerNotAvailableException
684
+     */
685
+    public function updateExtStorageHome(string $valueFromLDAP = null):string {
686
+        if ($valueFromLDAP === null) {
687
+            $extHomeValues = $this->access->readAttribute($this->getDN(), $this->connection->ldapExtStorageHomeAttribute);
688
+        } else {
689
+            $extHomeValues = [$valueFromLDAP];
690
+        }
691
+        if ($extHomeValues && isset($extHomeValues[0])) {
692
+            $extHome = $extHomeValues[0];
693
+            $this->config->setUserValue($this->getUsername(), 'user_ldap', 'extStorageHome', $extHome);
694
+            return $extHome;
695
+        } else {
696
+            $this->config->deleteUserValue($this->getUsername(), 'user_ldap', 'extStorageHome');
697
+            return '';
698
+        }
699
+    }
700
+
701
+    /**
702
+     * called by a post_login hook to handle password expiry
703
+     *
704
+     * @param array $params
705
+     */
706
+    public function handlePasswordExpiry($params) {
707
+        $ppolicyDN = $this->connection->ldapDefaultPPolicyDN;
708
+        if (empty($ppolicyDN) || ((int)$this->connection->turnOnPasswordChange !== 1)) {
709
+            return;//password expiry handling disabled
710
+        }
711
+        $uid = $params['uid'];
712
+        if (isset($uid) && $uid === $this->getUsername()) {
713
+            //retrieve relevant user attributes
714
+            $result = $this->access->search('objectclass=*', array($this->dn), ['pwdpolicysubentry', 'pwdgraceusetime', 'pwdreset', 'pwdchangedtime']);
715
+
716
+            if (array_key_exists('pwdpolicysubentry', $result[0])) {
717
+                $pwdPolicySubentry = $result[0]['pwdpolicysubentry'];
718
+                if ($pwdPolicySubentry && (count($pwdPolicySubentry) > 0)){
719
+                    $ppolicyDN = $pwdPolicySubentry[0];//custom ppolicy DN
720
+                }
721
+            }
722
+
723
+            $pwdGraceUseTime = array_key_exists('pwdgraceusetime', $result[0]) ? $result[0]['pwdgraceusetime'] : [];
724
+            $pwdReset = array_key_exists('pwdreset', $result[0]) ? $result[0]['pwdreset'] : [];
725
+            $pwdChangedTime = array_key_exists('pwdchangedtime', $result[0]) ? $result[0]['pwdchangedtime'] : [];
726
+
727
+            //retrieve relevant password policy attributes
728
+            $cacheKey = 'ppolicyAttributes' . $ppolicyDN;
729
+            $result = $this->connection->getFromCache($cacheKey);
730
+            if(is_null($result)) {
731
+                $result = $this->access->search('objectclass=*', array($ppolicyDN), ['pwdgraceauthnlimit', 'pwdmaxage', 'pwdexpirewarning']);
732
+                $this->connection->writeToCache($cacheKey, $result);
733
+            }
734
+
735
+            $pwdGraceAuthNLimit = array_key_exists('pwdgraceauthnlimit', $result[0]) ? $result[0]['pwdgraceauthnlimit'] : [];
736
+            $pwdMaxAge = array_key_exists('pwdmaxage', $result[0]) ? $result[0]['pwdmaxage'] : [];
737
+            $pwdExpireWarning = array_key_exists('pwdexpirewarning', $result[0]) ? $result[0]['pwdexpirewarning'] : [];
738
+
739
+            //handle grace login
740
+            if (!empty($pwdGraceUseTime)) { //was this a grace login?
741
+                if (!empty($pwdGraceAuthNLimit)
742
+                    && count($pwdGraceUseTime) < (int)$pwdGraceAuthNLimit[0]) { //at least one more grace login available?
743
+                    $this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
744
+                    header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
745
+                    'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid)));
746
+                } else { //no more grace login available
747
+                    header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
748
+                    'user_ldap.renewPassword.showLoginFormInvalidPassword', array('user' => $uid)));
749
+                }
750
+                exit();
751
+            }
752
+            //handle pwdReset attribute
753
+            if (!empty($pwdReset) && $pwdReset[0] === 'TRUE') { //user must change his password
754
+                $this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
755
+                header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
756
+                'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid)));
757
+                exit();
758
+            }
759
+            //handle password expiry warning
760
+            if (!empty($pwdChangedTime)) {
761
+                if (!empty($pwdMaxAge)
762
+                    && !empty($pwdExpireWarning)) {
763
+                    $pwdMaxAgeInt = (int)$pwdMaxAge[0];
764
+                    $pwdExpireWarningInt = (int)$pwdExpireWarning[0];
765
+                    if ($pwdMaxAgeInt > 0 && $pwdExpireWarningInt > 0){
766
+                        $pwdChangedTimeDt = \DateTime::createFromFormat('YmdHisZ', $pwdChangedTime[0]);
767
+                        $pwdChangedTimeDt->add(new \DateInterval('PT'.$pwdMaxAgeInt.'S'));
768
+                        $currentDateTime = new \DateTime();
769
+                        $secondsToExpiry = $pwdChangedTimeDt->getTimestamp() - $currentDateTime->getTimestamp();
770
+                        if ($secondsToExpiry <= $pwdExpireWarningInt) {
771
+                            //remove last password expiry warning if any
772
+                            $notification = $this->notificationManager->createNotification();
773
+                            $notification->setApp('user_ldap')
774
+                                ->setUser($uid)
775
+                                ->setObject('pwd_exp_warn', $uid)
776
+                            ;
777
+                            $this->notificationManager->markProcessed($notification);
778
+                            //create new password expiry warning
779
+                            $notification = $this->notificationManager->createNotification();
780
+                            $notification->setApp('user_ldap')
781
+                                ->setUser($uid)
782
+                                ->setDateTime($currentDateTime)
783
+                                ->setObject('pwd_exp_warn', $uid)
784
+                                ->setSubject('pwd_exp_warn_days', [(int) ceil($secondsToExpiry / 60 / 60 / 24)])
785
+                            ;
786
+                            $this->notificationManager->notify($notification);
787
+                        }
788
+                    }
789
+                }
790
+            }
791
+        }
792
+    }
793 793
 }
Please login to merge, or discard this patch.
Spacing   +70 added lines, -70 removed lines patch added patch discarded remove patch
@@ -154,17 +154,17 @@  discard block
 block discarded – undo
154 154
 	 * @return null
155 155
 	 */
156 156
 	public function update() {
157
-		if(is_null($this->dn)) {
157
+		if (is_null($this->dn)) {
158 158
 			return null;
159 159
 		}
160 160
 
161 161
 		$hasLoggedIn = $this->config->getUserValue($this->uid, 'user_ldap',
162 162
 				self::USER_PREFKEY_FIRSTLOGIN, 0);
163 163
 
164
-		if($this->needsRefresh()) {
164
+		if ($this->needsRefresh()) {
165 165
 			$this->updateEmail();
166 166
 			$this->updateQuota();
167
-			if($hasLoggedIn !== 0) {
167
+			if ($hasLoggedIn !== 0) {
168 168
 				//we do not need to try it, when the user has not been logged in
169 169
 				//before, because the file system will not be ready.
170 170
 				$this->updateAvatar();
@@ -182,12 +182,12 @@  discard block
 block discarded – undo
182 182
 	 */
183 183
 	public function markUser() {
184 184
 		$curValue = $this->config->getUserValue($this->getUsername(), 'user_ldap', 'isDeleted', '0');
185
-		if($curValue === '1') {
185
+		if ($curValue === '1') {
186 186
 			// the user is already marked, do not write to DB again
187 187
 			return;
188 188
 		}
189 189
 		$this->config->setUserValue($this->getUsername(), 'user_ldap', 'isDeleted', '1');
190
-		$this->config->setUserValue($this->getUsername(), 'user_ldap', 'foundDeleted', (string)time());
190
+		$this->config->setUserValue($this->getUsername(), 'user_ldap', 'foundDeleted', (string) time());
191 191
 	}
192 192
 
193 193
 	/**
@@ -198,7 +198,7 @@  discard block
 block discarded – undo
198 198
 		$this->markRefreshTime();
199 199
 		//Quota
200 200
 		$attr = strtolower($this->connection->ldapQuotaAttribute);
201
-		if(isset($ldapEntry[$attr])) {
201
+		if (isset($ldapEntry[$attr])) {
202 202
 			$this->updateQuota($ldapEntry[$attr][0]);
203 203
 		} else {
204 204
 			if ($this->connection->ldapQuotaDefault !== '') {
@@ -210,12 +210,12 @@  discard block
 block discarded – undo
210 210
 		//displayName
211 211
 		$displayName = $displayName2 = '';
212 212
 		$attr = strtolower($this->connection->ldapUserDisplayName);
213
-		if(isset($ldapEntry[$attr])) {
214
-			$displayName = (string)$ldapEntry[$attr][0];
213
+		if (isset($ldapEntry[$attr])) {
214
+			$displayName = (string) $ldapEntry[$attr][0];
215 215
 		}
216 216
 		$attr = strtolower($this->connection->ldapUserDisplayName2);
217
-		if(isset($ldapEntry[$attr])) {
218
-			$displayName2 = (string)$ldapEntry[$attr][0];
217
+		if (isset($ldapEntry[$attr])) {
218
+			$displayName2 = (string) $ldapEntry[$attr][0];
219 219
 		}
220 220
 		if ($displayName !== '') {
221 221
 			$this->composeAndStoreDisplayName($displayName, $displayName2);
@@ -231,22 +231,22 @@  discard block
 block discarded – undo
231 231
 		//email must be stored after displayname, because it would cause a user
232 232
 		//change event that will trigger fetching the display name again
233 233
 		$attr = strtolower($this->connection->ldapEmailAttribute);
234
-		if(isset($ldapEntry[$attr])) {
234
+		if (isset($ldapEntry[$attr])) {
235 235
 			$this->updateEmail($ldapEntry[$attr][0]);
236 236
 		}
237 237
 		unset($attr);
238 238
 
239 239
 		// LDAP Username, needed for s2s sharing
240
-		if(isset($ldapEntry['uid'])) {
240
+		if (isset($ldapEntry['uid'])) {
241 241
 			$this->storeLDAPUserName($ldapEntry['uid'][0]);
242
-		} else if(isset($ldapEntry['samaccountname'])) {
242
+		} else if (isset($ldapEntry['samaccountname'])) {
243 243
 			$this->storeLDAPUserName($ldapEntry['samaccountname'][0]);
244 244
 		}
245 245
 
246 246
 		//homePath
247
-		if(strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) {
247
+		if (strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) {
248 248
 			$attr = strtolower(substr($this->connection->homeFolderNamingRule, strlen('attr:')));
249
-			if(isset($ldapEntry[$attr])) {
249
+			if (isset($ldapEntry[$attr])) {
250 250
 				$this->access->cacheUserHome(
251 251
 					$this->getUsername(), $this->getHomePath($ldapEntry[$attr][0]));
252 252
 			}
@@ -255,14 +255,14 @@  discard block
 block discarded – undo
255 255
 		//memberOf groups
256 256
 		$cacheKey = 'getMemberOf'.$this->getUsername();
257 257
 		$groups = false;
258
-		if(isset($ldapEntry['memberof'])) {
258
+		if (isset($ldapEntry['memberof'])) {
259 259
 			$groups = $ldapEntry['memberof'];
260 260
 		}
261 261
 		$this->connection->writeToCache($cacheKey, $groups);
262 262
 
263 263
 		//external storage var
264 264
 		$attr = strtolower($this->connection->ldapExtStorageHomeAttribute);
265
-		if(isset($ldapEntry[$attr])) {
265
+		if (isset($ldapEntry[$attr])) {
266 266
 			$this->updateExtStorageHome($ldapEntry[$attr][0]);
267 267
 		}
268 268
 		unset($attr);
@@ -271,8 +271,8 @@  discard block
 block discarded – undo
271 271
 		/** @var Connection $connection */
272 272
 		$connection = $this->access->getConnection();
273 273
 		$attributes = $connection->resolveRule('avatar');
274
-		foreach ($attributes as $attribute)  {
275
-			if(isset($ldapEntry[$attribute])) {
274
+		foreach ($attributes as $attribute) {
275
+			if (isset($ldapEntry[$attribute])) {
276 276
 				$this->avatarImage = $ldapEntry[$attribute][0];
277 277
 				// the call to the method that saves the avatar in the file
278 278
 				// system must be postponed after the login. It is to ensure
@@ -307,7 +307,7 @@  discard block
 block discarded – undo
307 307
 	 * @throws \Exception
308 308
 	 */
309 309
 	public function getHomePath($valueFromLDAP = null) {
310
-		$path = (string)$valueFromLDAP;
310
+		$path = (string) $valueFromLDAP;
311 311
 		$attr = null;
312 312
 
313 313
 		if (is_null($valueFromLDAP)
@@ -325,12 +325,12 @@  discard block
 block discarded – undo
325 325
 		if ($path !== '') {
326 326
 			//if attribute's value is an absolute path take this, otherwise append it to data dir
327 327
 			//check for / at the beginning or pattern c:\ resp. c:/
328
-			if(   '/' !== $path[0]
328
+			if ('/' !== $path[0]
329 329
 			   && !(3 < strlen($path) && ctype_alpha($path[0])
330 330
 			       && $path[1] === ':' && ('\\' === $path[2] || '/' === $path[2]))
331 331
 			) {
332 332
 				$path = $this->config->getSystemValue('datadirectory',
333
-						\OC::$SERVERROOT.'/data' ) . '/' . $path;
333
+						\OC::$SERVERROOT.'/data').'/'.$path;
334 334
 			}
335 335
 			//we need it to store it in the DB as well in case a user gets
336 336
 			//deleted so we can clean up afterwards
@@ -340,11 +340,11 @@  discard block
 block discarded – undo
340 340
 			return $path;
341 341
 		}
342 342
 
343
-		if(    !is_null($attr)
343
+		if (!is_null($attr)
344 344
 			&& $this->config->getAppValue('user_ldap', 'enforce_home_folder_naming_rule', true)
345 345
 		) {
346 346
 			// a naming rule attribute is defined, but it doesn't exist for that LDAP user
347
-			throw new \Exception('Home dir attribute can\'t be read from LDAP for uid: ' . $this->getUsername());
347
+			throw new \Exception('Home dir attribute can\'t be read from LDAP for uid: '.$this->getUsername());
348 348
 		}
349 349
 
350 350
 		//false will apply default behaviour as defined and done by OC_User
@@ -355,7 +355,7 @@  discard block
 block discarded – undo
355 355
 	public function getMemberOfGroups() {
356 356
 		$cacheKey = 'getMemberOf'.$this->getUsername();
357 357
 		$memberOfGroups = $this->connection->getFromCache($cacheKey);
358
-		if(!is_null($memberOfGroups)) {
358
+		if (!is_null($memberOfGroups)) {
359 359
 			return $memberOfGroups;
360 360
 		}
361 361
 		$groupDNs = $this->access->readAttribute($this->getDN(), 'memberOf');
@@ -368,7 +368,7 @@  discard block
 block discarded – undo
368 368
 	 * @return string data (provided by LDAP) | false
369 369
 	 */
370 370
 	public function getAvatarImage() {
371
-		if(!is_null($this->avatarImage)) {
371
+		if (!is_null($this->avatarImage)) {
372 372
 			return $this->avatarImage;
373 373
 		}
374 374
 
@@ -376,9 +376,9 @@  discard block
 block discarded – undo
376 376
 		/** @var Connection $connection */
377 377
 		$connection = $this->access->getConnection();
378 378
 		$attributes = $connection->resolveRule('avatar');
379
-		foreach($attributes as $attribute) {
379
+		foreach ($attributes as $attribute) {
380 380
 			$result = $this->access->readAttribute($this->dn, $attribute);
381
-			if($result !== false && is_array($result) && isset($result[0])) {
381
+			if ($result !== false && is_array($result) && isset($result[0])) {
382 382
 				$this->avatarImage = $result[0];
383 383
 				break;
384 384
 			}
@@ -415,7 +415,7 @@  discard block
 block discarded – undo
415 415
 		$lastChecked = $this->config->getUserValue($this->uid, 'user_ldap',
416 416
 			self::USER_PREFKEY_LASTREFRESH, 0);
417 417
 
418
-		if((time() - (int)$lastChecked) < (int)$this->config->getAppValue('user_ldap', 'updateAttributesInterval', 86400)) {
418
+		if ((time() - (int) $lastChecked) < (int) $this->config->getAppValue('user_ldap', 'updateAttributesInterval', 86400)) {
419 419
 			return false;
420 420
 		}
421 421
 		return  true;
@@ -440,12 +440,12 @@  discard block
 block discarded – undo
440 440
 	 * @return string the effective display name
441 441
 	 */
442 442
 	public function composeAndStoreDisplayName($displayName, $displayName2 = '') {
443
-		$displayName2 = (string)$displayName2;
444
-		if($displayName2 !== '') {
445
-			$displayName .= ' (' . $displayName2 . ')';
443
+		$displayName2 = (string) $displayName2;
444
+		if ($displayName2 !== '') {
445
+			$displayName .= ' ('.$displayName2.')';
446 446
 		}
447 447
 		$oldName = $this->config->getUserValue($this->uid, 'user_ldap', 'displayName', null);
448
-		if ($oldName !== $displayName)  {
448
+		if ($oldName !== $displayName) {
449 449
 			$this->store('displayName', $displayName);
450 450
 			$user = $this->userManager->get($this->getUsername());
451 451
 			if (!empty($oldName) && $user instanceof \OC\User\User) {
@@ -473,7 +473,7 @@  discard block
 block discarded – undo
473 473
 	 * @return bool
474 474
 	 */
475 475
 	private function wasRefreshed($feature) {
476
-		if(isset($this->refreshedFeatures[$feature])) {
476
+		if (isset($this->refreshedFeatures[$feature])) {
477 477
 			return true;
478 478
 		}
479 479
 		$this->refreshedFeatures[$feature] = 1;
@@ -486,23 +486,23 @@  discard block
 block discarded – undo
486 486
 	 * @return null
487 487
 	 */
488 488
 	public function updateEmail($valueFromLDAP = null) {
489
-		if($this->wasRefreshed('email')) {
489
+		if ($this->wasRefreshed('email')) {
490 490
 			return;
491 491
 		}
492
-		$email = (string)$valueFromLDAP;
493
-		if(is_null($valueFromLDAP)) {
492
+		$email = (string) $valueFromLDAP;
493
+		if (is_null($valueFromLDAP)) {
494 494
 			$emailAttribute = $this->connection->ldapEmailAttribute;
495 495
 			if ($emailAttribute !== '') {
496 496
 				$aEmail = $this->access->readAttribute($this->dn, $emailAttribute);
497
-				if(is_array($aEmail) && (count($aEmail) > 0)) {
498
-					$email = (string)$aEmail[0];
497
+				if (is_array($aEmail) && (count($aEmail) > 0)) {
498
+					$email = (string) $aEmail[0];
499 499
 				}
500 500
 			}
501 501
 		}
502 502
 		if ($email !== '') {
503 503
 			$user = $this->userManager->get($this->uid);
504 504
 			if (!is_null($user)) {
505
-				$currentEmail = (string)$user->getEMailAddress();
505
+				$currentEmail = (string) $user->getEMailAddress();
506 506
 				if ($currentEmail !== $email) {
507 507
 					$user->setEMailAddress($email);
508 508
 				}
@@ -531,35 +531,35 @@  discard block
 block discarded – undo
531 531
 	 * @return null
532 532
 	 */
533 533
 	public function updateQuota($valueFromLDAP = null) {
534
-		if($this->wasRefreshed('quota')) {
534
+		if ($this->wasRefreshed('quota')) {
535 535
 			return;
536 536
 		}
537 537
 
538 538
 		$quotaAttribute = $this->connection->ldapQuotaAttribute;
539 539
 		$defaultQuota = $this->connection->ldapQuotaDefault;
540
-		if($quotaAttribute === '' && $defaultQuota === '') {
540
+		if ($quotaAttribute === '' && $defaultQuota === '') {
541 541
 			return;
542 542
 		}
543 543
 
544 544
 		$quota = false;
545
-		if(is_null($valueFromLDAP) && $quotaAttribute !== '') {
545
+		if (is_null($valueFromLDAP) && $quotaAttribute !== '') {
546 546
 			$aQuota = $this->access->readAttribute($this->dn, $quotaAttribute);
547
-			if($aQuota && (count($aQuota) > 0) && $this->verifyQuotaValue($aQuota[0])) {
547
+			if ($aQuota && (count($aQuota) > 0) && $this->verifyQuotaValue($aQuota[0])) {
548 548
 				$quota = $aQuota[0];
549
-			} else if(is_array($aQuota) && isset($aQuota[0])) {
550
-				$this->log->log('no suitable LDAP quota found for user ' . $this->uid . ': [' . $aQuota[0] . ']', ILogger::DEBUG);
549
+			} else if (is_array($aQuota) && isset($aQuota[0])) {
550
+				$this->log->log('no suitable LDAP quota found for user '.$this->uid.': ['.$aQuota[0].']', ILogger::DEBUG);
551 551
 			}
552 552
 		} else if ($this->verifyQuotaValue($valueFromLDAP)) {
553 553
 			$quota = $valueFromLDAP;
554 554
 		} else {
555
-			$this->log->log('no suitable LDAP quota found for user ' . $this->uid . ': [' . $valueFromLDAP . ']', ILogger::DEBUG);
555
+			$this->log->log('no suitable LDAP quota found for user '.$this->uid.': ['.$valueFromLDAP.']', ILogger::DEBUG);
556 556
 		}
557 557
 
558 558
 		if ($quota === false && $this->verifyQuotaValue($defaultQuota)) {
559 559
 			// quota not found using the LDAP attribute (or not parseable). Try the default quota
560 560
 			$quota = $defaultQuota;
561
-		} else if($quota === false) {
562
-			$this->log->log('no suitable default quota found for user ' . $this->uid . ': [' . $defaultQuota . ']', ILogger::DEBUG);
561
+		} else if ($quota === false) {
562
+			$this->log->log('no suitable default quota found for user '.$this->uid.': ['.$defaultQuota.']', ILogger::DEBUG);
563 563
 			return;
564 564
 		}
565 565
 
@@ -567,7 +567,7 @@  discard block
 block discarded – undo
567 567
 		if ($targetUser instanceof IUser) {
568 568
 			$targetUser->setQuota($quota);
569 569
 		} else {
570
-			$this->log->log('trying to set a quota for user ' . $this->uid . ' but the user is missing', ILogger::INFO);
570
+			$this->log->log('trying to set a quota for user '.$this->uid.' but the user is missing', ILogger::INFO);
571 571
 		}
572 572
 	}
573 573
 
@@ -581,7 +581,7 @@  discard block
 block discarded – undo
581 581
 	 * @param array $params
582 582
 	 */
583 583
 	public function updateAvatarPostLogin($params) {
584
-		if(isset($params['uid']) && $params['uid'] === $this->getUsername()) {
584
+		if (isset($params['uid']) && $params['uid'] === $this->getUsername()) {
585 585
 			$this->updateAvatar();
586 586
 		}
587 587
 	}
@@ -591,29 +591,29 @@  discard block
 block discarded – undo
591 591
 	 * @return bool
592 592
 	 */
593 593
 	public function updateAvatar($force = false) {
594
-		if(!$force && $this->wasRefreshed('avatar')) {
594
+		if (!$force && $this->wasRefreshed('avatar')) {
595 595
 			return false;
596 596
 		}
597 597
 		$avatarImage = $this->getAvatarImage();
598
-		if($avatarImage === false) {
598
+		if ($avatarImage === false) {
599 599
 			//not set, nothing left to do;
600 600
 			return false;
601 601
 		}
602 602
 
603
-		if(!$this->image->loadFromBase64(base64_encode($avatarImage))) {
603
+		if (!$this->image->loadFromBase64(base64_encode($avatarImage))) {
604 604
 			return false;
605 605
 		}
606 606
 
607 607
 		// use the checksum before modifications
608 608
 		$checksum = md5($this->image->data());
609 609
 
610
-		if($checksum === $this->config->getUserValue($this->uid, 'user_ldap', 'lastAvatarChecksum', '')) {
610
+		if ($checksum === $this->config->getUserValue($this->uid, 'user_ldap', 'lastAvatarChecksum', '')) {
611 611
 			return true;
612 612
 		}
613 613
 
614 614
 		$isSet = $this->setOwnCloudAvatar();
615 615
 
616
-		if($isSet) {
616
+		if ($isSet) {
617 617
 			// save checksum only after successful setting
618 618
 			$this->config->setUserValue($this->uid, 'user_ldap', 'lastAvatarChecksum', $checksum);
619 619
 		}
@@ -626,7 +626,7 @@  discard block
 block discarded – undo
626 626
 	 * @return bool
627 627
 	 */
628 628
 	private function setOwnCloudAvatar() {
629
-		if(!$this->image->valid()) {
629
+		if (!$this->image->valid()) {
630 630
 			$this->log->log('avatar image data from LDAP invalid for '.$this->dn, ILogger::ERROR);
631 631
 			return false;
632 632
 		}
@@ -634,12 +634,12 @@  discard block
 block discarded – undo
634 634
 
635 635
 		//make sure it is a square and not bigger than 128x128
636 636
 		$size = min([$this->image->width(), $this->image->height(), 128]);
637
-		if(!$this->image->centerCrop($size)) {
637
+		if (!$this->image->centerCrop($size)) {
638 638
 			$this->log->log('croping image for avatar failed for '.$this->dn, ILogger::ERROR);
639 639
 			return false;
640 640
 		}
641 641
 
642
-		if(!$this->fs->isLoaded()) {
642
+		if (!$this->fs->isLoaded()) {
643 643
 			$this->fs->setup($this->uid);
644 644
 		}
645 645
 
@@ -649,7 +649,7 @@  discard block
 block discarded – undo
649 649
 			return true;
650 650
 		} catch (\Exception $e) {
651 651
 			\OC::$server->getLogger()->logException($e, [
652
-				'message' => 'Could not set avatar for ' . $this->dn,
652
+				'message' => 'Could not set avatar for '.$this->dn,
653 653
 				'level' => ILogger::INFO,
654 654
 				'app' => 'user_ldap',
655 655
 			]);
@@ -705,8 +705,8 @@  discard block
 block discarded – undo
705 705
 	 */
706 706
 	public function handlePasswordExpiry($params) {
707 707
 		$ppolicyDN = $this->connection->ldapDefaultPPolicyDN;
708
-		if (empty($ppolicyDN) || ((int)$this->connection->turnOnPasswordChange !== 1)) {
709
-			return;//password expiry handling disabled
708
+		if (empty($ppolicyDN) || ((int) $this->connection->turnOnPasswordChange !== 1)) {
709
+			return; //password expiry handling disabled
710 710
 		}
711 711
 		$uid = $params['uid'];
712 712
 		if (isset($uid) && $uid === $this->getUsername()) {
@@ -715,8 +715,8 @@  discard block
 block discarded – undo
715 715
 
716 716
 			if (array_key_exists('pwdpolicysubentry', $result[0])) {
717 717
 				$pwdPolicySubentry = $result[0]['pwdpolicysubentry'];
718
-				if ($pwdPolicySubentry && (count($pwdPolicySubentry) > 0)){
719
-					$ppolicyDN = $pwdPolicySubentry[0];//custom ppolicy DN
718
+				if ($pwdPolicySubentry && (count($pwdPolicySubentry) > 0)) {
719
+					$ppolicyDN = $pwdPolicySubentry[0]; //custom ppolicy DN
720 720
 				}
721 721
 			}
722 722
 
@@ -725,9 +725,9 @@  discard block
 block discarded – undo
725 725
 			$pwdChangedTime = array_key_exists('pwdchangedtime', $result[0]) ? $result[0]['pwdchangedtime'] : [];
726 726
 
727 727
 			//retrieve relevant password policy attributes
728
-			$cacheKey = 'ppolicyAttributes' . $ppolicyDN;
728
+			$cacheKey = 'ppolicyAttributes'.$ppolicyDN;
729 729
 			$result = $this->connection->getFromCache($cacheKey);
730
-			if(is_null($result)) {
730
+			if (is_null($result)) {
731 731
 				$result = $this->access->search('objectclass=*', array($ppolicyDN), ['pwdgraceauthnlimit', 'pwdmaxage', 'pwdexpirewarning']);
732 732
 				$this->connection->writeToCache($cacheKey, $result);
733 733
 			}
@@ -739,7 +739,7 @@  discard block
 block discarded – undo
739 739
 			//handle grace login
740 740
 			if (!empty($pwdGraceUseTime)) { //was this a grace login?
741 741
 				if (!empty($pwdGraceAuthNLimit)
742
-					&& count($pwdGraceUseTime) < (int)$pwdGraceAuthNLimit[0]) { //at least one more grace login available?
742
+					&& count($pwdGraceUseTime) < (int) $pwdGraceAuthNLimit[0]) { //at least one more grace login available?
743 743
 					$this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
744 744
 					header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
745 745
 					'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid)));
@@ -760,9 +760,9 @@  discard block
 block discarded – undo
760 760
 			if (!empty($pwdChangedTime)) {
761 761
 				if (!empty($pwdMaxAge)
762 762
 					&& !empty($pwdExpireWarning)) {
763
-					$pwdMaxAgeInt = (int)$pwdMaxAge[0];
764
-					$pwdExpireWarningInt = (int)$pwdExpireWarning[0];
765
-					if ($pwdMaxAgeInt > 0 && $pwdExpireWarningInt > 0){
763
+					$pwdMaxAgeInt = (int) $pwdMaxAge[0];
764
+					$pwdExpireWarningInt = (int) $pwdExpireWarning[0];
765
+					if ($pwdMaxAgeInt > 0 && $pwdExpireWarningInt > 0) {
766 766
 						$pwdChangedTimeDt = \DateTime::createFromFormat('YmdHisZ', $pwdChangedTime[0]);
767 767
 						$pwdChangedTimeDt->add(new \DateInterval('PT'.$pwdMaxAgeInt.'S'));
768 768
 						$currentDateTime = new \DateTime();
Please login to merge, or discard this patch.