Completed
Push — master ( 2a7733...130780 )
by Morris
25s
created

AccountManager::checkEmailVerification()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 11
nc 2
nop 3
dl 0
loc 17
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Björn Schießle <[email protected]>
4
 *
5
 * @copyright Copyright (c) 2016, ownCloud, Inc.
6
 * @copyright Copyright (c) 2016, Björn Schießle
7
 * @license AGPL-3.0
8
 *
9
 * This code is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License, version 3,
11
 * as published by the Free Software Foundation.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Affero General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Affero General Public License, version 3,
19
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
20
 *
21
 */
22
23
24
namespace OC\Accounts;
25
26
use OCP\BackgroundJob\IJobList;
27
use OCP\IDBConnection;
28
use OCP\IUser;
29
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
30
use Symfony\Component\EventDispatcher\GenericEvent;
31
32
/**
33
 * Class AccountManager
34
 *
35
 * Manage system accounts table
36
 *
37
 * @group DB
38
 * @package OC\Accounts
39
 */
40
class AccountManager {
41
42
	/** nobody can see my account details */
43
	const VISIBILITY_PRIVATE = 'private';
44
	/** only contacts, especially trusted servers can see my contact details */
45
	const VISIBILITY_CONTACTS_ONLY = 'contacts';
46
	/** every body ca see my contact detail, will be published to the lookup server */
47
	const VISIBILITY_PUBLIC = 'public';
48
49
	const PROPERTY_AVATAR = 'avatar';
50
	const PROPERTY_DISPLAYNAME = 'displayname';
51
	const PROPERTY_PHONE = 'phone';
52
	const PROPERTY_EMAIL = 'email';
53
	const PROPERTY_WEBSITE = 'website';
54
	const PROPERTY_ADDRESS = 'address';
55
	const PROPERTY_TWITTER = 'twitter';
56
57
	const NOT_VERIFIED = '0';
58
	const VERIFICATION_IN_PROGRESS = '1';
59
	const VERIFIED = '2';
60
61
	/** @var  IDBConnection database connection */
62
	private $connection;
63
64
	/** @var string table name */
65
	private $table = 'accounts';
66
67
	/** @var EventDispatcherInterface */
68
	private $eventDispatcher;
69
70
	/** @var IJobList */
71
	private $jobList;
72
73
	/**
74
	 * AccountManager constructor.
75
	 *
76
	 * @param IDBConnection $connection
77
	 * @param EventDispatcherInterface $eventDispatcher
78
	 * @param IJobList $jobList
79
	 */
80
	public function __construct(IDBConnection $connection,
81
								EventDispatcherInterface $eventDispatcher,
82
								IJobList $jobList) {
83
		$this->connection = $connection;
84
		$this->eventDispatcher = $eventDispatcher;
85
		$this->jobList = $jobList;
86
	}
87
88
	/**
89
	 * update user record
90
	 *
91
	 * @param IUser $user
92
	 * @param $data
93
	 */
94
	public function updateUser(IUser $user, $data) {
95
		$userData = $this->getUser($user);
96
		$updated = true;
97
		if (empty($userData)) {
98
			$this->insertNewUser($user, $data);
99
		} elseif ($userData !== $data) {
100
			$data = $this->checkEmailVerification($userData, $data, $user);
101
			$data = $this->updateVerifyStatus($userData, $data);
102
			$this->updateExistingUser($user, $data);
103
		} else {
104
			// nothing needs to be done if new and old data set are the same
105
			$updated = false;
106
		}
107
108
		if ($updated) {
109
			$this->eventDispatcher->dispatch(
110
				'OC\AccountManager::userUpdated',
111
				new GenericEvent($user, $data)
112
			);
113
		}
114
	}
115
116
	/**
117
	 * get stored data from a given user
118
	 *
119
	 * @param IUser $user
120
	 * @return array
121
	 */
122
	public function getUser(IUser $user) {
123
		$uid = $user->getUID();
124
		$query = $this->connection->getQueryBuilder();
125
		$query->select('data')->from($this->table)
126
			->where($query->expr()->eq('uid', $query->createParameter('uid')))
127
			->setParameter('uid', $uid);
128
		$query->execute();
129
		$result = $query->execute()->fetchAll();
130
131
		if (empty($result)) {
132
			$userData = $this->buildDefaultUserRecord($user);
133
			$this->insertNewUser($user, $userData);
134
			return $userData;
135
		}
136
137
		$userDataArray = json_decode($result[0]['data'], true);
138
139
		$userDataArray = $this->addMissingDefaultValues($userDataArray);
140
141
		return $userDataArray;
142
	}
143
144
	/**
145
	 * check if we need to ask the server for email verification, if yes we create a cronjob
146
	 *
147
	 * @param $oldData
148
	 * @param $newData
149
	 * @param IUser $user
150
	 * @return array
151
	 */
152
	protected function checkEmailVerification($oldData, $newData, IUser $user) {
153
		if ($oldData[self::PROPERTY_EMAIL]['value'] !== $newData[self::PROPERTY_EMAIL]['value']) {
154
			$this->jobList->add('OC\Settings\BackgroundJobs\VerifyUserData',
155
				[
156
					'verificationCode' => '',
157
					'data' => $newData[self::PROPERTY_EMAIL]['value'],
158
					'type' => self::PROPERTY_EMAIL,
159
					'uid' => $user->getUID(),
160
					'try' => 0,
161
					'lastRun' => time()
162
				]
163
			);
164
			$newData[AccountManager::PROPERTY_EMAIL]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
165
		}
166
167
		return $newData;
168
	}
169
170
	/**
171
	 * make sure that all expected data are set
172
	 *
173
	 * @param array $userData
174
	 * @return array
175
	 */
176
	protected function addMissingDefaultValues(array $userData) {
177
178
		foreach ($userData as $key => $value) {
179
			if (!isset($userData[$key]['verified'])) {
180
				$userData[$key]['verified'] = self::NOT_VERIFIED;
181
			}
182
		}
183
184
		return $userData;
185
	}
186
187
	/**
188
	 * reset verification status if personal data changed
189
	 *
190
	 * @param array $oldData
191
	 * @param array $newData
192
	 * @return array
193
	 */
194
	protected function updateVerifyStatus($oldData, $newData) {
195
196
		// which account was already verified successfully?
197
		$twitterVerified = isset($oldData[self::PROPERTY_TWITTER]['verified']) && $oldData[self::PROPERTY_TWITTER]['verified'] === self::VERIFIED;
198
		$websiteVerified = isset($oldData[self::PROPERTY_WEBSITE]['verified']) && $oldData[self::PROPERTY_WEBSITE]['verified'] === self::VERIFIED;
199
		$emailVerified = isset($oldData[self::PROPERTY_EMAIL]['verified']) && $oldData[self::PROPERTY_EMAIL]['verified'] === self::VERIFIED;
200
201
		// keep old verification status if we don't have a new one
202 View Code Duplication
		if(!isset($newData[self::PROPERTY_TWITTER]['verified'])) {
203
			// keep old verification status if value didn't changed and an old value exists
204
			$keepOldStatus = $newData[self::PROPERTY_TWITTER]['value'] === $oldData[self::PROPERTY_TWITTER]['value'] && isset($oldData[self::PROPERTY_TWITTER]['verified']);
205
			$newData[self::PROPERTY_TWITTER]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_TWITTER]['verified'] : self::NOT_VERIFIED;
206
		}
207
208 View Code Duplication
		if(!isset($newData[self::PROPERTY_WEBSITE]['verified'])) {
209
			// keep old verification status if value didn't changed and an old value exists
210
			$keepOldStatus = $newData[self::PROPERTY_WEBSITE]['value'] === $oldData[self::PROPERTY_WEBSITE]['value'] && isset($oldData[self::PROPERTY_WEBSITE]['verified']);
211
			$newData[self::PROPERTY_WEBSITE]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_WEBSITE]['verified'] : self::NOT_VERIFIED;
212
		}
213
214 View Code Duplication
		if(!isset($newData[self::PROPERTY_EMAIL]['verified'])) {
215
			// keep old verification status if value didn't changed and an old value exists
216
			$keepOldStatus = $newData[self::PROPERTY_EMAIL]['value'] === $oldData[self::PROPERTY_EMAIL]['value'] && isset($oldData[self::PROPERTY_EMAIL]['verified']);
217
			$newData[self::PROPERTY_EMAIL]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_EMAIL]['verified'] : self::VERIFICATION_IN_PROGRESS;
218
		}
219
220
		// reset verification status if a value from a previously verified data was changed
221 View Code Duplication
		if($twitterVerified &&
222
			$oldData[self::PROPERTY_TWITTER]['value'] !== $newData[self::PROPERTY_TWITTER]['value']
223
		) {
224
			$newData[self::PROPERTY_TWITTER]['verified'] = self::NOT_VERIFIED;
225
		}
226
227 View Code Duplication
		if($websiteVerified &&
228
			$oldData[self::PROPERTY_WEBSITE]['value'] !== $newData[self::PROPERTY_WEBSITE]['value']
229
		) {
230
			$newData[self::PROPERTY_WEBSITE]['verified'] = self::NOT_VERIFIED;
231
		}
232
233 View Code Duplication
		if($emailVerified &&
234
			$oldData[self::PROPERTY_EMAIL]['value'] !== $newData[self::PROPERTY_EMAIL]['value']
235
		) {
236
			$newData[self::PROPERTY_EMAIL]['verified'] = self::NOT_VERIFIED;
237
		}
238
239
		return $newData;
240
241
	}
242
243
	/**
244
	 * add new user to accounts table
245
	 *
246
	 * @param IUser $user
247
	 * @param array $data
248
	 */
249
	protected function insertNewUser(IUser $user, $data) {
250
		$uid = $user->getUID();
251
		$jsonEncodedData = json_encode($data);
252
		$query = $this->connection->getQueryBuilder();
253
		$query->insert($this->table)
254
			->values(
255
				[
256
					'uid' => $query->createNamedParameter($uid),
257
					'data' => $query->createNamedParameter($jsonEncodedData),
258
				]
259
			)
260
			->execute();
261
	}
262
263
	/**
264
	 * update existing user in accounts table
265
	 *
266
	 * @param IUser $user
267
	 * @param array $data
268
	 */
269 View Code Duplication
	protected function updateExistingUser(IUser $user, $data) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
270
		$uid = $user->getUID();
271
		$jsonEncodedData = json_encode($data);
272
		$query = $this->connection->getQueryBuilder();
273
		$query->update($this->table)
274
			->set('data', $query->createNamedParameter($jsonEncodedData))
275
			->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
276
			->execute();
277
	}
278
279
	/**
280
	 * build default user record in case not data set exists yet
281
	 *
282
	 * @param IUser $user
283
	 * @return array
284
	 */
285
	protected function buildDefaultUserRecord(IUser $user) {
286
		return [
287
			self::PROPERTY_DISPLAYNAME =>
288
				[
289
					'value' => $user->getDisplayName(),
290
					'scope' => self::VISIBILITY_CONTACTS_ONLY,
291
					'verified' => self::NOT_VERIFIED,
292
				],
293
			self::PROPERTY_ADDRESS =>
294
				[
295
					'value' => '',
296
					'scope' => self::VISIBILITY_PRIVATE,
297
					'verified' => self::NOT_VERIFIED,
298
				],
299
			self::PROPERTY_WEBSITE =>
300
				[
301
					'value' => '',
302
					'scope' => self::VISIBILITY_PRIVATE,
303
					'verified' => self::NOT_VERIFIED,
304
				],
305
			self::PROPERTY_EMAIL =>
306
				[
307
					'value' => $user->getEMailAddress(),
308
					'scope' => self::VISIBILITY_CONTACTS_ONLY,
309
					'verified' => self::NOT_VERIFIED,
310
				],
311
			self::PROPERTY_AVATAR =>
312
				[
313
					'scope' => self::VISIBILITY_CONTACTS_ONLY
314
				],
315
			self::PROPERTY_PHONE =>
316
				[
317
					'value' => '',
318
					'scope' => self::VISIBILITY_PRIVATE,
319
					'verified' => self::NOT_VERIFIED,
320
				],
321
			self::PROPERTY_TWITTER =>
322
				[
323
					'value' => '',
324
					'scope' => self::VISIBILITY_PRIVATE,
325
					'verified' => self::NOT_VERIFIED,
326
				],
327
		];
328
	}
329
330
}
331