|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* Copyright (C) 2016 SURFnet. |
|
4
|
|
|
* |
|
5
|
|
|
* This program is free software: you can redistribute it and/or modify |
|
6
|
|
|
* it under the terms of the GNU Affero General Public License as |
|
7
|
|
|
* published by the Free Software Foundation, either version 3 of the |
|
8
|
|
|
* License, or (at your option) any later version. |
|
9
|
|
|
* |
|
10
|
|
|
* This program is distributed in the hope that it will be useful, |
|
11
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13
|
|
|
* GNU Affero General Public License for more details. |
|
14
|
|
|
* |
|
15
|
|
|
* You should have received a copy of the GNU Affero General Public License |
|
16
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
17
|
|
|
*/ |
|
18
|
|
|
|
|
19
|
|
|
namespace SURFnet\VPN\Server; |
|
20
|
|
|
|
|
21
|
|
|
use DateTime; |
|
22
|
|
|
use PDO; |
|
23
|
|
|
use PDOException; |
|
24
|
|
|
|
|
25
|
|
|
class Storage |
|
26
|
|
|
{ |
|
27
|
|
|
/** @var \PDO */ |
|
28
|
|
|
private $db; |
|
29
|
|
|
|
|
30
|
|
|
public function __construct(PDO $db) |
|
31
|
|
|
{ |
|
32
|
|
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
|
33
|
|
|
$this->db = $db; |
|
34
|
|
|
|
|
35
|
|
|
if ('sqlite' === $this->db->getAttribute(PDO::ATTR_DRIVER_NAME)) { |
|
36
|
|
|
$this->db->query('PRAGMA foreign_keys = ON'); |
|
37
|
|
|
} |
|
38
|
|
|
} |
|
39
|
|
|
|
|
40
|
|
|
public function getId($userId) |
|
41
|
|
|
{ |
|
42
|
|
|
$stmt = $this->db->prepare( |
|
43
|
|
|
<<< 'SQL' |
|
44
|
|
|
SELECT |
|
45
|
|
|
id |
|
46
|
|
|
FROM |
|
47
|
|
|
users |
|
48
|
|
|
WHERE user_id = :user_id |
|
49
|
|
|
SQL |
|
50
|
|
|
); |
|
51
|
|
|
|
|
52
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_STR); |
|
53
|
|
|
$stmt->execute(); |
|
54
|
|
|
if (false !== $result = $stmt->fetch(PDO::FETCH_ASSOC)) { |
|
55
|
|
|
return $result['id']; |
|
56
|
|
|
} |
|
57
|
|
|
|
|
58
|
|
|
// user does not exist yet, add it |
|
59
|
|
|
$stmt = $this->db->prepare( |
|
60
|
|
|
<<< 'SQL' |
|
61
|
|
|
INSERT INTO |
|
62
|
|
|
users ( |
|
63
|
|
|
user_id |
|
64
|
|
|
) |
|
65
|
|
|
VALUES ( |
|
66
|
|
|
:user_id |
|
67
|
|
|
) |
|
68
|
|
|
SQL |
|
69
|
|
|
); |
|
70
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_STR); |
|
71
|
|
|
$stmt->execute(); |
|
72
|
|
|
|
|
73
|
|
|
return $this->db->lastInsertId('id'); |
|
74
|
|
|
} |
|
75
|
|
|
|
|
76
|
|
|
public function getUsers() |
|
77
|
|
|
{ |
|
78
|
|
|
$stmt = $this->db->prepare( |
|
79
|
|
|
<<< 'SQL' |
|
80
|
|
|
SELECT |
|
81
|
|
|
user_id, |
|
82
|
|
|
is_disabled |
|
83
|
|
|
FROM |
|
84
|
|
|
users |
|
85
|
|
|
SQL |
|
86
|
|
|
); |
|
87
|
|
|
$stmt->execute(); |
|
88
|
|
|
|
|
89
|
|
|
$userList = []; |
|
90
|
|
|
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $result) { |
|
91
|
|
|
$userList[] = [ |
|
92
|
|
|
'user_id' => $result['user_id'], |
|
93
|
|
|
'is_disabled' => (bool) $result['is_disabled'], |
|
94
|
|
|
]; |
|
95
|
|
|
} |
|
96
|
|
|
|
|
97
|
|
|
return $userList; |
|
98
|
|
|
} |
|
99
|
|
|
|
|
100
|
|
View Code Duplication |
public function getUserCertificateInfo($commonName) |
|
|
|
|
|
|
101
|
|
|
{ |
|
102
|
|
|
$stmt = $this->db->prepare( |
|
103
|
|
|
<<< 'SQL' |
|
104
|
|
|
SELECT |
|
105
|
|
|
u.user_id AS user_id, |
|
106
|
|
|
u.is_disabled AS user_is_disabled, |
|
107
|
|
|
c.display_name AS display_name, |
|
108
|
|
|
c.is_disabled AS certificate_is_disabled |
|
109
|
|
|
FROM |
|
110
|
|
|
users u, certificates c |
|
111
|
|
|
WHERE |
|
112
|
|
|
u.id = c.user_id AND |
|
113
|
|
|
c.common_name = :common_name |
|
114
|
|
|
SQL |
|
115
|
|
|
); |
|
116
|
|
|
|
|
117
|
|
|
$stmt->bindValue(':common_name', $commonName, PDO::PARAM_STR); |
|
118
|
|
|
$stmt->execute(); |
|
119
|
|
|
|
|
120
|
|
|
return $stmt->fetch(PDO::FETCH_ASSOC); |
|
121
|
|
|
} |
|
122
|
|
|
|
|
123
|
|
View Code Duplication |
public function getVootToken($userId) |
|
|
|
|
|
|
124
|
|
|
{ |
|
125
|
|
|
$userId = $this->getId($userId); |
|
126
|
|
|
$stmt = $this->db->prepare( |
|
127
|
|
|
<<< 'SQL' |
|
128
|
|
|
SELECT |
|
129
|
|
|
voot_token |
|
130
|
|
|
FROM |
|
131
|
|
|
voot_tokens |
|
132
|
|
|
WHERE |
|
133
|
|
|
user_id = :user_id |
|
134
|
|
|
SQL |
|
135
|
|
|
); |
|
136
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
137
|
|
|
$stmt->execute(); |
|
138
|
|
|
|
|
139
|
|
|
return $stmt->fetchColumn(); |
|
140
|
|
|
} |
|
141
|
|
|
|
|
142
|
|
View Code Duplication |
public function setVootToken($userId, $vootToken) |
|
|
|
|
|
|
143
|
|
|
{ |
|
144
|
|
|
$userId = $this->getId($userId); |
|
145
|
|
|
$stmt = $this->db->prepare( |
|
146
|
|
|
<<< 'SQL' |
|
147
|
|
|
INSERT INTO voot_tokens |
|
148
|
|
|
(user_id, voot_token) |
|
149
|
|
|
VALUES |
|
150
|
|
|
(:user_id, :voot_token) |
|
151
|
|
|
SQL |
|
152
|
|
|
); |
|
153
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
154
|
|
|
$stmt->bindValue(':voot_token', $vootToken, PDO::PARAM_STR); |
|
155
|
|
|
|
|
156
|
|
|
$stmt->execute(); |
|
157
|
|
|
|
|
158
|
|
|
// XXX deal with errors! |
|
159
|
|
|
return 1 === $stmt->rowCount(); |
|
160
|
|
|
} |
|
161
|
|
|
|
|
162
|
|
View Code Duplication |
public function hasVootToken($userId) |
|
|
|
|
|
|
163
|
|
|
{ |
|
164
|
|
|
$userId = $this->getId($userId); |
|
165
|
|
|
$stmt = $this->db->prepare( |
|
166
|
|
|
<<< 'SQL' |
|
167
|
|
|
SELECT |
|
168
|
|
|
COUNT(*) |
|
169
|
|
|
FROM |
|
170
|
|
|
voot_tokens |
|
171
|
|
|
WHERE |
|
172
|
|
|
user_id = :user_id |
|
173
|
|
|
SQL |
|
174
|
|
|
); |
|
175
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
176
|
|
|
$stmt->execute(); |
|
177
|
|
|
|
|
178
|
|
|
return 1 === (int) $stmt->fetchColumn(); |
|
179
|
|
|
} |
|
180
|
|
|
|
|
181
|
|
View Code Duplication |
public function deleteVootToken($userId) |
|
|
|
|
|
|
182
|
|
|
{ |
|
183
|
|
|
$userId = $this->getId($userId); |
|
184
|
|
|
$stmt = $this->db->prepare( |
|
185
|
|
|
<<< 'SQL' |
|
186
|
|
|
DELETE FROM |
|
187
|
|
|
voot_tokens |
|
188
|
|
|
WHERE |
|
189
|
|
|
user_id = :user_id |
|
190
|
|
|
SQL |
|
191
|
|
|
); |
|
192
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
193
|
|
|
|
|
194
|
|
|
$stmt->execute(); |
|
195
|
|
|
// XXX error handling! |
|
196
|
|
|
return 1 === $stmt->rowCount(); |
|
197
|
|
|
} |
|
198
|
|
|
|
|
199
|
|
View Code Duplication |
public function hasTotpSecret($userId) |
|
|
|
|
|
|
200
|
|
|
{ |
|
201
|
|
|
$userId = $this->getId($userId); |
|
202
|
|
|
$stmt = $this->db->prepare( |
|
203
|
|
|
<<< 'SQL' |
|
204
|
|
|
SELECT |
|
205
|
|
|
COUNT(*) |
|
206
|
|
|
FROM |
|
207
|
|
|
totp_secrets |
|
208
|
|
|
WHERE |
|
209
|
|
|
user_id = :user_id |
|
210
|
|
|
SQL |
|
211
|
|
|
); |
|
212
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
213
|
|
|
$stmt->execute(); |
|
214
|
|
|
|
|
215
|
|
|
return 1 === (int) $stmt->fetchColumn(); |
|
216
|
|
|
} |
|
217
|
|
|
|
|
218
|
|
View Code Duplication |
public function getTotpSecret($userId) |
|
|
|
|
|
|
219
|
|
|
{ |
|
220
|
|
|
$userId = $this->getId($userId); |
|
221
|
|
|
$stmt = $this->db->prepare( |
|
222
|
|
|
<<< 'SQL' |
|
223
|
|
|
SELECT |
|
224
|
|
|
totp_secret |
|
225
|
|
|
FROM |
|
226
|
|
|
totp_secrets |
|
227
|
|
|
WHERE |
|
228
|
|
|
user_id = :user_id |
|
229
|
|
|
SQL |
|
230
|
|
|
); |
|
231
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
232
|
|
|
$stmt->execute(); |
|
233
|
|
|
|
|
234
|
|
|
return $stmt->fetchColumn(); |
|
235
|
|
|
} |
|
236
|
|
|
|
|
237
|
|
|
public function setTotpSecret($userId, $totpSecret) |
|
238
|
|
|
{ |
|
239
|
|
|
$userId = $this->getId($userId); |
|
240
|
|
|
$stmt = $this->db->prepare( |
|
241
|
|
|
<<< 'SQL' |
|
242
|
|
|
INSERT INTO totp_secrets |
|
243
|
|
|
(user_id, totp_secret) |
|
244
|
|
|
VALUES |
|
245
|
|
|
(:user_id, :totp_secret) |
|
246
|
|
|
SQL |
|
247
|
|
|
); |
|
248
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
249
|
|
|
$stmt->bindValue(':totp_secret', $totpSecret, PDO::PARAM_STR); |
|
250
|
|
|
|
|
251
|
|
|
try { |
|
252
|
|
|
$stmt->execute(); |
|
253
|
|
|
|
|
254
|
|
|
return true; |
|
255
|
|
|
} catch (PDOException $e) { |
|
256
|
|
|
// unable to add the TOTP secret, probably uniqueness contrains |
|
257
|
|
|
return false; |
|
258
|
|
|
} |
|
259
|
|
|
} |
|
260
|
|
|
|
|
261
|
|
View Code Duplication |
public function deleteTotpSecret($userId) |
|
|
|
|
|
|
262
|
|
|
{ |
|
263
|
|
|
$userId = $this->getId($userId); |
|
264
|
|
|
$stmt = $this->db->prepare( |
|
265
|
|
|
<<< 'SQL' |
|
266
|
|
|
DELETE FROM |
|
267
|
|
|
totp_secrets |
|
268
|
|
|
WHERE |
|
269
|
|
|
user_id = :user_id |
|
270
|
|
|
SQL |
|
271
|
|
|
); |
|
272
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
273
|
|
|
|
|
274
|
|
|
$stmt->execute(); |
|
275
|
|
|
|
|
276
|
|
|
// XXX error handling? |
|
277
|
|
|
return 1 === $stmt->rowCount(); |
|
278
|
|
|
} |
|
279
|
|
|
|
|
280
|
|
View Code Duplication |
public function deleteUser($userId) |
|
|
|
|
|
|
281
|
|
|
{ |
|
282
|
|
|
$userId = $this->getId($userId); |
|
283
|
|
|
$stmt = $this->db->prepare( |
|
284
|
|
|
<<< 'SQL' |
|
285
|
|
|
DELETE FROM |
|
286
|
|
|
users |
|
287
|
|
|
WHERE |
|
288
|
|
|
id = :user_id |
|
289
|
|
|
SQL |
|
290
|
|
|
); |
|
291
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
292
|
|
|
|
|
293
|
|
|
$stmt->execute(); |
|
294
|
|
|
// XXX error handling? |
|
295
|
|
|
return 1 === $stmt->rowCount(); |
|
296
|
|
|
} |
|
297
|
|
|
|
|
298
|
|
|
public function addCertificate($userId, $commonName, $displayName, DateTime $validFrom, DateTime $validTo) |
|
299
|
|
|
{ |
|
300
|
|
|
$userId = $this->getId($userId); |
|
301
|
|
|
$stmt = $this->db->prepare( |
|
302
|
|
|
<<< 'SQL' |
|
303
|
|
|
INSERT INTO certificates |
|
304
|
|
|
(common_name, user_id, display_name, valid_from, valid_to) |
|
305
|
|
|
VALUES |
|
306
|
|
|
(:common_name, :user_id, :display_name, :valid_from, :valid_to) |
|
307
|
|
|
SQL |
|
308
|
|
|
); |
|
309
|
|
|
$stmt->bindValue(':common_name', $commonName, PDO::PARAM_STR); |
|
310
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
311
|
|
|
$stmt->bindValue(':display_name', $displayName, PDO::PARAM_STR); |
|
312
|
|
|
$stmt->bindValue(':valid_from', $validFrom->format('Y-m-d H:i:s'), PDO::PARAM_STR); |
|
313
|
|
|
$stmt->bindValue(':valid_to', $validTo->format('Y-m-d H:i:s'), PDO::PARAM_STR); |
|
314
|
|
|
|
|
315
|
|
|
$stmt->execute(); |
|
316
|
|
|
// XXX |
|
317
|
|
|
return 1 === $stmt->rowCount(); |
|
318
|
|
|
} |
|
319
|
|
|
|
|
320
|
|
|
public function getCertificates($userId) |
|
321
|
|
|
{ |
|
322
|
|
|
$userId = $this->getId($userId); |
|
323
|
|
|
$stmt = $this->db->prepare( |
|
324
|
|
|
<<< 'SQL' |
|
325
|
|
|
SELECT |
|
326
|
|
|
common_name, |
|
327
|
|
|
display_name, |
|
328
|
|
|
valid_from, |
|
329
|
|
|
valid_to, |
|
330
|
|
|
is_disabled |
|
331
|
|
|
FROM |
|
332
|
|
|
certificates |
|
333
|
|
|
WHERE |
|
334
|
|
|
user_id = :user_id |
|
335
|
|
|
SQL |
|
336
|
|
|
); |
|
337
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
338
|
|
|
$stmt->execute(); |
|
339
|
|
|
|
|
340
|
|
|
$certificateList = []; |
|
341
|
|
|
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $result) { |
|
342
|
|
|
$result['is_disabled'] = (bool) $result['is_disabled']; |
|
343
|
|
|
$certificateList[] = $result; |
|
344
|
|
|
} |
|
345
|
|
|
|
|
346
|
|
|
return $certificateList; |
|
347
|
|
|
} |
|
348
|
|
|
|
|
349
|
|
View Code Duplication |
public function disableCertificate($commonName) |
|
|
|
|
|
|
350
|
|
|
{ |
|
351
|
|
|
$stmt = $this->db->prepare( |
|
352
|
|
|
<<< 'SQL' |
|
353
|
|
|
UPDATE |
|
354
|
|
|
certificates |
|
355
|
|
|
SET |
|
356
|
|
|
is_disabled = 1 |
|
357
|
|
|
WHERE |
|
358
|
|
|
common_name = :common_name |
|
359
|
|
|
SQL |
|
360
|
|
|
); |
|
361
|
|
|
$stmt->bindValue(':common_name', $commonName, PDO::PARAM_STR); |
|
362
|
|
|
|
|
363
|
|
|
$stmt->execute(); |
|
364
|
|
|
// XXX |
|
365
|
|
|
return 1 === $stmt->rowCount(); |
|
366
|
|
|
} |
|
367
|
|
|
|
|
368
|
|
View Code Duplication |
public function deleteCertificate($commonName) |
|
|
|
|
|
|
369
|
|
|
{ |
|
370
|
|
|
$stmt = $this->db->prepare( |
|
371
|
|
|
<<< 'SQL' |
|
372
|
|
|
DELETE FROM |
|
373
|
|
|
certificates |
|
374
|
|
|
WHERE |
|
375
|
|
|
common_name = :common_name |
|
376
|
|
|
SQL |
|
377
|
|
|
); |
|
378
|
|
|
$stmt->bindValue(':common_name', $commonName, PDO::PARAM_STR); |
|
379
|
|
|
|
|
380
|
|
|
$stmt->execute(); |
|
381
|
|
|
// XXX |
|
382
|
|
|
return 1 === $stmt->rowCount(); |
|
383
|
|
|
} |
|
384
|
|
|
|
|
385
|
|
View Code Duplication |
public function enableCertificate($commonName) |
|
|
|
|
|
|
386
|
|
|
{ |
|
387
|
|
|
$stmt = $this->db->prepare( |
|
388
|
|
|
<<< 'SQL' |
|
389
|
|
|
UPDATE |
|
390
|
|
|
certificates |
|
391
|
|
|
SET |
|
392
|
|
|
is_disabled = 0 |
|
393
|
|
|
WHERE |
|
394
|
|
|
common_name = :common_name |
|
395
|
|
|
SQL |
|
396
|
|
|
); |
|
397
|
|
|
$stmt->bindValue(':common_name', $commonName, PDO::PARAM_STR); |
|
398
|
|
|
|
|
399
|
|
|
$stmt->execute(); |
|
400
|
|
|
// XXX |
|
401
|
|
|
return 1 === $stmt->rowCount(); |
|
402
|
|
|
} |
|
403
|
|
|
|
|
404
|
|
View Code Duplication |
public function disableUser($userId) |
|
|
|
|
|
|
405
|
|
|
{ |
|
406
|
|
|
$userId = $this->getId($userId); |
|
407
|
|
|
$stmt = $this->db->prepare( |
|
408
|
|
|
<<< 'SQL' |
|
409
|
|
|
UPDATE |
|
410
|
|
|
users |
|
411
|
|
|
SET |
|
412
|
|
|
is_disabled = 1 |
|
413
|
|
|
WHERE |
|
414
|
|
|
id = :user_id |
|
415
|
|
|
SQL |
|
416
|
|
|
); |
|
417
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
418
|
|
|
|
|
419
|
|
|
$stmt->execute(); |
|
420
|
|
|
|
|
421
|
|
|
// XXX it seems on update the rowCount is always 1, even if nothing was |
|
422
|
|
|
// modified? |
|
423
|
|
|
return 1 === $stmt->rowCount(); |
|
424
|
|
|
} |
|
425
|
|
|
|
|
426
|
|
View Code Duplication |
public function enableUser($userId) |
|
|
|
|
|
|
427
|
|
|
{ |
|
428
|
|
|
$userId = $this->getId($userId); |
|
429
|
|
|
$stmt = $this->db->prepare( |
|
430
|
|
|
<<< 'SQL' |
|
431
|
|
|
UPDATE |
|
432
|
|
|
users |
|
433
|
|
|
SET |
|
434
|
|
|
is_disabled = 0 |
|
435
|
|
|
WHERE |
|
436
|
|
|
id = :user_id |
|
437
|
|
|
SQL |
|
438
|
|
|
); |
|
439
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
440
|
|
|
|
|
441
|
|
|
$stmt->execute(); |
|
442
|
|
|
|
|
443
|
|
|
// XXX it seems on update the rowCount is always 1, even if nothing was |
|
444
|
|
|
// modified? |
|
445
|
|
|
return 1 === $stmt->rowCount(); |
|
446
|
|
|
} |
|
447
|
|
|
|
|
448
|
|
View Code Duplication |
public function isDisabledUser($userId) |
|
|
|
|
|
|
449
|
|
|
{ |
|
450
|
|
|
$userId = $this->getId($userId); |
|
451
|
|
|
$stmt = $this->db->prepare( |
|
452
|
|
|
<<< 'SQL' |
|
453
|
|
|
SELECT |
|
454
|
|
|
COUNT(*) |
|
455
|
|
|
FROM |
|
456
|
|
|
users |
|
457
|
|
|
WHERE |
|
458
|
|
|
id = :user_id |
|
459
|
|
|
AND |
|
460
|
|
|
is_disabled = 1 |
|
461
|
|
|
SQL |
|
462
|
|
|
); |
|
463
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
464
|
|
|
$stmt->execute(); |
|
465
|
|
|
|
|
466
|
|
|
return 1 === (int) $stmt->fetchColumn(); |
|
467
|
|
|
} |
|
468
|
|
|
|
|
469
|
|
|
public function getAllLogEntries() |
|
470
|
|
|
{ |
|
471
|
|
|
$stmt = $this->db->prepare( |
|
472
|
|
|
<<< 'SQL' |
|
473
|
|
|
SELECT |
|
474
|
|
|
user_id, |
|
475
|
|
|
common_name, |
|
476
|
|
|
connected_at, |
|
477
|
|
|
disconnected_at, |
|
478
|
|
|
bytes_transferred |
|
479
|
|
|
FROM |
|
480
|
|
|
connection_log |
|
481
|
|
|
WHERE |
|
482
|
|
|
disconnected_at IS NOT NULL |
|
483
|
|
|
ORDER BY |
|
484
|
|
|
connected_at |
|
485
|
|
|
SQL |
|
486
|
|
|
); |
|
487
|
|
|
|
|
488
|
|
|
$stmt->execute(); |
|
489
|
|
|
|
|
490
|
|
|
return $stmt->fetchAll(PDO::FETCH_ASSOC); |
|
491
|
|
|
} |
|
492
|
|
|
|
|
493
|
|
View Code Duplication |
public function clientConnect($profileId, $commonName, $ip4, $ip6, DateTime $connectedAt) |
|
|
|
|
|
|
494
|
|
|
{ |
|
495
|
|
|
// this query is so complex, because we want to store the user_id in the |
|
496
|
|
|
// log as well, not just the common_name... the user may delete the |
|
497
|
|
|
// certificate, or the user account may be deleted... |
|
498
|
|
|
$stmt = $this->db->prepare( |
|
499
|
|
|
<<< 'SQL' |
|
500
|
|
|
INSERT INTO connection_log |
|
501
|
|
|
( |
|
502
|
|
|
user_id, |
|
503
|
|
|
profile_id, |
|
504
|
|
|
common_name, |
|
505
|
|
|
ip4, |
|
506
|
|
|
ip6, |
|
507
|
|
|
connected_at |
|
508
|
|
|
) |
|
509
|
|
|
VALUES |
|
510
|
|
|
( |
|
511
|
|
|
( |
|
512
|
|
|
SELECT |
|
513
|
|
|
u.user_id |
|
514
|
|
|
FROM |
|
515
|
|
|
users u, certificates c |
|
516
|
|
|
WHERE |
|
517
|
|
|
u.id = c.user_id |
|
518
|
|
|
AND |
|
519
|
|
|
c.common_name = :common_name |
|
520
|
|
|
), |
|
521
|
|
|
:profile_id, |
|
522
|
|
|
:common_name, |
|
523
|
|
|
:ip4, |
|
524
|
|
|
:ip6, |
|
525
|
|
|
:connected_at |
|
526
|
|
|
) |
|
527
|
|
|
SQL |
|
528
|
|
|
); |
|
529
|
|
|
|
|
530
|
|
|
$stmt->bindValue(':profile_id', $profileId, PDO::PARAM_STR); |
|
531
|
|
|
$stmt->bindValue(':common_name', $commonName, PDO::PARAM_STR); |
|
532
|
|
|
$stmt->bindValue(':ip4', $ip4, PDO::PARAM_STR); |
|
533
|
|
|
$stmt->bindValue(':ip6', $ip6, PDO::PARAM_STR); |
|
534
|
|
|
$stmt->bindValue(':connected_at', $connectedAt->format('Y-m-d H:i:s'), PDO::PARAM_STR); |
|
535
|
|
|
|
|
536
|
|
|
$stmt->execute(); |
|
537
|
|
|
// XXX |
|
538
|
|
|
return 1 === $stmt->rowCount(); |
|
539
|
|
|
} |
|
540
|
|
|
|
|
541
|
|
|
public function clientDisconnect($profileId, $commonName, $ip4, $ip6, DateTime $connectedAt, DateTime $disconnectedAt, $bytesTransferred) |
|
542
|
|
|
{ |
|
543
|
|
|
$stmt = $this->db->prepare( |
|
544
|
|
|
<<< 'SQL' |
|
545
|
|
|
UPDATE |
|
546
|
|
|
connection_log |
|
547
|
|
|
SET |
|
548
|
|
|
disconnected_at = :disconnected_at, |
|
549
|
|
|
bytes_transferred = :bytes_transferred |
|
550
|
|
|
WHERE |
|
551
|
|
|
profile_id = :profile_id |
|
552
|
|
|
AND |
|
553
|
|
|
common_name = :common_name |
|
554
|
|
|
AND |
|
555
|
|
|
ip4 = :ip4 |
|
556
|
|
|
AND |
|
557
|
|
|
ip6 = :ip6 |
|
558
|
|
|
AND |
|
559
|
|
|
connected_at = :connected_at |
|
560
|
|
|
SQL |
|
561
|
|
|
); |
|
562
|
|
|
|
|
563
|
|
|
$stmt->bindValue(':profile_id', $profileId, PDO::PARAM_STR); |
|
564
|
|
|
$stmt->bindValue(':common_name', $commonName, PDO::PARAM_STR); |
|
565
|
|
|
$stmt->bindValue(':ip4', $ip4, PDO::PARAM_STR); |
|
566
|
|
|
$stmt->bindValue(':ip6', $ip6, PDO::PARAM_STR); |
|
567
|
|
|
$stmt->bindValue(':connected_at', $connectedAt->format('Y-m-d H:i:s'), PDO::PARAM_STR); |
|
568
|
|
|
$stmt->bindValue(':disconnected_at', $disconnectedAt->format('Y-m-d H:i:s'), PDO::PARAM_STR); |
|
569
|
|
|
$stmt->bindValue(':bytes_transferred', $bytesTransferred, PDO::PARAM_INT); |
|
570
|
|
|
|
|
571
|
|
|
$stmt->execute(); |
|
572
|
|
|
|
|
573
|
|
|
// XXX |
|
574
|
|
|
return 1 === $stmt->rowCount(); |
|
575
|
|
|
} |
|
576
|
|
|
|
|
577
|
|
View Code Duplication |
public function getLogEntry($dateTimeUnix, $ipAddress) |
|
|
|
|
|
|
578
|
|
|
{ |
|
579
|
|
|
$stmt = $this->db->prepare( |
|
580
|
|
|
<<< 'SQL' |
|
581
|
|
|
SELECT |
|
582
|
|
|
user_id, |
|
583
|
|
|
profile_id, |
|
584
|
|
|
common_name, |
|
585
|
|
|
ip4, |
|
586
|
|
|
ip6, |
|
587
|
|
|
connected_at, |
|
588
|
|
|
disconnected_at |
|
589
|
|
|
FROM |
|
590
|
|
|
connection_log |
|
591
|
|
|
WHERE |
|
592
|
|
|
(ip4 = :ip_address OR ip6 = :ip_address) |
|
593
|
|
|
AND |
|
594
|
|
|
connected_at < :date_time_unix |
|
595
|
|
|
AND |
|
596
|
|
|
(disconnected_at > :date_time_unix OR disconnected_at IS NULL) |
|
597
|
|
|
SQL |
|
598
|
|
|
); |
|
599
|
|
|
$stmt->bindValue(':ip_address', $ipAddress, PDO::PARAM_STR); |
|
600
|
|
|
$stmt->bindValue(':date_time_unix', $dateTimeUnix, PDO::PARAM_STR); |
|
601
|
|
|
$stmt->execute(); |
|
602
|
|
|
|
|
603
|
|
|
return $stmt->fetchAll(PDO::FETCH_ASSOC); |
|
604
|
|
|
} |
|
605
|
|
|
|
|
606
|
|
View Code Duplication |
public function getTotpAttemptCount($userId) |
|
|
|
|
|
|
607
|
|
|
{ |
|
608
|
|
|
$userId = $this->getId($userId); |
|
609
|
|
|
$stmt = $this->db->prepare( |
|
610
|
|
|
<<< 'SQL' |
|
611
|
|
|
SELECT |
|
612
|
|
|
COUNT(*) |
|
613
|
|
|
FROM |
|
614
|
|
|
totp_log |
|
615
|
|
|
WHERE user_id = :user_id |
|
616
|
|
|
SQL |
|
617
|
|
|
); |
|
618
|
|
|
|
|
619
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
620
|
|
|
$stmt->execute(); |
|
621
|
|
|
|
|
622
|
|
|
return (int) $stmt->fetchColumn(); |
|
623
|
|
|
} |
|
624
|
|
|
|
|
625
|
|
|
public function recordTotpKey($userId, $totpKey, DateTime $dateTime) |
|
626
|
|
|
{ |
|
627
|
|
|
$userId = $this->getId($userId); |
|
628
|
|
|
$stmt = $this->db->prepare( |
|
629
|
|
|
<<< 'SQL' |
|
630
|
|
|
INSERT INTO totp_log |
|
631
|
|
|
(user_id, totp_key, date_time) |
|
632
|
|
|
VALUES |
|
633
|
|
|
(:user_id, :totp_key, :date_time) |
|
634
|
|
|
SQL |
|
635
|
|
|
); |
|
636
|
|
|
|
|
637
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
638
|
|
|
$stmt->bindValue(':totp_key', $totpKey, PDO::PARAM_STR); |
|
639
|
|
|
$stmt->bindValue(':date_time', $dateTime->format('Y-m-d H:i:s'), PDO::PARAM_STR); |
|
640
|
|
|
|
|
641
|
|
|
try { |
|
642
|
|
|
$stmt->execute(); |
|
643
|
|
|
} catch (PDOException $e) { |
|
644
|
|
|
// unable to record the TOTP, probably uniqueness contrains |
|
645
|
|
|
return false; |
|
646
|
|
|
} |
|
647
|
|
|
|
|
648
|
|
|
return true; |
|
649
|
|
|
} |
|
650
|
|
|
|
|
651
|
|
View Code Duplication |
public function cleanConnectionLog(DateTime $dateTime) |
|
|
|
|
|
|
652
|
|
|
{ |
|
653
|
|
|
$stmt = $this->db->prepare( |
|
654
|
|
|
<<< 'SQL' |
|
655
|
|
|
DELETE FROM |
|
656
|
|
|
connection_log |
|
657
|
|
|
WHERE |
|
658
|
|
|
connected_at < :date_time |
|
659
|
|
|
AND |
|
660
|
|
|
disconnected_at IS NOT NULL |
|
661
|
|
|
SQL |
|
662
|
|
|
); |
|
663
|
|
|
|
|
664
|
|
|
$stmt->bindValue(':date_time', $dateTime->format('Y-m-d H:i:s'), PDO::PARAM_STR); |
|
665
|
|
|
|
|
666
|
|
|
return $stmt->execute(); |
|
667
|
|
|
} |
|
668
|
|
|
|
|
669
|
|
View Code Duplication |
public function cleanUserMessages(DateTime $dateTime) |
|
|
|
|
|
|
670
|
|
|
{ |
|
671
|
|
|
$stmt = $this->db->prepare( |
|
672
|
|
|
<<< 'SQL' |
|
673
|
|
|
DELETE FROM |
|
674
|
|
|
user_messages |
|
675
|
|
|
WHERE |
|
676
|
|
|
date_time < :date_time |
|
677
|
|
|
SQL |
|
678
|
|
|
); |
|
679
|
|
|
|
|
680
|
|
|
$stmt->bindValue(':date_time', $dateTime->format('Y-m-d H:i:s'), PDO::PARAM_STR); |
|
681
|
|
|
|
|
682
|
|
|
return $stmt->execute(); |
|
683
|
|
|
} |
|
684
|
|
|
|
|
685
|
|
View Code Duplication |
public function cleanTotpLog(DateTime $dateTime) |
|
|
|
|
|
|
686
|
|
|
{ |
|
687
|
|
|
$stmt = $this->db->prepare( |
|
688
|
|
|
<<< 'SQL' |
|
689
|
|
|
DELETE FROM |
|
690
|
|
|
totp_log |
|
691
|
|
|
WHERE |
|
692
|
|
|
date_time < :date_time |
|
693
|
|
|
SQL |
|
694
|
|
|
); |
|
695
|
|
|
|
|
696
|
|
|
$stmt->bindValue(':date_time', $dateTime->format('Y-m-d H:i:s'), PDO::PARAM_STR); |
|
697
|
|
|
|
|
698
|
|
|
return $stmt->execute(); |
|
699
|
|
|
} |
|
700
|
|
|
|
|
701
|
|
View Code Duplication |
public function systemMessages($type) |
|
|
|
|
|
|
702
|
|
|
{ |
|
703
|
|
|
$stmt = $this->db->prepare( |
|
704
|
|
|
<<< 'SQL' |
|
705
|
|
|
SELECT |
|
706
|
|
|
id, message, date_time |
|
707
|
|
|
FROM |
|
708
|
|
|
system_messages |
|
709
|
|
|
WHERE |
|
710
|
|
|
type = :type |
|
711
|
|
|
SQL |
|
712
|
|
|
); |
|
713
|
|
|
|
|
714
|
|
|
$stmt->bindValue(':type', $type, PDO::PARAM_STR); |
|
715
|
|
|
$stmt->execute(); |
|
716
|
|
|
|
|
717
|
|
|
return $stmt->fetchAll(PDO::FETCH_ASSOC); |
|
718
|
|
|
} |
|
719
|
|
|
|
|
720
|
|
|
public function addSystemMessage($type, $message, DateTime $dateTime) |
|
721
|
|
|
{ |
|
722
|
|
|
$stmt = $this->db->prepare( |
|
723
|
|
|
<<< 'SQL' |
|
724
|
|
|
INSERT INTO system_messages |
|
725
|
|
|
(type, message, date_time) |
|
726
|
|
|
VALUES |
|
727
|
|
|
(:type, :message, :date_time) |
|
728
|
|
|
SQL |
|
729
|
|
|
); |
|
730
|
|
|
|
|
731
|
|
|
$stmt->bindValue(':type', $type, PDO::PARAM_STR); |
|
732
|
|
|
$stmt->bindValue(':message', $message, PDO::PARAM_STR); |
|
733
|
|
|
$stmt->bindValue(':date_time', $dateTime->format('Y-m-d H:i:s'), PDO::PARAM_STR); |
|
734
|
|
|
$stmt->execute(); |
|
735
|
|
|
|
|
736
|
|
|
return 1 === $stmt->rowCount(); |
|
737
|
|
|
} |
|
738
|
|
|
|
|
739
|
|
View Code Duplication |
public function deleteSystemMessage($messageId) |
|
|
|
|
|
|
740
|
|
|
{ |
|
741
|
|
|
$stmt = $this->db->prepare( |
|
742
|
|
|
<<< 'SQL' |
|
743
|
|
|
DELETE FROM |
|
744
|
|
|
system_messages |
|
745
|
|
|
WHERE id = :message_id |
|
746
|
|
|
SQL |
|
747
|
|
|
); |
|
748
|
|
|
|
|
749
|
|
|
$stmt->bindValue(':message_id', $messageId, PDO::PARAM_INT); |
|
750
|
|
|
$stmt->execute(); |
|
751
|
|
|
|
|
752
|
|
|
return 1 === $stmt->rowCount(); |
|
753
|
|
|
} |
|
754
|
|
|
|
|
755
|
|
View Code Duplication |
public function userMessages($userId) |
|
|
|
|
|
|
756
|
|
|
{ |
|
757
|
|
|
$userId = $this->getId($userId); |
|
758
|
|
|
|
|
759
|
|
|
$stmt = $this->db->prepare( |
|
760
|
|
|
<<< 'SQL' |
|
761
|
|
|
SELECT |
|
762
|
|
|
id, type, message, date_time |
|
763
|
|
|
FROM |
|
764
|
|
|
user_messages |
|
765
|
|
|
WHERE |
|
766
|
|
|
user_id = :user_id |
|
767
|
|
|
ORDER BY |
|
768
|
|
|
date_time DESC |
|
769
|
|
|
SQL |
|
770
|
|
|
); |
|
771
|
|
|
|
|
772
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
773
|
|
|
$stmt->execute(); |
|
774
|
|
|
|
|
775
|
|
|
return $stmt->fetchAll(PDO::FETCH_ASSOC); |
|
776
|
|
|
} |
|
777
|
|
|
|
|
778
|
|
View Code Duplication |
public function addUserMessage($userId, $type, $message, DateTime $dateTime) |
|
|
|
|
|
|
779
|
|
|
{ |
|
780
|
|
|
$userId = $this->getId($userId); |
|
781
|
|
|
|
|
782
|
|
|
$stmt = $this->db->prepare( |
|
783
|
|
|
<<< 'SQL' |
|
784
|
|
|
INSERT INTO user_messages |
|
785
|
|
|
(user_id, type, message, date_time) |
|
786
|
|
|
VALUES |
|
787
|
|
|
(:user_id, :type, :message, :date_time) |
|
788
|
|
|
SQL |
|
789
|
|
|
); |
|
790
|
|
|
|
|
791
|
|
|
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); |
|
792
|
|
|
$stmt->bindValue(':type', $type, PDO::PARAM_STR); |
|
793
|
|
|
$stmt->bindValue(':message', $message, PDO::PARAM_STR); |
|
794
|
|
|
$stmt->bindValue(':date_time', $dateTime->format('Y-m-d H:i:s'), PDO::PARAM_STR); |
|
795
|
|
|
$stmt->execute(); |
|
796
|
|
|
|
|
797
|
|
|
return 1 === $stmt->rowCount(); |
|
798
|
|
|
} |
|
799
|
|
|
|
|
800
|
|
|
public function init() |
|
801
|
|
|
{ |
|
802
|
|
|
$queryList = []; |
|
803
|
|
|
$queryList[] = |
|
804
|
|
|
<<< 'SQL' |
|
805
|
|
|
CREATE TABLE IF NOT EXISTS users ( |
|
806
|
|
|
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, |
|
807
|
|
|
user_id VARCHAR(255) NOT NULL UNIQUE, |
|
808
|
|
|
is_disabled BOOLEAN DEFAULT 0 NOT NULL |
|
809
|
|
|
) |
|
810
|
|
|
SQL; |
|
811
|
|
|
|
|
812
|
|
|
$queryList[] = |
|
813
|
|
|
<<< 'SQL' |
|
814
|
|
|
CREATE TABLE IF NOT EXISTS voot_tokens ( |
|
815
|
|
|
voot_token VARCHAR(255) UNIQUE NOT NULL, |
|
816
|
|
|
user_id INTEGER UNIQUE NOT NULL REFERENCES users(id) ON DELETE CASCADE |
|
817
|
|
|
) |
|
818
|
|
|
SQL; |
|
819
|
|
|
|
|
820
|
|
|
$queryList[] = |
|
821
|
|
|
<<< 'SQL' |
|
822
|
|
|
CREATE TABLE IF NOT EXISTS totp_secrets ( |
|
823
|
|
|
totp_secret VARCHAR(255) UNIQUE NOT NULL, |
|
824
|
|
|
user_id INTEGER UNIQUE NOT NULL REFERENCES users(id) ON DELETE CASCADE |
|
825
|
|
|
) |
|
826
|
|
|
SQL; |
|
827
|
|
|
|
|
828
|
|
|
$queryList[] = |
|
829
|
|
|
<<< 'SQL' |
|
830
|
|
|
CREATE TABLE IF NOT EXISTS certificates ( |
|
831
|
|
|
common_name VARCHAR(255) UNIQUE NOT NULL, |
|
832
|
|
|
display_name VARCHAR(255) NOT NULL, |
|
833
|
|
|
valid_from DATETIME NOT NULL, |
|
834
|
|
|
valid_to DATETIME NOT NULL, |
|
835
|
|
|
is_disabled BOOLEAN DEFAULT 0, |
|
836
|
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE |
|
837
|
|
|
) |
|
838
|
|
|
SQL; |
|
839
|
|
|
|
|
840
|
|
|
$queryList[] = |
|
841
|
|
|
<<< 'SQL' |
|
842
|
|
|
CREATE TABLE IF NOT EXISTS totp_log ( |
|
843
|
|
|
totp_key VARCHAR(255) NOT NULL, |
|
844
|
|
|
date_time DATETIME NOT NULL, |
|
845
|
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, |
|
846
|
|
|
UNIQUE (user_id, totp_key) |
|
847
|
|
|
) |
|
848
|
|
|
SQL; |
|
849
|
|
|
|
|
850
|
|
|
$queryList[] = |
|
851
|
|
|
<<< 'SQL' |
|
852
|
|
|
CREATE TABLE IF NOT EXISTS connection_log ( |
|
853
|
|
|
user_id VARCHAR(255) NOT NULL, |
|
854
|
|
|
common_name VARCHAR(255) NOT NULL, |
|
855
|
|
|
profile_id VARCHAR(255) NOT NULL, |
|
856
|
|
|
ip4 VARCHAR(255) NOT NULL, |
|
857
|
|
|
ip6 VARCHAR(255) NOT NULL, |
|
858
|
|
|
connected_at DATETIME NOT NULL, |
|
859
|
|
|
disconnected_at DATETIME DEFAULT NULL, |
|
860
|
|
|
bytes_transferred INTEGER DEFAULT NULL |
|
861
|
|
|
) |
|
862
|
|
|
SQL; |
|
863
|
|
|
|
|
864
|
|
|
$queryList[] = |
|
865
|
|
|
<<< 'SQL' |
|
866
|
|
|
CREATE TABLE IF NOT EXISTS system_messages ( |
|
867
|
|
|
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, |
|
868
|
|
|
type VARCHAR(255) NOT NULL DEFAULT "notification", |
|
869
|
|
|
message TINYTEXT NOT NULL, |
|
870
|
|
|
date_time DATETIME NOT NULL |
|
871
|
|
|
) |
|
872
|
|
|
SQL; |
|
873
|
|
|
|
|
874
|
|
|
$queryList[] = |
|
875
|
|
|
<<< 'SQL' |
|
876
|
|
|
CREATE TABLE IF NOT EXISTS user_messages ( |
|
877
|
|
|
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, |
|
878
|
|
|
type VARCHAR(255) NOT NULL DEFAULT "notification", |
|
879
|
|
|
message TINYTEXT NOT NULL, |
|
880
|
|
|
date_time DATETIME NOT NULL, |
|
881
|
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE |
|
882
|
|
|
) |
|
883
|
|
|
SQL; |
|
884
|
|
|
|
|
885
|
|
|
foreach ($queryList as $query) { |
|
886
|
|
|
if ('sqlite' === $this->db->getAttribute(PDO::ATTR_DRIVER_NAME)) { |
|
887
|
|
|
$query = str_replace('AUTO_INCREMENT', 'AUTOINCREMENT', $query); |
|
888
|
|
|
} |
|
889
|
|
|
$this->db->query($query); |
|
890
|
|
|
} |
|
891
|
|
|
} |
|
892
|
|
|
|
|
893
|
|
|
public function drop() |
|
894
|
|
|
{ |
|
895
|
|
|
$tableList = [ |
|
896
|
|
|
'users', |
|
897
|
|
|
'voot_tokens', |
|
898
|
|
|
'totp_secrets', |
|
899
|
|
|
'certificates', |
|
900
|
|
|
'totp_log', |
|
901
|
|
|
'connection_log', |
|
902
|
|
|
'system_messages', |
|
903
|
|
|
'user_messages', |
|
904
|
|
|
]; |
|
905
|
|
|
|
|
906
|
|
|
foreach ($tableList as $table) { |
|
907
|
|
|
$this->db->query(sprintf('DROP TABLE IF EXISTS %s', $table)); |
|
908
|
|
|
} |
|
909
|
|
|
} |
|
910
|
|
|
} |
|
911
|
|
|
|
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.