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