Completed
Pull Request — master (#3869)
by Björn
15:39
created
settings/BackgroundJobs/VerifyUserData.php 1 patch
Indentation   +234 added lines, -234 removed lines patch added patch discarded remove patch
@@ -35,239 +35,239 @@
 block discarded – undo
35 35
 
36 36
 class VerifyUserData extends Job {
37 37
 
38
-	/** @var  bool */
39
-	private $retainJob = true;
40
-
41
-	/** @var int max number of attempts to send the request */
42
-	private $maxTry = 24;
43
-
44
-	/** @var int how much time should be between two tries (1 hour) */
45
-	private $interval = 3600;
46
-
47
-	/** @var AccountManager */
48
-	private $accountManager;
49
-
50
-	/** @var IUserManager */
51
-	private $userManager;
52
-
53
-	/** @var IClientService */
54
-	private $httpClientService;
55
-
56
-	/** @var ILogger */
57
-	private $logger;
58
-
59
-	/** @var string */
60
-	private $lookupServerUrl;
61
-
62
-	/**
63
-	 * VerifyUserData constructor.
64
-	 *
65
-	 * @param AccountManager $accountManager
66
-	 * @param IUserManager $userManager
67
-	 * @param IClientService $clientService
68
-	 * @param ILogger $logger
69
-	 * @param IConfig $config
70
-	 */
71
-	public function __construct(AccountManager $accountManager,
72
-								IUserManager $userManager,
73
-								IClientService $clientService,
74
-								ILogger $logger,
75
-								IConfig $config
76
-	) {
77
-		$this->accountManager = $accountManager;
78
-		$this->userManager = $userManager;
79
-		$this->httpClientService = $clientService;
80
-		$this->logger = $logger;
81
-
82
-		$lookupServerUrl = $config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
83
-		$this->lookupServerUrl = rtrim($lookupServerUrl, '/');
84
-	}
85
-
86
-	/**
87
-	 * run the job, then remove it from the jobList
88
-	 *
89
-	 * @param JobList $jobList
90
-	 * @param ILogger $logger
91
-	 */
92
-	public function execute($jobList, ILogger $logger = null) {
93
-
94
-		if ($this->shouldRun($this->argument)) {
95
-			parent::execute($jobList, $logger);
96
-			$jobList->remove($this, $this->argument);
97
-			if ($this->retainJob) {
98
-				$this->reAddJob($jobList, $this->argument);
99
-			}
100
-		}
101
-
102
-	}
103
-
104
-	protected function run($argument) {
105
-
106
-		$try = (int)$argument['try'] + 1;
107
-
108
-		switch($argument['type']) {
109
-			case AccountManager::PROPERTY_WEBSITE:
110
-				$result = $this->verifyWebsite($argument);
111
-				break;
112
-			case AccountManager::PROPERTY_TWITTER:
113
-			case AccountManager::PROPERTY_EMAIL:
114
-				$result = $this->verifyViaLookupServer($argument, $argument['type']);
115
-				break;
116
-			default:
117
-				// no valid type given, no need to retry
118
-				$this->logger->error($argument['type'] . ' is no valid type for user account data.');
119
-				$result = true;
120
-		}
121
-
122
-		if ($result === true || $try > $this->maxTry) {
123
-			$this->retainJob = false;
124
-		}
125
-	}
126
-
127
-	/**
128
-	 * verify web page
129
-	 *
130
-	 * @param array $argument
131
-	 * @return bool true if we could check the verification code, otherwise false
132
-	 */
133
-	protected function verifyWebsite(array $argument) {
134
-
135
-		$result = false;
136
-
137
-		$url = rtrim($argument['data'], '/') . '/.well-known/' . 'CloudIdVerificationCode.txt';
138
-
139
-		$client = $this->httpClientService->newClient();
140
-		try {
141
-			$response = $client->get($url);
142
-		} catch (\Exception $e) {
143
-			return false;
144
-		}
145
-
146
-		if ($response->getStatusCode() === Http::STATUS_OK) {
147
-			$result = true;
148
-			$publishedCode = $response->getBody();
149
-			// remove new lines and spaces
150
-			$publishedCodeSanitized = trim(preg_replace('/\s\s+/', ' ', $publishedCode));
151
-			$user = $this->userManager->get($argument['uid']);
152
-			// we don't check a valid user -> give up
153
-			if ($user === null) {
154
-				$this->logger->error($argument['uid'] . ' doesn\'t exist, can\'t verify user data.');
155
-				return $result;
156
-			}
157
-			$userData = $this->accountManager->getUser($user);
158
-
159
-			if ($publishedCodeSanitized === $argument['verificationCode']) {
160
-				$userData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFIED;
161
-			} else {
162
-				$userData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::NOT_VERIFIED;
163
-			}
164
-
165
-			$this->accountManager->updateUser($user, $userData);
166
-		}
167
-
168
-		return $result;
169
-	}
170
-
171
-	/**
172
-	 * verify email address
173
-	 *
174
-	 * @param array $argument
175
-	 * @param string $dataType
176
-	 * @return bool true if we could check the verification code, otherwise false
177
-	 */
178
-	protected function verifyViaLookupServer(array $argument, $dataType) {
179
-
180
-		$user = $this->userManager->get($argument['uid']);
181
-
182
-		// we don't check a valid user -> give up
183
-		if ($user === null) {
184
-			$this->logger->error($argument['uid'] . ' doesn\'t exist, can\'t verify user data.');
185
-			return true;
186
-		}
187
-
188
-		$localUserData = $this->accountManager->getUser($user);
189
-		$cloudId = $user->getCloudId();
190
-
191
-		// ask lookup-server for user data
192
-		$lookupServerData = $this->queryLookupServer($cloudId);
193
-
194
-		// for some reasons we couldn't read any data from the lookup server, try again later
195
-		if (empty($lookupServerData)) {
196
-			return false;
197
-		}
198
-
199
-		// lookup server has verification data for wrong user data (e.g. email address), try again later
200
-		if ($lookupServerData[$dataType]['value'] !== $argument['data']) {
201
-			return false;
202
-		}
203
-
204
-		// lookup server hasn't verified the email address so far, try again later
205
-		if ($lookupServerData[$dataType]['verified'] === AccountManager::NOT_VERIFIED) {
206
-			return false;
207
-		}
208
-
209
-		$localUserData[$dataType]['verified'] = AccountManager::VERIFIED;
210
-		$this->accountManager->updateUser($user, $localUserData);
211
-
212
-		return true;
213
-	}
214
-
215
-	/**
216
-	 * @param string $cloudId
217
-	 * @return array
218
-	 */
219
-	protected function queryLookupServer($cloudId) {
220
-		try {
221
-			$client = $this->httpClientService->newClient();
222
-			$response = $client->get(
223
-				$this->lookupServerUrl . '/users?search=' . urlencode($cloudId) . '&exactCloudId=1',
224
-				[
225
-					'timeout' => 10,
226
-					'connect_timeout' => 3,
227
-				]
228
-			);
229
-
230
-			$body = json_decode($response->getBody(), true);
231
-
232
-			if ($body['federationId'] === $cloudId) {
233
-				return $body;
234
-			}
235
-
236
-		} catch (\Exception $e) {
237
-			// do nothing, we will just re-try later
238
-		}
239
-
240
-		return [];
241
-	}
242
-
243
-	/**
244
-	 * re-add background job with new arguments
245
-	 *
246
-	 * @param IJobList $jobList
247
-	 * @param array $argument
248
-	 */
249
-	protected function reAddJob(IJobList $jobList, array $argument) {
250
-		$jobList->add('OC\Settings\BackgroundJobs\VerifyUserData',
251
-			[
252
-				'verificationCode' => $argument['verificationCode'],
253
-				'data' => $argument['data'],
254
-				'type' => $argument['type'],
255
-				'uid' => $argument['uid'],
256
-				'try' => (int)$argument['try'] + 1,
257
-				'lastRun' => time()
258
-			]
259
-		);
260
-	}
261
-
262
-	/**
263
-	 * test if it is time for the next run
264
-	 *
265
-	 * @param array $argument
266
-	 * @return bool
267
-	 */
268
-	protected function shouldRun(array $argument) {
269
-		$lastRun = (int)$argument['lastRun'];
270
-		return ((time() - $lastRun) > $this->interval);
271
-	}
38
+    /** @var  bool */
39
+    private $retainJob = true;
40
+
41
+    /** @var int max number of attempts to send the request */
42
+    private $maxTry = 24;
43
+
44
+    /** @var int how much time should be between two tries (1 hour) */
45
+    private $interval = 3600;
46
+
47
+    /** @var AccountManager */
48
+    private $accountManager;
49
+
50
+    /** @var IUserManager */
51
+    private $userManager;
52
+
53
+    /** @var IClientService */
54
+    private $httpClientService;
55
+
56
+    /** @var ILogger */
57
+    private $logger;
58
+
59
+    /** @var string */
60
+    private $lookupServerUrl;
61
+
62
+    /**
63
+     * VerifyUserData constructor.
64
+     *
65
+     * @param AccountManager $accountManager
66
+     * @param IUserManager $userManager
67
+     * @param IClientService $clientService
68
+     * @param ILogger $logger
69
+     * @param IConfig $config
70
+     */
71
+    public function __construct(AccountManager $accountManager,
72
+                                IUserManager $userManager,
73
+                                IClientService $clientService,
74
+                                ILogger $logger,
75
+                                IConfig $config
76
+    ) {
77
+        $this->accountManager = $accountManager;
78
+        $this->userManager = $userManager;
79
+        $this->httpClientService = $clientService;
80
+        $this->logger = $logger;
81
+
82
+        $lookupServerUrl = $config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
83
+        $this->lookupServerUrl = rtrim($lookupServerUrl, '/');
84
+    }
85
+
86
+    /**
87
+     * run the job, then remove it from the jobList
88
+     *
89
+     * @param JobList $jobList
90
+     * @param ILogger $logger
91
+     */
92
+    public function execute($jobList, ILogger $logger = null) {
93
+
94
+        if ($this->shouldRun($this->argument)) {
95
+            parent::execute($jobList, $logger);
96
+            $jobList->remove($this, $this->argument);
97
+            if ($this->retainJob) {
98
+                $this->reAddJob($jobList, $this->argument);
99
+            }
100
+        }
101
+
102
+    }
103
+
104
+    protected function run($argument) {
105
+
106
+        $try = (int)$argument['try'] + 1;
107
+
108
+        switch($argument['type']) {
109
+            case AccountManager::PROPERTY_WEBSITE:
110
+                $result = $this->verifyWebsite($argument);
111
+                break;
112
+            case AccountManager::PROPERTY_TWITTER:
113
+            case AccountManager::PROPERTY_EMAIL:
114
+                $result = $this->verifyViaLookupServer($argument, $argument['type']);
115
+                break;
116
+            default:
117
+                // no valid type given, no need to retry
118
+                $this->logger->error($argument['type'] . ' is no valid type for user account data.');
119
+                $result = true;
120
+        }
121
+
122
+        if ($result === true || $try > $this->maxTry) {
123
+            $this->retainJob = false;
124
+        }
125
+    }
126
+
127
+    /**
128
+     * verify web page
129
+     *
130
+     * @param array $argument
131
+     * @return bool true if we could check the verification code, otherwise false
132
+     */
133
+    protected function verifyWebsite(array $argument) {
134
+
135
+        $result = false;
136
+
137
+        $url = rtrim($argument['data'], '/') . '/.well-known/' . 'CloudIdVerificationCode.txt';
138
+
139
+        $client = $this->httpClientService->newClient();
140
+        try {
141
+            $response = $client->get($url);
142
+        } catch (\Exception $e) {
143
+            return false;
144
+        }
145
+
146
+        if ($response->getStatusCode() === Http::STATUS_OK) {
147
+            $result = true;
148
+            $publishedCode = $response->getBody();
149
+            // remove new lines and spaces
150
+            $publishedCodeSanitized = trim(preg_replace('/\s\s+/', ' ', $publishedCode));
151
+            $user = $this->userManager->get($argument['uid']);
152
+            // we don't check a valid user -> give up
153
+            if ($user === null) {
154
+                $this->logger->error($argument['uid'] . ' doesn\'t exist, can\'t verify user data.');
155
+                return $result;
156
+            }
157
+            $userData = $this->accountManager->getUser($user);
158
+
159
+            if ($publishedCodeSanitized === $argument['verificationCode']) {
160
+                $userData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFIED;
161
+            } else {
162
+                $userData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::NOT_VERIFIED;
163
+            }
164
+
165
+            $this->accountManager->updateUser($user, $userData);
166
+        }
167
+
168
+        return $result;
169
+    }
170
+
171
+    /**
172
+     * verify email address
173
+     *
174
+     * @param array $argument
175
+     * @param string $dataType
176
+     * @return bool true if we could check the verification code, otherwise false
177
+     */
178
+    protected function verifyViaLookupServer(array $argument, $dataType) {
179
+
180
+        $user = $this->userManager->get($argument['uid']);
181
+
182
+        // we don't check a valid user -> give up
183
+        if ($user === null) {
184
+            $this->logger->error($argument['uid'] . ' doesn\'t exist, can\'t verify user data.');
185
+            return true;
186
+        }
187
+
188
+        $localUserData = $this->accountManager->getUser($user);
189
+        $cloudId = $user->getCloudId();
190
+
191
+        // ask lookup-server for user data
192
+        $lookupServerData = $this->queryLookupServer($cloudId);
193
+
194
+        // for some reasons we couldn't read any data from the lookup server, try again later
195
+        if (empty($lookupServerData)) {
196
+            return false;
197
+        }
198
+
199
+        // lookup server has verification data for wrong user data (e.g. email address), try again later
200
+        if ($lookupServerData[$dataType]['value'] !== $argument['data']) {
201
+            return false;
202
+        }
203
+
204
+        // lookup server hasn't verified the email address so far, try again later
205
+        if ($lookupServerData[$dataType]['verified'] === AccountManager::NOT_VERIFIED) {
206
+            return false;
207
+        }
208
+
209
+        $localUserData[$dataType]['verified'] = AccountManager::VERIFIED;
210
+        $this->accountManager->updateUser($user, $localUserData);
211
+
212
+        return true;
213
+    }
214
+
215
+    /**
216
+     * @param string $cloudId
217
+     * @return array
218
+     */
219
+    protected function queryLookupServer($cloudId) {
220
+        try {
221
+            $client = $this->httpClientService->newClient();
222
+            $response = $client->get(
223
+                $this->lookupServerUrl . '/users?search=' . urlencode($cloudId) . '&exactCloudId=1',
224
+                [
225
+                    'timeout' => 10,
226
+                    'connect_timeout' => 3,
227
+                ]
228
+            );
229
+
230
+            $body = json_decode($response->getBody(), true);
231
+
232
+            if ($body['federationId'] === $cloudId) {
233
+                return $body;
234
+            }
235
+
236
+        } catch (\Exception $e) {
237
+            // do nothing, we will just re-try later
238
+        }
239
+
240
+        return [];
241
+    }
242
+
243
+    /**
244
+     * re-add background job with new arguments
245
+     *
246
+     * @param IJobList $jobList
247
+     * @param array $argument
248
+     */
249
+    protected function reAddJob(IJobList $jobList, array $argument) {
250
+        $jobList->add('OC\Settings\BackgroundJobs\VerifyUserData',
251
+            [
252
+                'verificationCode' => $argument['verificationCode'],
253
+                'data' => $argument['data'],
254
+                'type' => $argument['type'],
255
+                'uid' => $argument['uid'],
256
+                'try' => (int)$argument['try'] + 1,
257
+                'lastRun' => time()
258
+            ]
259
+        );
260
+    }
261
+
262
+    /**
263
+     * test if it is time for the next run
264
+     *
265
+     * @param array $argument
266
+     * @return bool
267
+     */
268
+    protected function shouldRun(array $argument) {
269
+        $lastRun = (int)$argument['lastRun'];
270
+        return ((time() - $lastRun) > $this->interval);
271
+    }
272 272
 
273 273
 }
Please login to merge, or discard this patch.
settings/Controller/UsersController.php 1 patch
Indentation   +855 added lines, -855 removed lines patch added patch discarded remove patch
@@ -58,860 +58,860 @@
 block discarded – undo
58 58
  * @package OC\Settings\Controller
59 59
  */
60 60
 class UsersController extends Controller {
61
-	/** @var IL10N */
62
-	private $l10n;
63
-	/** @var IUserSession */
64
-	private $userSession;
65
-	/** @var bool */
66
-	private $isAdmin;
67
-	/** @var IUserManager */
68
-	private $userManager;
69
-	/** @var IGroupManager */
70
-	private $groupManager;
71
-	/** @var IConfig */
72
-	private $config;
73
-	/** @var ILogger */
74
-	private $log;
75
-	/** @var IMailer */
76
-	private $mailer;
77
-	/** @var bool contains the state of the encryption app */
78
-	private $isEncryptionAppEnabled;
79
-	/** @var bool contains the state of the admin recovery setting */
80
-	private $isRestoreEnabled = false;
81
-	/** @var IAvatarManager */
82
-	private $avatarManager;
83
-	/** @var AccountManager */
84
-	private $accountManager;
85
-	/** @var ISecureRandom */
86
-	private $secureRandom;
87
-	/** @var NewUserMailHelper */
88
-	private $newUserMailHelper;
89
-	/** @var ITimeFactory */
90
-	private $timeFactory;
91
-	/** @var ICrypto */
92
-	private $crypto;
93
-	/** @var Manager */
94
-	private $keyManager;
95
-	/** @var IJobList */
96
-	private $jobList;
97
-
98
-	/**
99
-	 * @param string $appName
100
-	 * @param IRequest $request
101
-	 * @param IUserManager $userManager
102
-	 * @param IGroupManager $groupManager
103
-	 * @param IUserSession $userSession
104
-	 * @param IConfig $config
105
-	 * @param bool $isAdmin
106
-	 * @param IL10N $l10n
107
-	 * @param ILogger $log
108
-	 * @param IMailer $mailer
109
-	 * @param IURLGenerator $urlGenerator
110
-	 * @param IAppManager $appManager
111
-	 * @param IAvatarManager $avatarManager
112
-	 * @param AccountManager $accountManager
113
-	 * @param ISecureRandom $secureRandom
114
-	 * @param NewUserMailHelper $newUserMailHelper
115
-	 * @param ITimeFactory $timeFactory
116
-	 * @param ICrypto $crypto
117
-	 * @param Manager $keyManager
118
-	 * @param IJobList $jobList
119
-	 */
120
-	public function __construct($appName,
121
-								IRequest $request,
122
-								IUserManager $userManager,
123
-								IGroupManager $groupManager,
124
-								IUserSession $userSession,
125
-								IConfig $config,
126
-								$isAdmin,
127
-								IL10N $l10n,
128
-								ILogger $log,
129
-								IMailer $mailer,
130
-								IURLGenerator $urlGenerator,
131
-								IAppManager $appManager,
132
-								IAvatarManager $avatarManager,
133
-								AccountManager $accountManager,
134
-								ISecureRandom $secureRandom,
135
-								NewUserMailHelper $newUserMailHelper,
136
-								ITimeFactory $timeFactory,
137
-								ICrypto $crypto,
138
-								Manager $keyManager,
139
-								IJobList $jobList) {
140
-		parent::__construct($appName, $request);
141
-		$this->userManager = $userManager;
142
-		$this->groupManager = $groupManager;
143
-		$this->userSession = $userSession;
144
-		$this->config = $config;
145
-		$this->isAdmin = $isAdmin;
146
-		$this->l10n = $l10n;
147
-		$this->log = $log;
148
-		$this->mailer = $mailer;
149
-		$this->avatarManager = $avatarManager;
150
-		$this->accountManager = $accountManager;
151
-		$this->secureRandom = $secureRandom;
152
-		$this->newUserMailHelper = $newUserMailHelper;
153
-		$this->timeFactory = $timeFactory;
154
-		$this->crypto = $crypto;
155
-		$this->keyManager = $keyManager;
156
-		$this->jobList = $jobList;
157
-
158
-		// check for encryption state - TODO see formatUserForIndex
159
-		$this->isEncryptionAppEnabled = $appManager->isEnabledForUser('encryption');
160
-		if($this->isEncryptionAppEnabled) {
161
-			// putting this directly in empty is possible in PHP 5.5+
162
-			$result = $config->getAppValue('encryption', 'recoveryAdminEnabled', 0);
163
-			$this->isRestoreEnabled = !empty($result);
164
-		}
165
-	}
166
-
167
-	/**
168
-	 * @param IUser $user
169
-	 * @param array $userGroups
170
-	 * @return array
171
-	 */
172
-	private function formatUserForIndex(IUser $user, array $userGroups = null) {
173
-
174
-		// TODO: eliminate this encryption specific code below and somehow
175
-		// hook in additional user info from other apps
176
-
177
-		// recovery isn't possible if admin or user has it disabled and encryption
178
-		// is enabled - so we eliminate the else paths in the conditional tree
179
-		// below
180
-		$restorePossible = false;
181
-
182
-		if ($this->isEncryptionAppEnabled) {
183
-			if ($this->isRestoreEnabled) {
184
-				// check for the users recovery setting
185
-				$recoveryMode = $this->config->getUserValue($user->getUID(), 'encryption', 'recoveryEnabled', '0');
186
-				// method call inside empty is possible with PHP 5.5+
187
-				$recoveryModeEnabled = !empty($recoveryMode);
188
-				if ($recoveryModeEnabled) {
189
-					// user also has recovery mode enabled
190
-					$restorePossible = true;
191
-				}
192
-			}
193
-		} else {
194
-			// recovery is possible if encryption is disabled (plain files are
195
-			// available)
196
-			$restorePossible = true;
197
-		}
198
-
199
-		$subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($user);
200
-		foreach($subAdminGroups as $key => $subAdminGroup) {
201
-			$subAdminGroups[$key] = $subAdminGroup->getGID();
202
-		}
203
-
204
-		$displayName = $user->getEMailAddress();
205
-		if (is_null($displayName)) {
206
-			$displayName = '';
207
-		}
208
-
209
-		$avatarAvailable = false;
210
-		try {
211
-			$avatarAvailable = $this->avatarManager->getAvatar($user->getUID())->exists();
212
-		} catch (\Exception $e) {
213
-			//No avatar yet
214
-		}
215
-
216
-		return [
217
-			'name' => $user->getUID(),
218
-			'displayname' => $user->getDisplayName(),
219
-			'groups' => (empty($userGroups)) ? $this->groupManager->getUserGroupIds($user) : $userGroups,
220
-			'subadmin' => $subAdminGroups,
221
-			'quota' => $user->getQuota(),
222
-			'storageLocation' => $user->getHome(),
223
-			'lastLogin' => $user->getLastLogin() * 1000,
224
-			'backend' => $user->getBackendClassName(),
225
-			'email' => $displayName,
226
-			'isRestoreDisabled' => !$restorePossible,
227
-			'isAvatarAvailable' => $avatarAvailable,
228
-		];
229
-	}
230
-
231
-	/**
232
-	 * @param array $userIDs Array with schema [$uid => $displayName]
233
-	 * @return IUser[]
234
-	 */
235
-	private function getUsersForUID(array $userIDs) {
236
-		$users = [];
237
-		foreach ($userIDs as $uid => $displayName) {
238
-			$users[$uid] = $this->userManager->get($uid);
239
-		}
240
-		return $users;
241
-	}
242
-
243
-	/**
244
-	 * @NoAdminRequired
245
-	 *
246
-	 * @param int $offset
247
-	 * @param int $limit
248
-	 * @param string $gid GID to filter for
249
-	 * @param string $pattern Pattern to search for in the username
250
-	 * @param string $backend Backend to filter for (class-name)
251
-	 * @return DataResponse
252
-	 *
253
-	 * TODO: Tidy up and write unit tests - code is mainly static method calls
254
-	 */
255
-	public function index($offset = 0, $limit = 10, $gid = '', $pattern = '', $backend = '') {
256
-		// FIXME: The JS sends the group '_everyone' instead of no GID for the "all users" group.
257
-		if($gid === '_everyone') {
258
-			$gid = '';
259
-		}
260
-
261
-		// Remove backends
262
-		if(!empty($backend)) {
263
-			$activeBackends = $this->userManager->getBackends();
264
-			$this->userManager->clearBackends();
265
-			foreach($activeBackends as $singleActiveBackend) {
266
-				if($backend === get_class($singleActiveBackend)) {
267
-					$this->userManager->registerBackend($singleActiveBackend);
268
-					break;
269
-				}
270
-			}
271
-		}
272
-
273
-		$users = [];
274
-		if ($this->isAdmin) {
275
-
276
-			if($gid !== '') {
277
-				$batch = $this->getUsersForUID($this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset));
278
-			} else {
279
-				$batch = $this->userManager->search($pattern, $limit, $offset);
280
-			}
281
-
282
-			foreach ($batch as $user) {
283
-				$users[] = $this->formatUserForIndex($user);
284
-			}
285
-
286
-		} else {
287
-			$subAdminOfGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser());
288
-			// New class returns IGroup[] so convert back
289
-			$gids = [];
290
-			foreach ($subAdminOfGroups as $group) {
291
-				$gids[] = $group->getGID();
292
-			}
293
-			$subAdminOfGroups = $gids;
294
-
295
-			// Set the $gid parameter to an empty value if the subadmin has no rights to access a specific group
296
-			if($gid !== '' && !in_array($gid, $subAdminOfGroups)) {
297
-				$gid = '';
298
-			}
299
-
300
-			// Batch all groups the user is subadmin of when a group is specified
301
-			$batch = [];
302
-			if($gid === '') {
303
-				foreach($subAdminOfGroups as $group) {
304
-					$groupUsers = $this->groupManager->displayNamesInGroup($group, $pattern, $limit, $offset);
305
-
306
-					foreach($groupUsers as $uid => $displayName) {
307
-						$batch[$uid] = $displayName;
308
-					}
309
-				}
310
-			} else {
311
-				$batch = $this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset);
312
-			}
313
-			$batch = $this->getUsersForUID($batch);
314
-
315
-			foreach ($batch as $user) {
316
-				// Only add the groups, this user is a subadmin of
317
-				$userGroups = array_values(array_intersect(
318
-					$this->groupManager->getUserGroupIds($user),
319
-					$subAdminOfGroups
320
-				));
321
-				$users[] = $this->formatUserForIndex($user, $userGroups);
322
-			}
323
-		}
324
-
325
-		return new DataResponse($users);
326
-	}
327
-
328
-	/**
329
-	 * @NoAdminRequired
330
-	 * @PasswordConfirmationRequired
331
-	 *
332
-	 * @param string $username
333
-	 * @param string $password
334
-	 * @param array $groups
335
-	 * @param string $email
336
-	 * @return DataResponse
337
-	 */
338
-	public function create($username, $password, array $groups=array(), $email='') {
339
-		if($email !== '' && !$this->mailer->validateMailAddress($email)) {
340
-			return new DataResponse(
341
-				array(
342
-					'message' => (string)$this->l10n->t('Invalid mail address')
343
-				),
344
-				Http::STATUS_UNPROCESSABLE_ENTITY
345
-			);
346
-		}
347
-
348
-		$currentUser = $this->userSession->getUser();
349
-
350
-		if (!$this->isAdmin) {
351
-			if (!empty($groups)) {
352
-				foreach ($groups as $key => $group) {
353
-					$groupObject = $this->groupManager->get($group);
354
-					if($groupObject === null) {
355
-						unset($groups[$key]);
356
-						continue;
357
-					}
358
-
359
-					if (!$this->groupManager->getSubAdmin()->isSubAdminofGroup($currentUser, $groupObject)) {
360
-						unset($groups[$key]);
361
-					}
362
-				}
363
-			}
364
-
365
-			if (empty($groups)) {
366
-				return new DataResponse(
367
-					array(
368
-						'message' => $this->l10n->t('No valid group selected'),
369
-					),
370
-					Http::STATUS_FORBIDDEN
371
-				);
372
-			}
373
-		}
374
-
375
-		if ($this->userManager->userExists($username)) {
376
-			return new DataResponse(
377
-				array(
378
-					'message' => (string)$this->l10n->t('A user with that name already exists.')
379
-				),
380
-				Http::STATUS_CONFLICT
381
-			);
382
-		}
383
-
384
-		$generatePasswordResetToken = false;
385
-		if ($password === '') {
386
-			if ($email === '') {
387
-				return new DataResponse(
388
-					array(
389
-						'message' => (string)$this->l10n->t('To send a password link to the user an email address is required.')
390
-					),
391
-					Http::STATUS_UNPROCESSABLE_ENTITY
392
-				);
393
-			}
394
-
395
-			$password = $this->secureRandom->generate(32);
396
-			$generatePasswordResetToken = true;
397
-		}
398
-
399
-		try {
400
-			$user = $this->userManager->createUser($username, $password);
401
-		} catch (\Exception $exception) {
402
-			$message = $exception->getMessage();
403
-			if (!$message) {
404
-				$message = $this->l10n->t('Unable to create user.');
405
-			}
406
-			return new DataResponse(
407
-				array(
408
-					'message' => (string) $message,
409
-				),
410
-				Http::STATUS_FORBIDDEN
411
-			);
412
-		}
413
-
414
-		if($user instanceof IUser) {
415
-			if($groups !== null) {
416
-				foreach($groups as $groupName) {
417
-					$group = $this->groupManager->get($groupName);
418
-
419
-					if(empty($group)) {
420
-						$group = $this->groupManager->createGroup($groupName);
421
-					}
422
-					$group->addUser($user);
423
-				}
424
-			}
425
-			/**
426
-			 * Send new user mail only if a mail is set
427
-			 */
428
-			if($email !== '') {
429
-				$user->setEMailAddress($email);
430
-				try {
431
-					$emailTemplate = $this->newUserMailHelper->generateTemplate($user, $generatePasswordResetToken);
432
-					$this->newUserMailHelper->sendMail($user, $emailTemplate);
433
-				} catch(\Exception $e) {
434
-					$this->log->error("Can't send new user mail to $email: " . $e->getMessage(), array('app' => 'settings'));
435
-				}
436
-			}
437
-			// fetch users groups
438
-			$userGroups = $this->groupManager->getUserGroupIds($user);
439
-
440
-			return new DataResponse(
441
-				$this->formatUserForIndex($user, $userGroups),
442
-				Http::STATUS_CREATED
443
-			);
444
-		}
445
-
446
-		return new DataResponse(
447
-			array(
448
-				'message' => (string)$this->l10n->t('Unable to create user.')
449
-			),
450
-			Http::STATUS_FORBIDDEN
451
-		);
452
-
453
-	}
454
-
455
-	/**
456
-	 * @NoAdminRequired
457
-	 * @PasswordConfirmationRequired
458
-	 *
459
-	 * @param string $id
460
-	 * @return DataResponse
461
-	 */
462
-	public function destroy($id) {
463
-		$userId = $this->userSession->getUser()->getUID();
464
-		$user = $this->userManager->get($id);
465
-
466
-		if($userId === $id) {
467
-			return new DataResponse(
468
-				array(
469
-					'status' => 'error',
470
-					'data' => array(
471
-						'message' => (string)$this->l10n->t('Unable to delete user.')
472
-					)
473
-				),
474
-				Http::STATUS_FORBIDDEN
475
-			);
476
-		}
477
-
478
-		if(!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
479
-			return new DataResponse(
480
-				array(
481
-					'status' => 'error',
482
-					'data' => array(
483
-						'message' => (string)$this->l10n->t('Authentication error')
484
-					)
485
-				),
486
-				Http::STATUS_FORBIDDEN
487
-			);
488
-		}
489
-
490
-		if($user) {
491
-			if($user->delete()) {
492
-				return new DataResponse(
493
-					array(
494
-						'status' => 'success',
495
-						'data' => array(
496
-							'username' => $id
497
-						)
498
-					),
499
-					Http::STATUS_NO_CONTENT
500
-				);
501
-			}
502
-		}
503
-
504
-		return new DataResponse(
505
-			array(
506
-				'status' => 'error',
507
-				'data' => array(
508
-					'message' => (string)$this->l10n->t('Unable to delete user.')
509
-				)
510
-			),
511
-			Http::STATUS_FORBIDDEN
512
-		);
513
-	}
514
-
515
-	/**
516
-	 * @NoAdminRequired
517
-	 * @NoSubadminRequired
518
-	 * @PasswordConfirmationRequired
519
-	 *
520
-	 * @param string $account
521
-	 * @param bool $onlyVerificationCode only return verification code without updating the data
522
-	 * @return DataResponse
523
-	 */
524
-	public function getVerificationCode($account, $onlyVerificationCode) {
525
-
526
-		$user = $this->userSession->getUser();
527
-
528
-		if ($user === null) {
529
-			return new DataResponse([], Http::STATUS_BAD_REQUEST);
530
-		}
531
-
532
-		$accountData = $this->accountManager->getUser($user);
533
-		$cloudId = $user->getCloudId();
534
-		$message = "Use my Federated Cloud ID to share with me: " . $cloudId;
535
-		$signature = $this->signMessage($user, $message);
536
-
537
-		$code = $message . ' ' . $signature;
538
-		$codeMd5 = $message . ' ' . md5($signature);
539
-
540
-		switch ($account) {
541
-			case 'verify-twitter':
542
-				$accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
543
-				$msg = $this->l10n->t('In order to verify your Twitter account post following tweet on Twitter (please make sure to post it without any line breaks):');
544
-				$code = $codeMd5;
545
-				$type = AccountManager::PROPERTY_TWITTER;
546
-				$data = $accountData[AccountManager::PROPERTY_TWITTER]['value'];
547
-				break;
548
-			case 'verify-website':
549
-				$accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
550
-				$msg = $this->l10n->t('In order to verify your Website store following content in your web-root at \'.well-known/CloudIdVerificationCode.txt\' (please make sure that the complete text is in one line):');
551
-				$type = AccountManager::PROPERTY_WEBSITE;
552
-				$data = $accountData[AccountManager::PROPERTY_WEBSITE]['value'];
553
-				break;
554
-			default:
555
-				return new DataResponse([], Http::STATUS_BAD_REQUEST);
556
-		}
557
-
558
-		if ($onlyVerificationCode === false) {
559
-			$this->accountManager->updateUser($user, $accountData);
560
-
561
-			$this->jobList->add('OC\Settings\BackgroundJobs\VerifyUserData',
562
-				[
563
-					'verificationCode' => $code,
564
-					'data' => $data,
565
-					'type' => $type,
566
-					'uid' => $user->getUID(),
567
-					'try' => 0,
568
-					'lastRun' => $this->getCurrentTime()
569
-				]
570
-			);
571
-		}
572
-
573
-		return new DataResponse(['msg' => $msg, 'code' => $code]);
574
-	}
575
-
576
-	/**
577
-	 * get current timestamp
578
-	 *
579
-	 * @return int
580
-	 */
581
-	protected function getCurrentTime() {
582
-		return time();
583
-	}
584
-
585
-	/**
586
-	 * sign message with users private key
587
-	 *
588
-	 * @param IUser $user
589
-	 * @param string $message
590
-	 *
591
-	 * @return string base64 encoded signature
592
-	 */
593
-	protected function signMessage(IUser $user, $message) {
594
-		$privateKey = $this->keyManager->getKey($user)->getPrivate();
595
-		openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
596
-		$signatureBase64 = base64_encode($signature);
597
-
598
-		return $signatureBase64;
599
-	}
600
-
601
-	/**
602
-	 * @NoAdminRequired
603
-	 * @NoSubadminRequired
604
-	 * @PasswordConfirmationRequired
605
-	 *
606
-	 * @param string $avatarScope
607
-	 * @param string $displayname
608
-	 * @param string $displaynameScope
609
-	 * @param string $phone
610
-	 * @param string $phoneScope
611
-	 * @param string $email
612
-	 * @param string $emailScope
613
-	 * @param string $website
614
-	 * @param string $websiteScope
615
-	 * @param string $address
616
-	 * @param string $addressScope
617
-	 * @param string $twitter
618
-	 * @param string $twitterScope
619
-	 * @return DataResponse
620
-	 */
621
-	public function setUserSettings($avatarScope,
622
-									$displayname,
623
-									$displaynameScope,
624
-									$phone,
625
-									$phoneScope,
626
-									$email,
627
-									$emailScope,
628
-									$website,
629
-									$websiteScope,
630
-									$address,
631
-									$addressScope,
632
-									$twitter,
633
-									$twitterScope
634
-	) {
635
-
636
-		if(!empty($email) && !$this->mailer->validateMailAddress($email)) {
637
-			return new DataResponse(
638
-				array(
639
-					'status' => 'error',
640
-					'data' => array(
641
-						'message' => (string)$this->l10n->t('Invalid mail address')
642
-					)
643
-				),
644
-				Http::STATUS_UNPROCESSABLE_ENTITY
645
-			);
646
-		}
647
-
648
-		$data = [
649
-			AccountManager::PROPERTY_AVATAR =>  ['scope' => $avatarScope],
650
-			AccountManager::PROPERTY_DISPLAYNAME => ['value' => $displayname, 'scope' => $displaynameScope],
651
-			AccountManager::PROPERTY_EMAIL=> ['value' => $email, 'scope' => $emailScope],
652
-			AccountManager::PROPERTY_WEBSITE => ['value' => $website, 'scope' => $websiteScope],
653
-			AccountManager::PROPERTY_ADDRESS => ['value' => $address, 'scope' => $addressScope],
654
-			AccountManager::PROPERTY_PHONE => ['value' => $phone, 'scope' => $phoneScope],
655
-			AccountManager::PROPERTY_TWITTER => ['value' => $twitter, 'scope' => $twitterScope]
656
-		];
657
-
658
-		$user = $this->userSession->getUser();
659
-
660
-		try {
661
-			$this->saveUserSettings($user, $data);
662
-			return new DataResponse(
663
-				array(
664
-					'status' => 'success',
665
-					'data' => array(
666
-						'userId' => $user->getUID(),
667
-						'avatarScope' => $avatarScope,
668
-						'displayname' => $displayname,
669
-						'displaynameScope' => $displaynameScope,
670
-						'email' => $email,
671
-						'emailScope' => $emailScope,
672
-						'website' => $website,
673
-						'websiteScope' => $websiteScope,
674
-						'address' => $address,
675
-						'addressScope' => $addressScope,
676
-						'message' => (string)$this->l10n->t('Settings saved')
677
-					)
678
-				),
679
-				Http::STATUS_OK
680
-			);
681
-		} catch (ForbiddenException $e) {
682
-			return new DataResponse([
683
-				'status' => 'error',
684
-				'data' => [
685
-					'message' => $e->getMessage()
686
-				],
687
-			]);
688
-		}
689
-
690
-	}
691
-
692
-
693
-	/**
694
-	 * update account manager with new user data
695
-	 *
696
-	 * @param IUser $user
697
-	 * @param array $data
698
-	 * @throws ForbiddenException
699
-	 */
700
-	protected function saveUserSettings(IUser $user, $data) {
701
-
702
-		// keep the user back-end up-to-date with the latest display name and email
703
-		// address
704
-		$oldDisplayName = $user->getDisplayName();
705
-		$oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
706
-		if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
707
-			&& $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value']
708
-		) {
709
-			$result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']);
710
-			if ($result === false) {
711
-				throw new ForbiddenException($this->l10n->t('Unable to change full name'));
712
-			}
713
-		}
714
-
715
-		$oldEmailAddress = $user->getEMailAddress();
716
-		$oldEmailAddress = is_null($oldEmailAddress) ? '' : $oldEmailAddress;
717
-		if (isset($data[AccountManager::PROPERTY_EMAIL]['value'])
718
-			&& $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value']
719
-		) {
720
-			// this is the only permission a backend provides and is also used
721
-			// for the permission of setting a email address
722
-			if (!$user->canChangeDisplayName()) {
723
-				throw new ForbiddenException($this->l10n->t('Unable to change email address'));
724
-			}
725
-			$user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']);
726
-		}
727
-
728
-		$this->accountManager->updateUser($user, $data);
729
-	}
730
-
731
-	/**
732
-	 * Count all unique users visible for the current admin/subadmin.
733
-	 *
734
-	 * @NoAdminRequired
735
-	 *
736
-	 * @return DataResponse
737
-	 */
738
-	public function stats() {
739
-		$userCount = 0;
740
-		if ($this->isAdmin) {
741
-			$countByBackend = $this->userManager->countUsers();
742
-
743
-			if (!empty($countByBackend)) {
744
-				foreach ($countByBackend as $count) {
745
-					$userCount += $count;
746
-				}
747
-			}
748
-		} else {
749
-			$groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser());
750
-
751
-			$uniqueUsers = [];
752
-			foreach ($groups as $group) {
753
-				foreach($group->getUsers() as $uid => $displayName) {
754
-					$uniqueUsers[$uid] = true;
755
-				}
756
-			}
757
-
758
-			$userCount = count($uniqueUsers);
759
-		}
760
-
761
-		return new DataResponse(
762
-			[
763
-				'totalUsers' => $userCount
764
-			]
765
-		);
766
-	}
767
-
768
-
769
-	/**
770
-	 * Set the displayName of a user
771
-	 *
772
-	 * @NoAdminRequired
773
-	 * @NoSubadminRequired
774
-	 * @PasswordConfirmationRequired
775
-	 * @todo merge into saveUserSettings
776
-	 *
777
-	 * @param string $username
778
-	 * @param string $displayName
779
-	 * @return DataResponse
780
-	 */
781
-	public function setDisplayName($username, $displayName) {
782
-		$currentUser = $this->userSession->getUser();
783
-		$user = $this->userManager->get($username);
784
-
785
-		if ($user === null ||
786
-			!$user->canChangeDisplayName() ||
787
-			(
788
-				!$this->groupManager->isAdmin($currentUser->getUID()) &&
789
-				!$this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $user) &&
790
-				$currentUser->getUID() !== $username
791
-
792
-			)
793
-		) {
794
-			return new DataResponse([
795
-				'status' => 'error',
796
-				'data' => [
797
-					'message' => $this->l10n->t('Authentication error'),
798
-				],
799
-			]);
800
-		}
801
-
802
-		$userData = $this->accountManager->getUser($user);
803
-		$userData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $displayName;
804
-
805
-
806
-		try {
807
-			$this->saveUserSettings($user, $userData);
808
-			return new DataResponse([
809
-				'status' => 'success',
810
-				'data' => [
811
-					'message' => $this->l10n->t('Your full name has been changed.'),
812
-					'username' => $username,
813
-					'displayName' => $displayName,
814
-				],
815
-			]);
816
-		} catch (ForbiddenException $e) {
817
-			return new DataResponse([
818
-				'status' => 'error',
819
-				'data' => [
820
-					'message' => $e->getMessage(),
821
-					'displayName' => $user->getDisplayName(),
822
-				],
823
-			]);
824
-		}
825
-	}
826
-
827
-	/**
828
-	 * Set the mail address of a user
829
-	 *
830
-	 * @NoAdminRequired
831
-	 * @NoSubadminRequired
832
-	 * @PasswordConfirmationRequired
833
-	 *
834
-	 * @param string $id
835
-	 * @param string $mailAddress
836
-	 * @return DataResponse
837
-	 */
838
-	public function setEMailAddress($id, $mailAddress) {
839
-		$user = $this->userManager->get($id);
840
-		if (!$this->isAdmin
841
-			&& !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)
842
-		) {
843
-			return new DataResponse(
844
-				array(
845
-					'status' => 'error',
846
-					'data' => array(
847
-						'message' => (string)$this->l10n->t('Forbidden')
848
-					)
849
-				),
850
-				Http::STATUS_FORBIDDEN
851
-			);
852
-		}
853
-
854
-		if($mailAddress !== '' && !$this->mailer->validateMailAddress($mailAddress)) {
855
-			return new DataResponse(
856
-				array(
857
-					'status' => 'error',
858
-					'data' => array(
859
-						'message' => (string)$this->l10n->t('Invalid mail address')
860
-					)
861
-				),
862
-				Http::STATUS_UNPROCESSABLE_ENTITY
863
-			);
864
-		}
865
-
866
-		if (!$user) {
867
-			return new DataResponse(
868
-				array(
869
-					'status' => 'error',
870
-					'data' => array(
871
-						'message' => (string)$this->l10n->t('Invalid user')
872
-					)
873
-				),
874
-				Http::STATUS_UNPROCESSABLE_ENTITY
875
-			);
876
-		}
877
-		// this is the only permission a backend provides and is also used
878
-		// for the permission of setting a email address
879
-		if (!$user->canChangeDisplayName()) {
880
-			return new DataResponse(
881
-				array(
882
-					'status' => 'error',
883
-					'data' => array(
884
-						'message' => (string)$this->l10n->t('Unable to change mail address')
885
-					)
886
-				),
887
-				Http::STATUS_FORBIDDEN
888
-			);
889
-		}
890
-
891
-		$userData = $this->accountManager->getUser($user);
892
-		$userData[AccountManager::PROPERTY_EMAIL]['value'] = $mailAddress;
893
-
894
-		try {
895
-			$this->saveUserSettings($user, $userData);
896
-			return new DataResponse(
897
-				array(
898
-					'status' => 'success',
899
-					'data' => array(
900
-						'username' => $id,
901
-						'mailAddress' => $mailAddress,
902
-						'message' => (string)$this->l10n->t('Email saved')
903
-					)
904
-				),
905
-				Http::STATUS_OK
906
-			);
907
-		} catch (ForbiddenException $e) {
908
-			return new DataResponse([
909
-				'status' => 'error',
910
-				'data' => [
911
-					'message' => $e->getMessage()
912
-				],
913
-			]);
914
-		}
915
-	}
61
+    /** @var IL10N */
62
+    private $l10n;
63
+    /** @var IUserSession */
64
+    private $userSession;
65
+    /** @var bool */
66
+    private $isAdmin;
67
+    /** @var IUserManager */
68
+    private $userManager;
69
+    /** @var IGroupManager */
70
+    private $groupManager;
71
+    /** @var IConfig */
72
+    private $config;
73
+    /** @var ILogger */
74
+    private $log;
75
+    /** @var IMailer */
76
+    private $mailer;
77
+    /** @var bool contains the state of the encryption app */
78
+    private $isEncryptionAppEnabled;
79
+    /** @var bool contains the state of the admin recovery setting */
80
+    private $isRestoreEnabled = false;
81
+    /** @var IAvatarManager */
82
+    private $avatarManager;
83
+    /** @var AccountManager */
84
+    private $accountManager;
85
+    /** @var ISecureRandom */
86
+    private $secureRandom;
87
+    /** @var NewUserMailHelper */
88
+    private $newUserMailHelper;
89
+    /** @var ITimeFactory */
90
+    private $timeFactory;
91
+    /** @var ICrypto */
92
+    private $crypto;
93
+    /** @var Manager */
94
+    private $keyManager;
95
+    /** @var IJobList */
96
+    private $jobList;
97
+
98
+    /**
99
+     * @param string $appName
100
+     * @param IRequest $request
101
+     * @param IUserManager $userManager
102
+     * @param IGroupManager $groupManager
103
+     * @param IUserSession $userSession
104
+     * @param IConfig $config
105
+     * @param bool $isAdmin
106
+     * @param IL10N $l10n
107
+     * @param ILogger $log
108
+     * @param IMailer $mailer
109
+     * @param IURLGenerator $urlGenerator
110
+     * @param IAppManager $appManager
111
+     * @param IAvatarManager $avatarManager
112
+     * @param AccountManager $accountManager
113
+     * @param ISecureRandom $secureRandom
114
+     * @param NewUserMailHelper $newUserMailHelper
115
+     * @param ITimeFactory $timeFactory
116
+     * @param ICrypto $crypto
117
+     * @param Manager $keyManager
118
+     * @param IJobList $jobList
119
+     */
120
+    public function __construct($appName,
121
+                                IRequest $request,
122
+                                IUserManager $userManager,
123
+                                IGroupManager $groupManager,
124
+                                IUserSession $userSession,
125
+                                IConfig $config,
126
+                                $isAdmin,
127
+                                IL10N $l10n,
128
+                                ILogger $log,
129
+                                IMailer $mailer,
130
+                                IURLGenerator $urlGenerator,
131
+                                IAppManager $appManager,
132
+                                IAvatarManager $avatarManager,
133
+                                AccountManager $accountManager,
134
+                                ISecureRandom $secureRandom,
135
+                                NewUserMailHelper $newUserMailHelper,
136
+                                ITimeFactory $timeFactory,
137
+                                ICrypto $crypto,
138
+                                Manager $keyManager,
139
+                                IJobList $jobList) {
140
+        parent::__construct($appName, $request);
141
+        $this->userManager = $userManager;
142
+        $this->groupManager = $groupManager;
143
+        $this->userSession = $userSession;
144
+        $this->config = $config;
145
+        $this->isAdmin = $isAdmin;
146
+        $this->l10n = $l10n;
147
+        $this->log = $log;
148
+        $this->mailer = $mailer;
149
+        $this->avatarManager = $avatarManager;
150
+        $this->accountManager = $accountManager;
151
+        $this->secureRandom = $secureRandom;
152
+        $this->newUserMailHelper = $newUserMailHelper;
153
+        $this->timeFactory = $timeFactory;
154
+        $this->crypto = $crypto;
155
+        $this->keyManager = $keyManager;
156
+        $this->jobList = $jobList;
157
+
158
+        // check for encryption state - TODO see formatUserForIndex
159
+        $this->isEncryptionAppEnabled = $appManager->isEnabledForUser('encryption');
160
+        if($this->isEncryptionAppEnabled) {
161
+            // putting this directly in empty is possible in PHP 5.5+
162
+            $result = $config->getAppValue('encryption', 'recoveryAdminEnabled', 0);
163
+            $this->isRestoreEnabled = !empty($result);
164
+        }
165
+    }
166
+
167
+    /**
168
+     * @param IUser $user
169
+     * @param array $userGroups
170
+     * @return array
171
+     */
172
+    private function formatUserForIndex(IUser $user, array $userGroups = null) {
173
+
174
+        // TODO: eliminate this encryption specific code below and somehow
175
+        // hook in additional user info from other apps
176
+
177
+        // recovery isn't possible if admin or user has it disabled and encryption
178
+        // is enabled - so we eliminate the else paths in the conditional tree
179
+        // below
180
+        $restorePossible = false;
181
+
182
+        if ($this->isEncryptionAppEnabled) {
183
+            if ($this->isRestoreEnabled) {
184
+                // check for the users recovery setting
185
+                $recoveryMode = $this->config->getUserValue($user->getUID(), 'encryption', 'recoveryEnabled', '0');
186
+                // method call inside empty is possible with PHP 5.5+
187
+                $recoveryModeEnabled = !empty($recoveryMode);
188
+                if ($recoveryModeEnabled) {
189
+                    // user also has recovery mode enabled
190
+                    $restorePossible = true;
191
+                }
192
+            }
193
+        } else {
194
+            // recovery is possible if encryption is disabled (plain files are
195
+            // available)
196
+            $restorePossible = true;
197
+        }
198
+
199
+        $subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($user);
200
+        foreach($subAdminGroups as $key => $subAdminGroup) {
201
+            $subAdminGroups[$key] = $subAdminGroup->getGID();
202
+        }
203
+
204
+        $displayName = $user->getEMailAddress();
205
+        if (is_null($displayName)) {
206
+            $displayName = '';
207
+        }
208
+
209
+        $avatarAvailable = false;
210
+        try {
211
+            $avatarAvailable = $this->avatarManager->getAvatar($user->getUID())->exists();
212
+        } catch (\Exception $e) {
213
+            //No avatar yet
214
+        }
215
+
216
+        return [
217
+            'name' => $user->getUID(),
218
+            'displayname' => $user->getDisplayName(),
219
+            'groups' => (empty($userGroups)) ? $this->groupManager->getUserGroupIds($user) : $userGroups,
220
+            'subadmin' => $subAdminGroups,
221
+            'quota' => $user->getQuota(),
222
+            'storageLocation' => $user->getHome(),
223
+            'lastLogin' => $user->getLastLogin() * 1000,
224
+            'backend' => $user->getBackendClassName(),
225
+            'email' => $displayName,
226
+            'isRestoreDisabled' => !$restorePossible,
227
+            'isAvatarAvailable' => $avatarAvailable,
228
+        ];
229
+    }
230
+
231
+    /**
232
+     * @param array $userIDs Array with schema [$uid => $displayName]
233
+     * @return IUser[]
234
+     */
235
+    private function getUsersForUID(array $userIDs) {
236
+        $users = [];
237
+        foreach ($userIDs as $uid => $displayName) {
238
+            $users[$uid] = $this->userManager->get($uid);
239
+        }
240
+        return $users;
241
+    }
242
+
243
+    /**
244
+     * @NoAdminRequired
245
+     *
246
+     * @param int $offset
247
+     * @param int $limit
248
+     * @param string $gid GID to filter for
249
+     * @param string $pattern Pattern to search for in the username
250
+     * @param string $backend Backend to filter for (class-name)
251
+     * @return DataResponse
252
+     *
253
+     * TODO: Tidy up and write unit tests - code is mainly static method calls
254
+     */
255
+    public function index($offset = 0, $limit = 10, $gid = '', $pattern = '', $backend = '') {
256
+        // FIXME: The JS sends the group '_everyone' instead of no GID for the "all users" group.
257
+        if($gid === '_everyone') {
258
+            $gid = '';
259
+        }
260
+
261
+        // Remove backends
262
+        if(!empty($backend)) {
263
+            $activeBackends = $this->userManager->getBackends();
264
+            $this->userManager->clearBackends();
265
+            foreach($activeBackends as $singleActiveBackend) {
266
+                if($backend === get_class($singleActiveBackend)) {
267
+                    $this->userManager->registerBackend($singleActiveBackend);
268
+                    break;
269
+                }
270
+            }
271
+        }
272
+
273
+        $users = [];
274
+        if ($this->isAdmin) {
275
+
276
+            if($gid !== '') {
277
+                $batch = $this->getUsersForUID($this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset));
278
+            } else {
279
+                $batch = $this->userManager->search($pattern, $limit, $offset);
280
+            }
281
+
282
+            foreach ($batch as $user) {
283
+                $users[] = $this->formatUserForIndex($user);
284
+            }
285
+
286
+        } else {
287
+            $subAdminOfGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser());
288
+            // New class returns IGroup[] so convert back
289
+            $gids = [];
290
+            foreach ($subAdminOfGroups as $group) {
291
+                $gids[] = $group->getGID();
292
+            }
293
+            $subAdminOfGroups = $gids;
294
+
295
+            // Set the $gid parameter to an empty value if the subadmin has no rights to access a specific group
296
+            if($gid !== '' && !in_array($gid, $subAdminOfGroups)) {
297
+                $gid = '';
298
+            }
299
+
300
+            // Batch all groups the user is subadmin of when a group is specified
301
+            $batch = [];
302
+            if($gid === '') {
303
+                foreach($subAdminOfGroups as $group) {
304
+                    $groupUsers = $this->groupManager->displayNamesInGroup($group, $pattern, $limit, $offset);
305
+
306
+                    foreach($groupUsers as $uid => $displayName) {
307
+                        $batch[$uid] = $displayName;
308
+                    }
309
+                }
310
+            } else {
311
+                $batch = $this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset);
312
+            }
313
+            $batch = $this->getUsersForUID($batch);
314
+
315
+            foreach ($batch as $user) {
316
+                // Only add the groups, this user is a subadmin of
317
+                $userGroups = array_values(array_intersect(
318
+                    $this->groupManager->getUserGroupIds($user),
319
+                    $subAdminOfGroups
320
+                ));
321
+                $users[] = $this->formatUserForIndex($user, $userGroups);
322
+            }
323
+        }
324
+
325
+        return new DataResponse($users);
326
+    }
327
+
328
+    /**
329
+     * @NoAdminRequired
330
+     * @PasswordConfirmationRequired
331
+     *
332
+     * @param string $username
333
+     * @param string $password
334
+     * @param array $groups
335
+     * @param string $email
336
+     * @return DataResponse
337
+     */
338
+    public function create($username, $password, array $groups=array(), $email='') {
339
+        if($email !== '' && !$this->mailer->validateMailAddress($email)) {
340
+            return new DataResponse(
341
+                array(
342
+                    'message' => (string)$this->l10n->t('Invalid mail address')
343
+                ),
344
+                Http::STATUS_UNPROCESSABLE_ENTITY
345
+            );
346
+        }
347
+
348
+        $currentUser = $this->userSession->getUser();
349
+
350
+        if (!$this->isAdmin) {
351
+            if (!empty($groups)) {
352
+                foreach ($groups as $key => $group) {
353
+                    $groupObject = $this->groupManager->get($group);
354
+                    if($groupObject === null) {
355
+                        unset($groups[$key]);
356
+                        continue;
357
+                    }
358
+
359
+                    if (!$this->groupManager->getSubAdmin()->isSubAdminofGroup($currentUser, $groupObject)) {
360
+                        unset($groups[$key]);
361
+                    }
362
+                }
363
+            }
364
+
365
+            if (empty($groups)) {
366
+                return new DataResponse(
367
+                    array(
368
+                        'message' => $this->l10n->t('No valid group selected'),
369
+                    ),
370
+                    Http::STATUS_FORBIDDEN
371
+                );
372
+            }
373
+        }
374
+
375
+        if ($this->userManager->userExists($username)) {
376
+            return new DataResponse(
377
+                array(
378
+                    'message' => (string)$this->l10n->t('A user with that name already exists.')
379
+                ),
380
+                Http::STATUS_CONFLICT
381
+            );
382
+        }
383
+
384
+        $generatePasswordResetToken = false;
385
+        if ($password === '') {
386
+            if ($email === '') {
387
+                return new DataResponse(
388
+                    array(
389
+                        'message' => (string)$this->l10n->t('To send a password link to the user an email address is required.')
390
+                    ),
391
+                    Http::STATUS_UNPROCESSABLE_ENTITY
392
+                );
393
+            }
394
+
395
+            $password = $this->secureRandom->generate(32);
396
+            $generatePasswordResetToken = true;
397
+        }
398
+
399
+        try {
400
+            $user = $this->userManager->createUser($username, $password);
401
+        } catch (\Exception $exception) {
402
+            $message = $exception->getMessage();
403
+            if (!$message) {
404
+                $message = $this->l10n->t('Unable to create user.');
405
+            }
406
+            return new DataResponse(
407
+                array(
408
+                    'message' => (string) $message,
409
+                ),
410
+                Http::STATUS_FORBIDDEN
411
+            );
412
+        }
413
+
414
+        if($user instanceof IUser) {
415
+            if($groups !== null) {
416
+                foreach($groups as $groupName) {
417
+                    $group = $this->groupManager->get($groupName);
418
+
419
+                    if(empty($group)) {
420
+                        $group = $this->groupManager->createGroup($groupName);
421
+                    }
422
+                    $group->addUser($user);
423
+                }
424
+            }
425
+            /**
426
+             * Send new user mail only if a mail is set
427
+             */
428
+            if($email !== '') {
429
+                $user->setEMailAddress($email);
430
+                try {
431
+                    $emailTemplate = $this->newUserMailHelper->generateTemplate($user, $generatePasswordResetToken);
432
+                    $this->newUserMailHelper->sendMail($user, $emailTemplate);
433
+                } catch(\Exception $e) {
434
+                    $this->log->error("Can't send new user mail to $email: " . $e->getMessage(), array('app' => 'settings'));
435
+                }
436
+            }
437
+            // fetch users groups
438
+            $userGroups = $this->groupManager->getUserGroupIds($user);
439
+
440
+            return new DataResponse(
441
+                $this->formatUserForIndex($user, $userGroups),
442
+                Http::STATUS_CREATED
443
+            );
444
+        }
445
+
446
+        return new DataResponse(
447
+            array(
448
+                'message' => (string)$this->l10n->t('Unable to create user.')
449
+            ),
450
+            Http::STATUS_FORBIDDEN
451
+        );
452
+
453
+    }
454
+
455
+    /**
456
+     * @NoAdminRequired
457
+     * @PasswordConfirmationRequired
458
+     *
459
+     * @param string $id
460
+     * @return DataResponse
461
+     */
462
+    public function destroy($id) {
463
+        $userId = $this->userSession->getUser()->getUID();
464
+        $user = $this->userManager->get($id);
465
+
466
+        if($userId === $id) {
467
+            return new DataResponse(
468
+                array(
469
+                    'status' => 'error',
470
+                    'data' => array(
471
+                        'message' => (string)$this->l10n->t('Unable to delete user.')
472
+                    )
473
+                ),
474
+                Http::STATUS_FORBIDDEN
475
+            );
476
+        }
477
+
478
+        if(!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
479
+            return new DataResponse(
480
+                array(
481
+                    'status' => 'error',
482
+                    'data' => array(
483
+                        'message' => (string)$this->l10n->t('Authentication error')
484
+                    )
485
+                ),
486
+                Http::STATUS_FORBIDDEN
487
+            );
488
+        }
489
+
490
+        if($user) {
491
+            if($user->delete()) {
492
+                return new DataResponse(
493
+                    array(
494
+                        'status' => 'success',
495
+                        'data' => array(
496
+                            'username' => $id
497
+                        )
498
+                    ),
499
+                    Http::STATUS_NO_CONTENT
500
+                );
501
+            }
502
+        }
503
+
504
+        return new DataResponse(
505
+            array(
506
+                'status' => 'error',
507
+                'data' => array(
508
+                    'message' => (string)$this->l10n->t('Unable to delete user.')
509
+                )
510
+            ),
511
+            Http::STATUS_FORBIDDEN
512
+        );
513
+    }
514
+
515
+    /**
516
+     * @NoAdminRequired
517
+     * @NoSubadminRequired
518
+     * @PasswordConfirmationRequired
519
+     *
520
+     * @param string $account
521
+     * @param bool $onlyVerificationCode only return verification code without updating the data
522
+     * @return DataResponse
523
+     */
524
+    public function getVerificationCode($account, $onlyVerificationCode) {
525
+
526
+        $user = $this->userSession->getUser();
527
+
528
+        if ($user === null) {
529
+            return new DataResponse([], Http::STATUS_BAD_REQUEST);
530
+        }
531
+
532
+        $accountData = $this->accountManager->getUser($user);
533
+        $cloudId = $user->getCloudId();
534
+        $message = "Use my Federated Cloud ID to share with me: " . $cloudId;
535
+        $signature = $this->signMessage($user, $message);
536
+
537
+        $code = $message . ' ' . $signature;
538
+        $codeMd5 = $message . ' ' . md5($signature);
539
+
540
+        switch ($account) {
541
+            case 'verify-twitter':
542
+                $accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
543
+                $msg = $this->l10n->t('In order to verify your Twitter account post following tweet on Twitter (please make sure to post it without any line breaks):');
544
+                $code = $codeMd5;
545
+                $type = AccountManager::PROPERTY_TWITTER;
546
+                $data = $accountData[AccountManager::PROPERTY_TWITTER]['value'];
547
+                break;
548
+            case 'verify-website':
549
+                $accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
550
+                $msg = $this->l10n->t('In order to verify your Website store following content in your web-root at \'.well-known/CloudIdVerificationCode.txt\' (please make sure that the complete text is in one line):');
551
+                $type = AccountManager::PROPERTY_WEBSITE;
552
+                $data = $accountData[AccountManager::PROPERTY_WEBSITE]['value'];
553
+                break;
554
+            default:
555
+                return new DataResponse([], Http::STATUS_BAD_REQUEST);
556
+        }
557
+
558
+        if ($onlyVerificationCode === false) {
559
+            $this->accountManager->updateUser($user, $accountData);
560
+
561
+            $this->jobList->add('OC\Settings\BackgroundJobs\VerifyUserData',
562
+                [
563
+                    'verificationCode' => $code,
564
+                    'data' => $data,
565
+                    'type' => $type,
566
+                    'uid' => $user->getUID(),
567
+                    'try' => 0,
568
+                    'lastRun' => $this->getCurrentTime()
569
+                ]
570
+            );
571
+        }
572
+
573
+        return new DataResponse(['msg' => $msg, 'code' => $code]);
574
+    }
575
+
576
+    /**
577
+     * get current timestamp
578
+     *
579
+     * @return int
580
+     */
581
+    protected function getCurrentTime() {
582
+        return time();
583
+    }
584
+
585
+    /**
586
+     * sign message with users private key
587
+     *
588
+     * @param IUser $user
589
+     * @param string $message
590
+     *
591
+     * @return string base64 encoded signature
592
+     */
593
+    protected function signMessage(IUser $user, $message) {
594
+        $privateKey = $this->keyManager->getKey($user)->getPrivate();
595
+        openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
596
+        $signatureBase64 = base64_encode($signature);
597
+
598
+        return $signatureBase64;
599
+    }
600
+
601
+    /**
602
+     * @NoAdminRequired
603
+     * @NoSubadminRequired
604
+     * @PasswordConfirmationRequired
605
+     *
606
+     * @param string $avatarScope
607
+     * @param string $displayname
608
+     * @param string $displaynameScope
609
+     * @param string $phone
610
+     * @param string $phoneScope
611
+     * @param string $email
612
+     * @param string $emailScope
613
+     * @param string $website
614
+     * @param string $websiteScope
615
+     * @param string $address
616
+     * @param string $addressScope
617
+     * @param string $twitter
618
+     * @param string $twitterScope
619
+     * @return DataResponse
620
+     */
621
+    public function setUserSettings($avatarScope,
622
+                                    $displayname,
623
+                                    $displaynameScope,
624
+                                    $phone,
625
+                                    $phoneScope,
626
+                                    $email,
627
+                                    $emailScope,
628
+                                    $website,
629
+                                    $websiteScope,
630
+                                    $address,
631
+                                    $addressScope,
632
+                                    $twitter,
633
+                                    $twitterScope
634
+    ) {
635
+
636
+        if(!empty($email) && !$this->mailer->validateMailAddress($email)) {
637
+            return new DataResponse(
638
+                array(
639
+                    'status' => 'error',
640
+                    'data' => array(
641
+                        'message' => (string)$this->l10n->t('Invalid mail address')
642
+                    )
643
+                ),
644
+                Http::STATUS_UNPROCESSABLE_ENTITY
645
+            );
646
+        }
647
+
648
+        $data = [
649
+            AccountManager::PROPERTY_AVATAR =>  ['scope' => $avatarScope],
650
+            AccountManager::PROPERTY_DISPLAYNAME => ['value' => $displayname, 'scope' => $displaynameScope],
651
+            AccountManager::PROPERTY_EMAIL=> ['value' => $email, 'scope' => $emailScope],
652
+            AccountManager::PROPERTY_WEBSITE => ['value' => $website, 'scope' => $websiteScope],
653
+            AccountManager::PROPERTY_ADDRESS => ['value' => $address, 'scope' => $addressScope],
654
+            AccountManager::PROPERTY_PHONE => ['value' => $phone, 'scope' => $phoneScope],
655
+            AccountManager::PROPERTY_TWITTER => ['value' => $twitter, 'scope' => $twitterScope]
656
+        ];
657
+
658
+        $user = $this->userSession->getUser();
659
+
660
+        try {
661
+            $this->saveUserSettings($user, $data);
662
+            return new DataResponse(
663
+                array(
664
+                    'status' => 'success',
665
+                    'data' => array(
666
+                        'userId' => $user->getUID(),
667
+                        'avatarScope' => $avatarScope,
668
+                        'displayname' => $displayname,
669
+                        'displaynameScope' => $displaynameScope,
670
+                        'email' => $email,
671
+                        'emailScope' => $emailScope,
672
+                        'website' => $website,
673
+                        'websiteScope' => $websiteScope,
674
+                        'address' => $address,
675
+                        'addressScope' => $addressScope,
676
+                        'message' => (string)$this->l10n->t('Settings saved')
677
+                    )
678
+                ),
679
+                Http::STATUS_OK
680
+            );
681
+        } catch (ForbiddenException $e) {
682
+            return new DataResponse([
683
+                'status' => 'error',
684
+                'data' => [
685
+                    'message' => $e->getMessage()
686
+                ],
687
+            ]);
688
+        }
689
+
690
+    }
691
+
692
+
693
+    /**
694
+     * update account manager with new user data
695
+     *
696
+     * @param IUser $user
697
+     * @param array $data
698
+     * @throws ForbiddenException
699
+     */
700
+    protected function saveUserSettings(IUser $user, $data) {
701
+
702
+        // keep the user back-end up-to-date with the latest display name and email
703
+        // address
704
+        $oldDisplayName = $user->getDisplayName();
705
+        $oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
706
+        if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
707
+            && $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value']
708
+        ) {
709
+            $result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']);
710
+            if ($result === false) {
711
+                throw new ForbiddenException($this->l10n->t('Unable to change full name'));
712
+            }
713
+        }
714
+
715
+        $oldEmailAddress = $user->getEMailAddress();
716
+        $oldEmailAddress = is_null($oldEmailAddress) ? '' : $oldEmailAddress;
717
+        if (isset($data[AccountManager::PROPERTY_EMAIL]['value'])
718
+            && $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value']
719
+        ) {
720
+            // this is the only permission a backend provides and is also used
721
+            // for the permission of setting a email address
722
+            if (!$user->canChangeDisplayName()) {
723
+                throw new ForbiddenException($this->l10n->t('Unable to change email address'));
724
+            }
725
+            $user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']);
726
+        }
727
+
728
+        $this->accountManager->updateUser($user, $data);
729
+    }
730
+
731
+    /**
732
+     * Count all unique users visible for the current admin/subadmin.
733
+     *
734
+     * @NoAdminRequired
735
+     *
736
+     * @return DataResponse
737
+     */
738
+    public function stats() {
739
+        $userCount = 0;
740
+        if ($this->isAdmin) {
741
+            $countByBackend = $this->userManager->countUsers();
742
+
743
+            if (!empty($countByBackend)) {
744
+                foreach ($countByBackend as $count) {
745
+                    $userCount += $count;
746
+                }
747
+            }
748
+        } else {
749
+            $groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser());
750
+
751
+            $uniqueUsers = [];
752
+            foreach ($groups as $group) {
753
+                foreach($group->getUsers() as $uid => $displayName) {
754
+                    $uniqueUsers[$uid] = true;
755
+                }
756
+            }
757
+
758
+            $userCount = count($uniqueUsers);
759
+        }
760
+
761
+        return new DataResponse(
762
+            [
763
+                'totalUsers' => $userCount
764
+            ]
765
+        );
766
+    }
767
+
768
+
769
+    /**
770
+     * Set the displayName of a user
771
+     *
772
+     * @NoAdminRequired
773
+     * @NoSubadminRequired
774
+     * @PasswordConfirmationRequired
775
+     * @todo merge into saveUserSettings
776
+     *
777
+     * @param string $username
778
+     * @param string $displayName
779
+     * @return DataResponse
780
+     */
781
+    public function setDisplayName($username, $displayName) {
782
+        $currentUser = $this->userSession->getUser();
783
+        $user = $this->userManager->get($username);
784
+
785
+        if ($user === null ||
786
+            !$user->canChangeDisplayName() ||
787
+            (
788
+                !$this->groupManager->isAdmin($currentUser->getUID()) &&
789
+                !$this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $user) &&
790
+                $currentUser->getUID() !== $username
791
+
792
+            )
793
+        ) {
794
+            return new DataResponse([
795
+                'status' => 'error',
796
+                'data' => [
797
+                    'message' => $this->l10n->t('Authentication error'),
798
+                ],
799
+            ]);
800
+        }
801
+
802
+        $userData = $this->accountManager->getUser($user);
803
+        $userData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $displayName;
804
+
805
+
806
+        try {
807
+            $this->saveUserSettings($user, $userData);
808
+            return new DataResponse([
809
+                'status' => 'success',
810
+                'data' => [
811
+                    'message' => $this->l10n->t('Your full name has been changed.'),
812
+                    'username' => $username,
813
+                    'displayName' => $displayName,
814
+                ],
815
+            ]);
816
+        } catch (ForbiddenException $e) {
817
+            return new DataResponse([
818
+                'status' => 'error',
819
+                'data' => [
820
+                    'message' => $e->getMessage(),
821
+                    'displayName' => $user->getDisplayName(),
822
+                ],
823
+            ]);
824
+        }
825
+    }
826
+
827
+    /**
828
+     * Set the mail address of a user
829
+     *
830
+     * @NoAdminRequired
831
+     * @NoSubadminRequired
832
+     * @PasswordConfirmationRequired
833
+     *
834
+     * @param string $id
835
+     * @param string $mailAddress
836
+     * @return DataResponse
837
+     */
838
+    public function setEMailAddress($id, $mailAddress) {
839
+        $user = $this->userManager->get($id);
840
+        if (!$this->isAdmin
841
+            && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)
842
+        ) {
843
+            return new DataResponse(
844
+                array(
845
+                    'status' => 'error',
846
+                    'data' => array(
847
+                        'message' => (string)$this->l10n->t('Forbidden')
848
+                    )
849
+                ),
850
+                Http::STATUS_FORBIDDEN
851
+            );
852
+        }
853
+
854
+        if($mailAddress !== '' && !$this->mailer->validateMailAddress($mailAddress)) {
855
+            return new DataResponse(
856
+                array(
857
+                    'status' => 'error',
858
+                    'data' => array(
859
+                        'message' => (string)$this->l10n->t('Invalid mail address')
860
+                    )
861
+                ),
862
+                Http::STATUS_UNPROCESSABLE_ENTITY
863
+            );
864
+        }
865
+
866
+        if (!$user) {
867
+            return new DataResponse(
868
+                array(
869
+                    'status' => 'error',
870
+                    'data' => array(
871
+                        'message' => (string)$this->l10n->t('Invalid user')
872
+                    )
873
+                ),
874
+                Http::STATUS_UNPROCESSABLE_ENTITY
875
+            );
876
+        }
877
+        // this is the only permission a backend provides and is also used
878
+        // for the permission of setting a email address
879
+        if (!$user->canChangeDisplayName()) {
880
+            return new DataResponse(
881
+                array(
882
+                    'status' => 'error',
883
+                    'data' => array(
884
+                        'message' => (string)$this->l10n->t('Unable to change mail address')
885
+                    )
886
+                ),
887
+                Http::STATUS_FORBIDDEN
888
+            );
889
+        }
890
+
891
+        $userData = $this->accountManager->getUser($user);
892
+        $userData[AccountManager::PROPERTY_EMAIL]['value'] = $mailAddress;
893
+
894
+        try {
895
+            $this->saveUserSettings($user, $userData);
896
+            return new DataResponse(
897
+                array(
898
+                    'status' => 'success',
899
+                    'data' => array(
900
+                        'username' => $id,
901
+                        'mailAddress' => $mailAddress,
902
+                        'message' => (string)$this->l10n->t('Email saved')
903
+                    )
904
+                ),
905
+                Http::STATUS_OK
906
+            );
907
+        } catch (ForbiddenException $e) {
908
+            return new DataResponse([
909
+                'status' => 'error',
910
+                'data' => [
911
+                    'message' => $e->getMessage()
912
+                ],
913
+            ]);
914
+        }
915
+    }
916 916
 
917 917
 }
Please login to merge, or discard this patch.