|
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
|
|
|
* delete user from accounts table |
|
118
|
|
|
* |
|
119
|
|
|
* @param IUser $user |
|
120
|
|
|
*/ |
|
121
|
|
View Code Duplication |
public function deleteUser(IUser $user) { |
|
|
|
|
|
|
122
|
|
|
$uid = $user->getUID(); |
|
123
|
|
|
$query = $this->connection->getQueryBuilder(); |
|
124
|
|
|
$query->delete($this->table) |
|
125
|
|
|
->where($query->expr()->eq('uid', $query->createNamedParameter($uid))) |
|
126
|
|
|
->execute(); |
|
127
|
|
|
} |
|
128
|
|
|
|
|
129
|
|
|
/** |
|
130
|
|
|
* get stored data from a given user |
|
131
|
|
|
* |
|
132
|
|
|
* @param IUser $user |
|
133
|
|
|
* @return array |
|
134
|
|
|
*/ |
|
135
|
|
|
public function getUser(IUser $user) { |
|
136
|
|
|
$uid = $user->getUID(); |
|
137
|
|
|
$query = $this->connection->getQueryBuilder(); |
|
138
|
|
|
$query->select('data')->from($this->table) |
|
139
|
|
|
->where($query->expr()->eq('uid', $query->createParameter('uid'))) |
|
140
|
|
|
->setParameter('uid', $uid); |
|
141
|
|
|
$query->execute(); |
|
142
|
|
|
$result = $query->execute()->fetchAll(); |
|
143
|
|
|
|
|
144
|
|
|
if (empty($result)) { |
|
145
|
|
|
$userData = $this->buildDefaultUserRecord($user); |
|
146
|
|
|
$this->insertNewUser($user, $userData); |
|
147
|
|
|
return $userData; |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
$userDataArray = json_decode($result[0]['data'], true); |
|
151
|
|
|
|
|
152
|
|
|
$userDataArray = $this->addMissingDefaultValues($userDataArray); |
|
153
|
|
|
|
|
154
|
|
|
return $userDataArray; |
|
155
|
|
|
} |
|
156
|
|
|
|
|
157
|
|
|
/** |
|
158
|
|
|
* check if we need to ask the server for email verification, if yes we create a cronjob |
|
159
|
|
|
* |
|
160
|
|
|
* @param $oldData |
|
161
|
|
|
* @param $newData |
|
162
|
|
|
* @param IUser $user |
|
163
|
|
|
* @return array |
|
164
|
|
|
*/ |
|
165
|
|
|
protected function checkEmailVerification($oldData, $newData, IUser $user) { |
|
166
|
|
|
if ($oldData[self::PROPERTY_EMAIL]['value'] !== $newData[self::PROPERTY_EMAIL]['value']) { |
|
167
|
|
|
$this->jobList->add('OC\Settings\BackgroundJobs\VerifyUserData', |
|
168
|
|
|
[ |
|
169
|
|
|
'verificationCode' => '', |
|
170
|
|
|
'data' => $newData[self::PROPERTY_EMAIL]['value'], |
|
171
|
|
|
'type' => self::PROPERTY_EMAIL, |
|
172
|
|
|
'uid' => $user->getUID(), |
|
173
|
|
|
'try' => 0, |
|
174
|
|
|
'lastRun' => time() |
|
175
|
|
|
] |
|
176
|
|
|
); |
|
177
|
|
|
$newData[AccountManager::PROPERTY_EMAIL]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS; |
|
178
|
|
|
} |
|
179
|
|
|
|
|
180
|
|
|
return $newData; |
|
181
|
|
|
} |
|
182
|
|
|
|
|
183
|
|
|
/** |
|
184
|
|
|
* make sure that all expected data are set |
|
185
|
|
|
* |
|
186
|
|
|
* @param array $userData |
|
187
|
|
|
* @return array |
|
188
|
|
|
*/ |
|
189
|
|
|
protected function addMissingDefaultValues(array $userData) { |
|
190
|
|
|
|
|
191
|
|
|
foreach ($userData as $key => $value) { |
|
192
|
|
|
if (!isset($userData[$key]['verified'])) { |
|
193
|
|
|
$userData[$key]['verified'] = self::NOT_VERIFIED; |
|
194
|
|
|
} |
|
195
|
|
|
} |
|
196
|
|
|
|
|
197
|
|
|
return $userData; |
|
198
|
|
|
} |
|
199
|
|
|
|
|
200
|
|
|
/** |
|
201
|
|
|
* reset verification status if personal data changed |
|
202
|
|
|
* |
|
203
|
|
|
* @param array $oldData |
|
204
|
|
|
* @param array $newData |
|
205
|
|
|
* @return array |
|
206
|
|
|
*/ |
|
207
|
|
|
protected function updateVerifyStatus($oldData, $newData) { |
|
208
|
|
|
|
|
209
|
|
|
// which account was already verified successfully? |
|
210
|
|
|
$twitterVerified = isset($oldData[self::PROPERTY_TWITTER]['verified']) && $oldData[self::PROPERTY_TWITTER]['verified'] === self::VERIFIED; |
|
211
|
|
|
$websiteVerified = isset($oldData[self::PROPERTY_WEBSITE]['verified']) && $oldData[self::PROPERTY_WEBSITE]['verified'] === self::VERIFIED; |
|
212
|
|
|
$emailVerified = isset($oldData[self::PROPERTY_EMAIL]['verified']) && $oldData[self::PROPERTY_EMAIL]['verified'] === self::VERIFIED; |
|
213
|
|
|
|
|
214
|
|
|
// keep old verification status if we don't have a new one |
|
215
|
|
View Code Duplication |
if(!isset($newData[self::PROPERTY_TWITTER]['verified'])) { |
|
216
|
|
|
// keep old verification status if value didn't changed and an old value exists |
|
217
|
|
|
$keepOldStatus = $newData[self::PROPERTY_TWITTER]['value'] === $oldData[self::PROPERTY_TWITTER]['value'] && isset($oldData[self::PROPERTY_TWITTER]['verified']); |
|
218
|
|
|
$newData[self::PROPERTY_TWITTER]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_TWITTER]['verified'] : self::NOT_VERIFIED; |
|
219
|
|
|
} |
|
220
|
|
|
|
|
221
|
|
View Code Duplication |
if(!isset($newData[self::PROPERTY_WEBSITE]['verified'])) { |
|
222
|
|
|
// keep old verification status if value didn't changed and an old value exists |
|
223
|
|
|
$keepOldStatus = $newData[self::PROPERTY_WEBSITE]['value'] === $oldData[self::PROPERTY_WEBSITE]['value'] && isset($oldData[self::PROPERTY_WEBSITE]['verified']); |
|
224
|
|
|
$newData[self::PROPERTY_WEBSITE]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_WEBSITE]['verified'] : self::NOT_VERIFIED; |
|
225
|
|
|
} |
|
226
|
|
|
|
|
227
|
|
View Code Duplication |
if(!isset($newData[self::PROPERTY_EMAIL]['verified'])) { |
|
228
|
|
|
// keep old verification status if value didn't changed and an old value exists |
|
229
|
|
|
$keepOldStatus = $newData[self::PROPERTY_EMAIL]['value'] === $oldData[self::PROPERTY_EMAIL]['value'] && isset($oldData[self::PROPERTY_EMAIL]['verified']); |
|
230
|
|
|
$newData[self::PROPERTY_EMAIL]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_EMAIL]['verified'] : self::VERIFICATION_IN_PROGRESS; |
|
231
|
|
|
} |
|
232
|
|
|
|
|
233
|
|
|
// reset verification status if a value from a previously verified data was changed |
|
234
|
|
View Code Duplication |
if($twitterVerified && |
|
235
|
|
|
$oldData[self::PROPERTY_TWITTER]['value'] !== $newData[self::PROPERTY_TWITTER]['value'] |
|
236
|
|
|
) { |
|
237
|
|
|
$newData[self::PROPERTY_TWITTER]['verified'] = self::NOT_VERIFIED; |
|
238
|
|
|
} |
|
239
|
|
|
|
|
240
|
|
View Code Duplication |
if($websiteVerified && |
|
241
|
|
|
$oldData[self::PROPERTY_WEBSITE]['value'] !== $newData[self::PROPERTY_WEBSITE]['value'] |
|
242
|
|
|
) { |
|
243
|
|
|
$newData[self::PROPERTY_WEBSITE]['verified'] = self::NOT_VERIFIED; |
|
244
|
|
|
} |
|
245
|
|
|
|
|
246
|
|
View Code Duplication |
if($emailVerified && |
|
247
|
|
|
$oldData[self::PROPERTY_EMAIL]['value'] !== $newData[self::PROPERTY_EMAIL]['value'] |
|
248
|
|
|
) { |
|
249
|
|
|
$newData[self::PROPERTY_EMAIL]['verified'] = self::NOT_VERIFIED; |
|
250
|
|
|
} |
|
251
|
|
|
|
|
252
|
|
|
return $newData; |
|
253
|
|
|
|
|
254
|
|
|
} |
|
255
|
|
|
|
|
256
|
|
|
/** |
|
257
|
|
|
* add new user to accounts table |
|
258
|
|
|
* |
|
259
|
|
|
* @param IUser $user |
|
260
|
|
|
* @param array $data |
|
261
|
|
|
*/ |
|
262
|
|
|
protected function insertNewUser(IUser $user, $data) { |
|
263
|
|
|
$uid = $user->getUID(); |
|
264
|
|
|
$jsonEncodedData = json_encode($data); |
|
265
|
|
|
$query = $this->connection->getQueryBuilder(); |
|
266
|
|
|
$query->insert($this->table) |
|
267
|
|
|
->values( |
|
268
|
|
|
[ |
|
269
|
|
|
'uid' => $query->createNamedParameter($uid), |
|
270
|
|
|
'data' => $query->createNamedParameter($jsonEncodedData), |
|
271
|
|
|
] |
|
272
|
|
|
) |
|
273
|
|
|
->execute(); |
|
274
|
|
|
} |
|
275
|
|
|
|
|
276
|
|
|
/** |
|
277
|
|
|
* update existing user in accounts table |
|
278
|
|
|
* |
|
279
|
|
|
* @param IUser $user |
|
280
|
|
|
* @param array $data |
|
281
|
|
|
*/ |
|
282
|
|
View Code Duplication |
protected function updateExistingUser(IUser $user, $data) { |
|
|
|
|
|
|
283
|
|
|
$uid = $user->getUID(); |
|
284
|
|
|
$jsonEncodedData = json_encode($data); |
|
285
|
|
|
$query = $this->connection->getQueryBuilder(); |
|
286
|
|
|
$query->update($this->table) |
|
287
|
|
|
->set('data', $query->createNamedParameter($jsonEncodedData)) |
|
288
|
|
|
->where($query->expr()->eq('uid', $query->createNamedParameter($uid))) |
|
289
|
|
|
->execute(); |
|
290
|
|
|
} |
|
291
|
|
|
|
|
292
|
|
|
/** |
|
293
|
|
|
* build default user record in case not data set exists yet |
|
294
|
|
|
* |
|
295
|
|
|
* @param IUser $user |
|
296
|
|
|
* @return array |
|
297
|
|
|
*/ |
|
298
|
|
|
protected function buildDefaultUserRecord(IUser $user) { |
|
299
|
|
|
return [ |
|
300
|
|
|
self::PROPERTY_DISPLAYNAME => |
|
301
|
|
|
[ |
|
302
|
|
|
'value' => $user->getDisplayName(), |
|
303
|
|
|
'scope' => self::VISIBILITY_CONTACTS_ONLY, |
|
304
|
|
|
'verified' => self::NOT_VERIFIED, |
|
305
|
|
|
], |
|
306
|
|
|
self::PROPERTY_ADDRESS => |
|
307
|
|
|
[ |
|
308
|
|
|
'value' => '', |
|
309
|
|
|
'scope' => self::VISIBILITY_PRIVATE, |
|
310
|
|
|
'verified' => self::NOT_VERIFIED, |
|
311
|
|
|
], |
|
312
|
|
|
self::PROPERTY_WEBSITE => |
|
313
|
|
|
[ |
|
314
|
|
|
'value' => '', |
|
315
|
|
|
'scope' => self::VISIBILITY_PRIVATE, |
|
316
|
|
|
'verified' => self::NOT_VERIFIED, |
|
317
|
|
|
], |
|
318
|
|
|
self::PROPERTY_EMAIL => |
|
319
|
|
|
[ |
|
320
|
|
|
'value' => $user->getEMailAddress(), |
|
321
|
|
|
'scope' => self::VISIBILITY_CONTACTS_ONLY, |
|
322
|
|
|
'verified' => self::NOT_VERIFIED, |
|
323
|
|
|
], |
|
324
|
|
|
self::PROPERTY_AVATAR => |
|
325
|
|
|
[ |
|
326
|
|
|
'scope' => self::VISIBILITY_CONTACTS_ONLY |
|
327
|
|
|
], |
|
328
|
|
|
self::PROPERTY_PHONE => |
|
329
|
|
|
[ |
|
330
|
|
|
'value' => '', |
|
331
|
|
|
'scope' => self::VISIBILITY_PRIVATE, |
|
332
|
|
|
'verified' => self::NOT_VERIFIED, |
|
333
|
|
|
], |
|
334
|
|
|
self::PROPERTY_TWITTER => |
|
335
|
|
|
[ |
|
336
|
|
|
'value' => '', |
|
337
|
|
|
'scope' => self::VISIBILITY_PRIVATE, |
|
338
|
|
|
'verified' => self::NOT_VERIFIED, |
|
339
|
|
|
], |
|
340
|
|
|
]; |
|
341
|
|
|
} |
|
342
|
|
|
|
|
343
|
|
|
} |
|
344
|
|
|
|
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.