1
|
|
|
<?php |
2
|
|
|
namespace App\Controller; |
3
|
|
|
|
4
|
|
|
use App\Event\Badges; |
5
|
|
|
use App\Event\Logs; |
6
|
|
|
use App\Event\Notifications; |
7
|
|
|
use App\Event\Statistics; |
8
|
|
|
use App\Event\Users; |
9
|
|
|
use App\Utility\Users as UsersUtility; |
10
|
|
|
use BrowscapPHP\Browscap; |
11
|
|
|
use Cake\Auth\DefaultPasswordHasher; |
12
|
|
|
use Cake\Core\Configure; |
13
|
|
|
use Cake\Event\Event; |
14
|
|
|
use Cake\I18n\Time; |
15
|
|
|
use Cake\Mailer\MailerAwareTrait; |
16
|
|
|
use Cake\Utility\Security; |
17
|
|
|
use RobThree\Auth\TwoFactorAuth; |
18
|
|
|
|
19
|
|
|
class UsersController extends AppController |
20
|
|
|
{ |
21
|
|
|
use MailerAwareTrait; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Initialize handle. |
25
|
|
|
* |
26
|
|
|
* @return void |
27
|
|
|
*/ |
28
|
|
|
public function initialize() |
29
|
|
|
{ |
30
|
|
|
parent::initialize(); |
31
|
|
|
|
32
|
|
|
$action = $this->request->action; |
|
|
|
|
33
|
|
|
|
34
|
|
|
if ($action === 'login' || $action === 'forgotPassword') { |
35
|
|
|
$this->loadComponent('Recaptcha.Recaptcha'); |
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
if ($action === 'login') { |
39
|
|
|
$this->loadComponent('TwoFactorAuth'); |
40
|
|
|
} |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* BeforeFilter handle. |
45
|
|
|
* |
46
|
|
|
* @param Event $event The beforeFilter event that was fired. |
47
|
|
|
* |
48
|
|
|
* @return void |
49
|
|
|
*/ |
50
|
|
|
public function beforeFilter(Event $event) |
51
|
|
|
{ |
52
|
|
|
parent::beforeFilter($event); |
53
|
|
|
|
54
|
|
|
$this->Auth->allow(['index', 'tfa', 'logout', 'profile', 'forgotPassword', 'resetPassword']); |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Display all Users. |
59
|
|
|
* |
60
|
|
|
* @return void |
61
|
|
|
*/ |
62
|
|
View Code Duplication |
public function index() |
|
|
|
|
63
|
|
|
{ |
64
|
|
|
$this->paginate = [ |
65
|
|
|
'maxLimit' => Configure::read('User.user_per_page') |
66
|
|
|
]; |
67
|
|
|
$users = $this->Users |
|
|
|
|
68
|
|
|
->find() |
69
|
|
|
->contain([ |
70
|
|
|
'Groups' |
71
|
|
|
]) |
72
|
|
|
->order([ |
73
|
|
|
'Users.created' => 'desc' |
74
|
|
|
]); |
75
|
|
|
|
76
|
|
|
$users = $this->paginate($users); |
77
|
|
|
|
78
|
|
|
$this->set(compact('users')); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Login and register page. |
83
|
|
|
* |
84
|
|
|
* @return \Cake\Network\Response|void |
85
|
|
|
*/ |
86
|
|
|
public function login() |
87
|
|
|
{ |
88
|
|
|
if ($this->request->is('post')) { |
89
|
|
|
$method = ($this->request->data['method']) ? $this->request->data['method'] : false; |
90
|
|
|
|
91
|
|
|
switch ($method) { |
92
|
|
|
case "login": |
93
|
|
|
if (Configure::read('User.Login.enabled') === false) { |
94
|
|
|
$userRegister = $userRegister = $this->Users->newEntity($this->request->data); |
|
|
|
|
95
|
|
|
|
96
|
|
|
break; |
97
|
|
|
} |
98
|
|
|
$userLogin = $this->Auth->identify(); |
99
|
|
|
|
100
|
|
|
if ($userLogin) { |
101
|
|
|
if ($userLogin['is_deleted'] == true) { |
102
|
|
|
$this->Flash->error(__("This account has been deleted.")); |
103
|
|
|
|
104
|
|
|
$userRegister = $this->Users->newEntity($this->request->data); |
|
|
|
|
105
|
|
|
|
106
|
|
|
break; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
//Check the 2FA if the user has enabled it. |
110
|
|
|
if ($userLogin['two_factor_auth_enabled'] == true && $this->TwoFactorAuth->isAuthorized($userLogin['id']) === false) { |
|
|
|
|
111
|
|
|
//Write the cookie |
112
|
|
|
$cookie = base64_encode(Security::encrypt($userLogin['id'], Configure::read('Security.key'))); |
113
|
|
|
$this->Cookie->configKey('CookieTfa', [ |
114
|
|
|
'expires' => '+1 hour', |
115
|
|
|
'httpOnly' => true |
116
|
|
|
]); |
117
|
|
|
$this->Cookie->write('CookieTfa', $cookie); |
118
|
|
|
|
119
|
|
|
return $this->redirect(['action' => 'tfa']); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
$this->_handleLogin($userLogin); |
|
|
|
|
123
|
|
|
|
124
|
|
|
$this->Auth->setUser($userLogin); |
|
|
|
|
125
|
|
|
|
126
|
|
|
$user = $this->Users->newEntity($userLogin, ['accessibleFields' => ['id' => true]]); |
|
|
|
|
127
|
|
|
$user->isNew(false); |
128
|
|
|
|
129
|
|
|
$user->last_login = new Time(); |
130
|
|
|
$user->last_login_ip = $this->request->clientIp(); |
131
|
|
|
|
132
|
|
|
$this->Users->save($user); |
|
|
|
|
133
|
|
|
|
134
|
|
|
//Cookies. |
135
|
|
|
$this->Cookie->configKey('CookieAuth', [ |
136
|
|
|
'expires' => '+1 year', |
137
|
|
|
'httpOnly' => true |
138
|
|
|
]); |
139
|
|
|
$this->Cookie->write('CookieAuth', [ |
140
|
|
|
'username' => $this->request->data('username'), |
141
|
|
|
'password' => $this->request->data('password') |
142
|
|
|
]); |
143
|
|
|
|
144
|
|
|
//Badge Event. |
145
|
|
|
$this->eventManager()->attach(new Badges($this)); |
|
|
|
|
146
|
|
|
|
147
|
|
|
$user = new Event('Model.Users.register', $this, [ |
148
|
|
|
'user' => $user |
149
|
|
|
]); |
150
|
|
|
$this->eventManager()->dispatch($user); |
151
|
|
|
|
152
|
|
|
$url = $this->Auth->redirectUrl(); |
153
|
|
View Code Duplication |
if (substr($this->Auth->redirectUrl(), -5) == 'login') { |
|
|
|
|
154
|
|
|
$url = ['controller' => 'pages', 'action' => 'home']; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
return $this->redirect($url); |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
$user = $this->Users |
|
|
|
|
161
|
|
|
->find() |
162
|
|
|
->where([ |
163
|
|
|
'username' => $this->request->data['username'] |
164
|
|
|
]) |
165
|
|
|
->select([ |
166
|
|
|
'id', |
167
|
|
|
'group_id', |
168
|
|
|
'username', |
169
|
|
|
'email' |
170
|
|
|
]) |
171
|
|
|
->first(); |
172
|
|
|
|
173
|
|
|
if (!is_null($user)) { |
174
|
|
|
//Users Event. |
175
|
|
|
$this->eventManager()->attach(new Users()); |
|
|
|
|
176
|
|
|
$event = new Event('Users.login.failed', $this, [ |
177
|
|
|
'user_id' => $user->id, |
178
|
|
|
'username' => $user->username, |
179
|
|
|
'group_id' => $user->group_id, |
180
|
|
|
'user_ip' => $this->request->clientIp(), |
181
|
|
|
'user_email' => $user->email, |
182
|
|
|
'user_agent' => $this->request->header('User-Agent'), |
183
|
|
|
'action' => 'user.connection.manual.failed' |
184
|
|
|
]); |
185
|
|
|
$this->eventManager()->dispatch($event); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
$this->Flash->error(__("Your username or password doesn't match.")); |
189
|
|
|
|
190
|
|
|
$userRegister = $this->Users->newEntity($this->request->data); |
|
|
|
|
191
|
|
|
|
192
|
|
|
break; |
193
|
|
|
|
194
|
|
|
case "register": |
195
|
|
|
$userRegister = $this->Users->newEntity($this->request->data, ['validate' => 'create']); |
|
|
|
|
196
|
|
|
|
197
|
|
|
$userRegister->register_ip = $this->request->clientIp(); |
198
|
|
|
$userRegister->last_login_ip = $this->request->clientIp(); |
199
|
|
|
$userRegister->last_login = new Time(); |
200
|
|
|
|
201
|
|
|
if ($this->Recaptcha->verify() || Configure::read('Recaptcha.bypass') === true) { |
|
|
|
|
202
|
|
|
if ($this->Users->save($userRegister)) { |
|
|
|
|
203
|
|
|
$user = $this->Auth->identify(); |
204
|
|
|
|
205
|
|
|
if ($user) { |
206
|
|
|
$this->Auth->setUser($user); |
|
|
|
|
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
$user = $this->Users->get($user['id']); |
|
|
|
|
210
|
|
|
|
211
|
|
|
//Statistics Event. |
212
|
|
|
$this->eventManager()->attach(new Statistics()); |
|
|
|
|
213
|
|
|
$stats = new Event('Model.Users.register', $this); |
214
|
|
|
$this->eventManager()->dispatch($stats); |
215
|
|
|
|
216
|
|
|
//Notification Events. |
217
|
|
|
$this->eventManager()->attach(new Notifications()); |
|
|
|
|
218
|
|
|
$event = new Event('Model.Notifications.new', $this, [ |
219
|
|
|
'user_id' => $user->id, |
220
|
|
|
'type' => 'bot' |
221
|
|
|
]); |
222
|
|
|
$this->eventManager()->dispatch($event); |
223
|
|
|
|
224
|
|
|
$viewVars = [ |
225
|
|
|
'user' => $user, |
226
|
|
|
'name' => $user->full_name |
227
|
|
|
]; |
228
|
|
|
|
229
|
|
|
$this->getMailer('User')->send('register', [$user, $viewVars]); |
230
|
|
|
|
231
|
|
|
$this->Flash->success(__("Your account has been created successfully !")); |
232
|
|
|
|
233
|
|
|
$url = $this->Auth->redirectUrl(); |
234
|
|
View Code Duplication |
if (substr($this->Auth->redirectUrl(), -5) == 'login') { |
|
|
|
|
235
|
|
|
$url = ['controller' => 'pages', 'action' => 'home']; |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
return $this->redirect($url); |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
$this->Flash->error(__("Please, correct your mistake.")); |
242
|
|
|
} else { |
243
|
|
|
$this->Flash->error(__("Please, correct your Captcha.")); |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
break; |
247
|
|
|
} |
248
|
|
|
} else { |
249
|
|
|
//Save the referer URL before the user send the login/register request else it will delete the referer. |
250
|
|
|
$this->request->session()->write('Auth.redirect', $this->referer()); |
251
|
|
|
|
252
|
|
|
$userRegister = $this->Users->newEntity($this->request->data, ['validate' => 'create']); |
|
|
|
|
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
if ($this->Auth->user()) { |
256
|
|
|
return $this->redirect($this->Auth->redirectUrl()); |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
$this->set(compact('userRegister')); |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* Handle the login part after all verification. |
264
|
|
|
* |
265
|
|
|
* @param array $userLogin The user information. |
266
|
|
|
* |
267
|
|
|
* @return void |
268
|
|
|
*/ |
269
|
|
|
protected function _handleLogin($userLogin) |
270
|
|
|
{ |
271
|
|
|
$this->Auth->setUser($userLogin); |
272
|
|
|
|
273
|
|
|
$user = $this->Users->newEntity($userLogin); |
|
|
|
|
274
|
|
|
$user->isNew(false); |
275
|
|
|
$user->id = $userLogin['id']; |
276
|
|
|
|
277
|
|
|
$user->last_login = new Time(); |
278
|
|
|
$user->last_login_ip = $this->request->clientIp(); |
279
|
|
|
|
280
|
|
|
$this->Users->save($user); |
|
|
|
|
281
|
|
|
|
282
|
|
|
//Cookies. |
283
|
|
|
$this->Cookie->configKey('CookieAuth', [ |
284
|
|
|
'expires' => '+1 year', |
285
|
|
|
'httpOnly' => true |
286
|
|
|
]); |
287
|
|
|
$this->Cookie->write('CookieAuth', [ |
288
|
|
|
'username' => $this->request->data('username'), |
289
|
|
|
'password' => $this->request->data('password') |
290
|
|
|
]); |
291
|
|
|
|
292
|
|
|
//Badge Event. |
293
|
|
|
$this->eventManager()->attach(new Badges($this)); |
|
|
|
|
294
|
|
|
$badge = new Event('Model.Users.register', $this, [ |
295
|
|
|
'user' => $user |
296
|
|
|
]); |
297
|
|
|
$this->eventManager()->dispatch($badge); |
298
|
|
|
|
299
|
|
|
//Logs Event. |
300
|
|
|
$this->eventManager()->attach(new Logs()); |
|
|
|
|
301
|
|
|
$event = new Event('Log.User', $this, [ |
302
|
|
|
'user_id' => $user->id, |
303
|
|
|
'username' => $user->username, |
304
|
|
|
'user_ip' => $this->request->clientIp(), |
305
|
|
|
'user_agent' => $this->request->header('User-Agent'), |
306
|
|
|
'action' => 'user.connection.manual.success' |
307
|
|
|
]); |
308
|
|
|
$this->eventManager()->dispatch($event); |
309
|
|
|
|
310
|
|
|
$this->request->session()->write('Notification', ['type' => 'primary', 'message' => __('Happy to see you again {0} ! ', h($user->username))]); |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
/** |
314
|
|
|
* Logout an user. |
315
|
|
|
* |
316
|
|
|
* @return \Cake\Network\Response |
317
|
|
|
*/ |
318
|
|
|
public function logout() |
319
|
|
|
{ |
320
|
|
|
$this->request->session()->write('Notification', ['type' => 'danger', 'message' => __('See you later {0} ! ', h($this->Auth->user('username')))]); |
321
|
|
|
|
322
|
|
|
return $this->redirect($this->Auth->logout()); |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
/** |
326
|
|
|
* Ask to the user the 2FA code and verify it. |
327
|
|
|
* |
328
|
|
|
* @return \Cake\Network\Response|void |
329
|
|
|
*/ |
330
|
|
|
public function tfa() |
331
|
|
|
{ |
332
|
|
|
if ($this->Auth->user()) { |
333
|
|
|
return $this->redirect($this->Auth->redirectUrl()); |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
if ($this->request->is('post')) { |
337
|
|
|
$this->loadModel('UsersTwoFactorAuth'); |
338
|
|
|
|
339
|
|
|
$id = $this->Cookie->read('CookieTfa'); |
340
|
|
|
|
341
|
|
View Code Duplication |
if (empty($id) || $id == false) { |
|
|
|
|
342
|
|
|
$this->Cookie->delete('CookieTfa'); |
343
|
|
|
|
344
|
|
|
return $this->redirect($this->Auth->config('loginAction')); |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
try { |
348
|
|
|
$id = Security::decrypt(base64_decode($id), Configure::read('Security.key')); |
349
|
|
|
} catch (\Exception $e) { |
350
|
|
|
$this->Flash->error(__('The link used for the Two-factor Authentication is incorrect.')); |
351
|
|
|
|
352
|
|
|
return $this->redirect($this->Auth->config('loginAction')); |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
$userTfa = $this->UsersTwoFactorAuth |
|
|
|
|
356
|
|
|
->find() |
357
|
|
|
->where([ |
358
|
|
|
'user_id' => $id |
359
|
|
|
]) |
360
|
|
|
->first(); |
361
|
|
|
|
362
|
|
|
$tfa = new TwoFactorAuth('Xeta'); |
363
|
|
|
|
364
|
|
|
$isAuthorized = false; |
365
|
|
|
$recoveryCodeUsed = false; |
366
|
|
|
|
367
|
|
|
if ($tfa->verifyCode($userTfa->secret, $this->request->data['code']) === true && $this->request->data['code'] !== $userTfa->current_code) { |
368
|
|
|
$isAuthorized = true; |
369
|
|
|
//Check recovery code and verify if the recovery code is not already used. |
370
|
|
|
} elseif ($userTfa->recovery_code === $this->request->data['code'] && $userTfa->recovery_code_used == false && $this->request->data['code'] !== $userTfa->current_code) { |
371
|
|
|
$isAuthorized = true; |
372
|
|
|
$recoveryCodeUsed = true; |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
if ($isAuthorized === true) { |
376
|
|
|
$data = [ |
377
|
|
|
'session' => $this->request->clientIp() . $this->request->header('User-Agent') . gethostbyaddr($this->request->clientIp()), |
378
|
|
|
'current_code' => $recoveryCodeUsed === true ? 'recovery' : $this->request->data['code'], |
379
|
|
|
'recovery_code_used' => $recoveryCodeUsed === true ? 1 : $userTfa->recovery_code_used |
380
|
|
|
]; |
381
|
|
|
|
382
|
|
|
$this->UsersTwoFactorAuth->patchEntity($userTfa, $data); |
|
|
|
|
383
|
|
|
$this->UsersTwoFactorAuth->save($userTfa); |
|
|
|
|
384
|
|
|
|
385
|
|
|
//Login the user. |
386
|
|
|
$userLogin = $this->Users |
|
|
|
|
387
|
|
|
->find() |
388
|
|
|
->where([ |
389
|
|
|
'id' => $id |
390
|
|
|
]) |
391
|
|
|
->hydrate(false) |
392
|
|
|
->first(); |
393
|
|
|
|
394
|
|
|
unset($userLogin['password']); |
395
|
|
|
|
396
|
|
|
$this->_handleLogin($userLogin); |
397
|
|
|
|
398
|
|
|
$this->Cookie->delete('CookieTfa'); |
399
|
|
|
|
400
|
|
|
//Logs Event. |
401
|
|
|
$this->eventManager()->attach(new Logs()); |
|
|
|
|
402
|
|
|
$event = new Event('Log.User', $this, [ |
403
|
|
|
'user_id' => $userLogin['id'], |
404
|
|
|
'username' => $userLogin['username'], |
405
|
|
|
'user_ip' => $this->request->clientIp(), |
406
|
|
|
'user_agent' => $this->request->header('User-Agent'), |
407
|
|
|
'action' => '2FA.recovery_code.used' |
408
|
|
|
]); |
409
|
|
|
$this->eventManager()->dispatch($event); |
410
|
|
|
|
411
|
|
|
return $this->redirect(['controller' => 'pages', 'action' => 'home']); |
412
|
|
|
} else { |
413
|
|
|
$this->Flash->error(__('Two-factor secret verification failed. Please verify your code and try again.')); |
414
|
|
|
} |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
$id = $this->Cookie->read('CookieTfa'); |
418
|
|
|
|
419
|
|
View Code Duplication |
if (empty($id) || $id == false) { |
|
|
|
|
420
|
|
|
$this->Cookie->delete('CookieTfa'); |
421
|
|
|
|
422
|
|
|
return $this->redirect($this->Auth->config('loginAction')); |
423
|
|
|
} |
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
/** |
427
|
|
|
* Page to configure our account. |
428
|
|
|
* |
429
|
|
|
* @return void |
430
|
|
|
*/ |
431
|
|
|
public function account() |
432
|
|
|
{ |
433
|
|
|
$user = $this->Users->get($this->Auth->user('id')); |
|
|
|
|
434
|
|
|
|
435
|
|
|
if ($this->request->is('put')) { |
436
|
|
|
$user->accessible('avatar_file', true); |
437
|
|
|
$this->Users->patchEntity($user, $this->request->data(), ['validate' => 'account']); |
|
|
|
|
438
|
|
|
|
439
|
|
|
if ($this->Users->save($user)) { |
|
|
|
|
440
|
|
|
$this->request->session()->write('Auth.User.avatar', $user->avatar); |
441
|
|
|
|
442
|
|
|
//Logs Event. |
443
|
|
|
$this->eventManager()->attach(new Logs()); |
|
|
|
|
444
|
|
|
$event = new Event('Log.User', $this, [ |
445
|
|
|
'user_id' => $user->id, |
446
|
|
|
'username' => $user->username, |
447
|
|
|
'user_ip' => $this->request->clientIp(), |
448
|
|
|
'user_agent' => $this->request->header('User-Agent'), |
449
|
|
|
'action' => 'user.account.modify' |
450
|
|
|
]); |
451
|
|
|
$this->eventManager()->dispatch($event); |
452
|
|
|
|
453
|
|
|
$this->Flash->success(__("Your information has been updated !")); |
454
|
|
|
} |
455
|
|
|
} |
456
|
|
|
|
457
|
|
|
$this->set(compact('user')); |
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
/** |
461
|
|
|
* Page to configure our settings. |
462
|
|
|
* |
463
|
|
|
* @return \Cake\Network\Response|void |
464
|
|
|
*/ |
465
|
|
|
public function settings() |
466
|
|
|
{ |
467
|
|
|
$user = $this->Users->get($this->Auth->user('id')); |
|
|
|
|
468
|
|
|
|
469
|
|
|
$oldEmail = $user->email; |
470
|
|
|
|
471
|
|
|
if ($this->request->is('put')) { |
472
|
|
|
$method = ($this->request->data['method']) ? $this->request->data['method'] : false; |
473
|
|
|
|
474
|
|
|
switch ($method) { |
475
|
|
|
case "email": |
476
|
|
|
if (!isset($this->request->data['email'])) { |
477
|
|
|
$this->set(compact('user', 'oldEmail')); |
478
|
|
|
|
479
|
|
|
return $this->redirect(['action' => 'settings']); |
480
|
|
|
} |
481
|
|
|
|
482
|
|
|
$this->Users->patchEntity($user, $this->request->data(), ['validate' => 'settings']); |
|
|
|
|
483
|
|
|
|
484
|
|
View Code Duplication |
if ($this->Users->save($user)) { |
|
|
|
|
485
|
|
|
$oldEmail = $this->request->data['email']; |
486
|
|
|
|
487
|
|
|
//Logs Event. |
488
|
|
|
$this->eventManager()->attach(new Logs()); |
|
|
|
|
489
|
|
|
$event = new Event('Log.User', $this, [ |
490
|
|
|
'user_id' => $user->id, |
491
|
|
|
'username' => $user->username, |
492
|
|
|
'user_ip' => $this->request->clientIp(), |
493
|
|
|
'user_agent' => $this->request->header('User-Agent'), |
494
|
|
|
'action' => 'user.email' |
495
|
|
|
]); |
496
|
|
|
$this->eventManager()->dispatch($event); |
497
|
|
|
|
498
|
|
|
$this->Flash->success(__("Your E-mail has been changed !")); |
499
|
|
|
} |
500
|
|
|
break; |
501
|
|
|
|
502
|
|
|
case "password": |
503
|
|
|
$data = $this->request->data; |
504
|
|
|
if (!isset($data['old_password']) || !isset($data['password']) || !isset($data['password_confirm'])) { |
505
|
|
|
$this->set(compact('user', 'oldEmail')); |
506
|
|
|
|
507
|
|
|
return $this->Flash->error(__("Please, complete all fields !")); |
508
|
|
|
} |
509
|
|
|
|
510
|
|
|
if (!(new DefaultPasswordHasher)->check($data['old_password'], $user->password)) { |
511
|
|
|
$this->set(compact('user', 'oldEmail')); |
512
|
|
|
|
513
|
|
|
return $this->Flash->error(__("Your old password don't match !")); |
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
$this->Users->patchEntity($user, $this->request->data(), ['validate' => 'settings']); |
|
|
|
|
517
|
|
View Code Duplication |
if ($this->Users->save($user)) { |
|
|
|
|
518
|
|
|
//Logs Event. |
519
|
|
|
$this->eventManager()->attach(new Logs()); |
|
|
|
|
520
|
|
|
$event = new Event('Log.User', $this, [ |
521
|
|
|
'user_id' => $user->id, |
522
|
|
|
'username' => $user->username, |
523
|
|
|
'user_ip' => $this->request->clientIp(), |
524
|
|
|
'user_agent' => $this->request->header('User-Agent'), |
525
|
|
|
'action' => 'user.password.change' |
526
|
|
|
]); |
527
|
|
|
$this->eventManager()->dispatch($event); |
528
|
|
|
|
529
|
|
|
$this->Flash->success(__("Your password has been changed !")); |
530
|
|
|
} |
531
|
|
|
break; |
532
|
|
|
} |
533
|
|
|
} |
534
|
|
|
|
535
|
|
|
$this->set(compact('user', 'oldEmail')); |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
/** |
539
|
|
|
* View a profile page of an user. |
540
|
|
|
* |
541
|
|
|
* @return \Cake\Network\Response|void |
542
|
|
|
*/ |
543
|
|
|
public function profile() |
544
|
|
|
{ |
545
|
|
|
$user = $this->Users |
|
|
|
|
546
|
|
|
->find() |
547
|
|
|
->where([ |
548
|
|
|
'Users.id' => $this->request->id |
549
|
|
|
]) |
550
|
|
|
->contain([ |
551
|
|
|
'Groups' => function ($q) { |
552
|
|
|
return $q->select(['id', 'name', 'css', 'is_staff', 'is_member']); |
553
|
|
|
}, |
554
|
|
|
'BlogArticles' => function ($q) { |
555
|
|
|
return $q |
556
|
|
|
->limit(Configure::read('User.Profile.max_blog_articles')) |
557
|
|
|
->order(['BlogArticles.created' => 'DESC']); |
558
|
|
|
}, |
559
|
|
|
'BlogArticlesComments' => function ($q) { |
560
|
|
|
return $q |
561
|
|
|
->limit(Configure::read('User.Profile.max_blog_comments')) |
562
|
|
|
->contain([ |
563
|
|
|
'BlogArticles' => function ($q) { |
564
|
|
|
return $q->select(['id', 'title']); |
565
|
|
|
} |
566
|
|
|
]) |
567
|
|
|
->order(['BlogArticlesComments.created' => 'DESC']); |
568
|
|
|
}, |
569
|
|
|
'BadgesUsers' => function ($q) { |
570
|
|
|
return $q |
571
|
|
|
->contain([ |
572
|
|
|
'Badges' => function ($q) { |
573
|
|
|
return $q |
574
|
|
|
->select([ |
575
|
|
|
'name', |
576
|
|
|
'picture' |
577
|
|
|
]); |
578
|
|
|
} |
579
|
|
|
]) |
580
|
|
|
->order([ |
581
|
|
|
'BadgesUsers.id' => 'DESC' |
582
|
|
|
]); |
583
|
|
|
} |
584
|
|
|
]) |
585
|
|
|
->map(function ($user) { |
586
|
|
|
$user->online = $this->SessionsActivity->getOnlineStatus($user); |
|
|
|
|
587
|
|
|
$user->background_profile = UsersUtility::getProfileBackground(); |
588
|
|
|
|
589
|
|
|
return $user; |
590
|
|
|
}) |
591
|
|
|
->first(); |
592
|
|
|
|
593
|
|
View Code Duplication |
if (is_null($user) || $user->is_deleted == true) { |
|
|
|
|
594
|
|
|
$this->Flash->error(__('This user doesn\'t exist or has been deleted.')); |
595
|
|
|
|
596
|
|
|
return $this->redirect(['controller' => 'pages', 'action' => 'home']); |
597
|
|
|
} |
598
|
|
|
|
599
|
|
|
$this->set(compact('user')); |
600
|
|
|
} |
601
|
|
|
|
602
|
|
|
/** |
603
|
|
|
* Delete an user with all his comments, articles and likes. |
604
|
|
|
* |
605
|
|
|
* @return \Cake\Network\Response |
606
|
|
|
*/ |
607
|
|
|
public function delete() |
608
|
|
|
{ |
609
|
|
|
if (!$this->request->is('post')) { |
610
|
|
|
return $this->redirect(['action' => 'settings']); |
611
|
|
|
} |
612
|
|
|
|
613
|
|
|
$user = $this->Users->get($this->Auth->user('id')); |
|
|
|
|
614
|
|
|
|
615
|
|
|
if (!(new DefaultPasswordHasher)->check($this->request->data['password'], $user->password)) { |
616
|
|
|
$this->Flash->error(__("Your password doesn't match !")); |
617
|
|
|
|
618
|
|
|
return $this->redirect(['action' => 'settings']); |
619
|
|
|
} |
620
|
|
|
|
621
|
|
|
$user->is_deleted = true; |
622
|
|
|
|
623
|
|
|
if ($this->Users->save($user)) { |
|
|
|
|
624
|
|
|
$this->Flash->success(__("Your account has been deleted successfully ! Thanks for your visit !")); |
625
|
|
|
|
626
|
|
|
return $this->redirect($this->Auth->logout()); |
627
|
|
|
} |
628
|
|
|
|
629
|
|
|
$this->Flash->error(__("Unable to delete your account, please try again.")); |
630
|
|
|
|
631
|
|
|
return $this->redirect(['action' => 'settings']); |
632
|
|
|
} |
633
|
|
|
|
634
|
|
|
/** |
635
|
|
|
* Display all notifications related to the user. |
636
|
|
|
* |
637
|
|
|
* @return void |
638
|
|
|
*/ |
639
|
|
View Code Duplication |
public function notifications() |
|
|
|
|
640
|
|
|
{ |
641
|
|
|
$this->loadModel('Notifications'); |
642
|
|
|
|
643
|
|
|
$this->paginate = [ |
644
|
|
|
'maxLimit' => Configure::read('User.notifications_per_page') |
645
|
|
|
]; |
646
|
|
|
|
647
|
|
|
$notifications = $this->Notifications |
|
|
|
|
648
|
|
|
->find() |
649
|
|
|
->where([ |
650
|
|
|
'user_id' => $this->Auth->user('id') |
651
|
|
|
]) |
652
|
|
|
->order([ |
653
|
|
|
'is_read' => 'ASC', |
654
|
|
|
'created' => 'DESC' |
655
|
|
|
]) |
656
|
|
|
->find('map', [ |
657
|
|
|
'session' => $this->request->session() |
658
|
|
|
]); |
659
|
|
|
|
660
|
|
|
$notifications = $this->paginate($notifications); |
661
|
|
|
|
662
|
|
|
$this->set(compact('notifications')); |
663
|
|
|
} |
664
|
|
|
|
665
|
|
|
/** |
666
|
|
|
* Display the form to reset the password. |
667
|
|
|
* |
668
|
|
|
* @return \Cake\Network\Response|void |
669
|
|
|
*/ |
670
|
|
|
public function forgotPassword() |
671
|
|
|
{ |
672
|
|
|
if ($this->Auth->user()) { |
673
|
|
|
return $this->redirect(['controller' => 'pages', 'action' => 'home']); |
674
|
|
|
} |
675
|
|
|
|
676
|
|
|
if ($this->request->is('post')) { |
677
|
|
|
$user = $this->Users |
|
|
|
|
678
|
|
|
->find() |
679
|
|
|
->where([ |
680
|
|
|
'Users.email' => $this->request->data['email'] |
681
|
|
|
]) |
682
|
|
|
->first(); |
683
|
|
|
|
684
|
|
|
if (is_null($user)) { |
685
|
|
|
$this->Flash->error(__("This E-mail doesn't exist or the account has been deleted.")); |
686
|
|
|
|
687
|
|
|
$this->set(compact('user')); |
688
|
|
|
|
689
|
|
|
return; |
690
|
|
|
} |
691
|
|
|
|
692
|
|
|
if (!$this->Recaptcha->verify()) { |
|
|
|
|
693
|
|
|
$this->Flash->error(__("Please, correct your Captcha.")); |
694
|
|
|
|
695
|
|
|
$this->set(compact('user')); |
696
|
|
|
|
697
|
|
|
return; |
698
|
|
|
} |
699
|
|
|
|
700
|
|
|
//Generate the unique code |
701
|
|
|
$code = md5(rand() . uniqid() . time()); |
702
|
|
|
|
703
|
|
|
//Update the user's information |
704
|
|
|
$user->password_code = $code; |
705
|
|
|
$user->password_code_expire = new Time(); |
706
|
|
|
|
707
|
|
|
$this->Users->save($user); |
|
|
|
|
708
|
|
|
|
709
|
|
|
$viewVars = [ |
710
|
|
|
'userId' => $user->id, |
711
|
|
|
'name' => $user->full_name, |
712
|
|
|
'username' => $user->username, |
713
|
|
|
'code' => $code |
714
|
|
|
]; |
715
|
|
|
|
716
|
|
|
$this->getMailer('User')->send('forgotPassword', [$user, $viewVars]); |
717
|
|
|
|
718
|
|
|
//Logs Event. |
719
|
|
|
$this->eventManager()->attach(new Logs()); |
|
|
|
|
720
|
|
|
$event = new Event('Log.User', $this, [ |
721
|
|
|
'user_id' => $user->id, |
722
|
|
|
'username' => $user->username, |
723
|
|
|
'user_ip' => $this->request->clientIp(), |
724
|
|
|
'user_agent' => $this->request->header('User-Agent'), |
725
|
|
|
'action' => 'user.password.reset' |
726
|
|
|
]); |
727
|
|
|
$this->eventManager()->dispatch($event); |
728
|
|
|
|
729
|
|
|
$this->Flash->success(__("An E-mail has been send to <strong>{0}</strong>. Please follow the instructions in the E-mail.", h($user->email))); |
730
|
|
|
} |
731
|
|
|
|
732
|
|
|
$this->set(compact('user')); |
733
|
|
|
} |
734
|
|
|
|
735
|
|
|
/** |
736
|
|
|
* Display the form to reset his password. |
737
|
|
|
* |
738
|
|
|
* @return \Cake\Network\Response|void |
739
|
|
|
*/ |
740
|
|
|
public function resetPassword() |
741
|
|
|
{ |
742
|
|
|
if ($this->Auth->user()) { |
743
|
|
|
return $this->redirect(['controller' => 'pages', 'action' => 'home']); |
744
|
|
|
} |
745
|
|
|
|
746
|
|
|
//Prevent for empty code. |
747
|
|
|
if (empty(trim($this->request->code))) { |
748
|
|
|
$this->Flash->error(__("This code is not associated with this users or is incorrect.")); |
749
|
|
|
|
750
|
|
|
return $this->redirect(['controller' => 'pages', 'action' => 'home']); |
751
|
|
|
} |
752
|
|
|
|
753
|
|
|
$user = $this->Users |
|
|
|
|
754
|
|
|
->find() |
755
|
|
|
->where([ |
756
|
|
|
'Users.password_code' => $this->request->code, |
757
|
|
|
'Users.id' => $this->request->id |
758
|
|
|
]) |
759
|
|
|
->first(); |
760
|
|
|
|
761
|
|
View Code Duplication |
if (is_null($user)) { |
|
|
|
|
762
|
|
|
$this->Flash->error(__("This code is not associated with this users or is incorrect.")); |
763
|
|
|
|
764
|
|
|
return $this->redirect(['controller' => 'pages', 'action' => 'home']); |
765
|
|
|
} |
766
|
|
|
|
767
|
|
|
$expire = $user->password_code_expire->timestamp + (Configure::read('User.ResetPassword.expire_code') * 60); |
768
|
|
|
|
769
|
|
|
if ($expire < time()) { |
770
|
|
|
$this->Flash->error(__("This code is expired, please ask another E-mail code.")); |
771
|
|
|
|
772
|
|
|
return $this->redirect(['action' => 'forgotPassword']); |
773
|
|
|
} |
774
|
|
|
|
775
|
|
|
if ($this->request->is(['post', 'put'])) { |
776
|
|
|
$this->Users->patchEntity($user, $this->request->data, ['validate' => 'resetpassword']); |
|
|
|
|
777
|
|
|
|
778
|
|
|
if ($this->Users->save($user)) { |
|
|
|
|
779
|
|
|
$this->Flash->success(__("Your password has been changed !")); |
780
|
|
|
|
781
|
|
|
//Reset the code and the time. |
782
|
|
|
$user->password_code = ''; |
783
|
|
|
$user->password_code_expire = new Time(); |
784
|
|
|
$user->password_reset_count = $user->password_reset_count + 1; |
785
|
|
|
$this->Users->save($user); |
|
|
|
|
786
|
|
|
|
787
|
|
|
//Logs Event. |
788
|
|
|
$this->eventManager()->attach(new Logs()); |
|
|
|
|
789
|
|
|
$event = new Event('Log.User', $this, [ |
790
|
|
|
'user_id' => $user->id, |
791
|
|
|
'username' => $user->username, |
792
|
|
|
'user_ip' => $this->request->clientIp(), |
793
|
|
|
'user_agent' => $this->request->header('User-Agent'), |
794
|
|
|
'action' => 'user.password.reset.successful' |
795
|
|
|
]); |
796
|
|
|
$this->eventManager()->dispatch($event); |
797
|
|
|
|
798
|
|
|
return $this->redirect(['controller' => 'users', 'action' => 'login']); |
799
|
|
|
} |
800
|
|
|
} |
801
|
|
|
|
802
|
|
|
$this->set(compact('user')); |
803
|
|
|
} |
804
|
|
|
|
805
|
|
|
/** |
806
|
|
|
* Display the sessions and logs informations. |
807
|
|
|
* |
808
|
|
|
* @return void |
809
|
|
|
*/ |
810
|
|
|
public function security() |
811
|
|
|
{ |
812
|
|
|
$records = $this->SessionsActivity->getOnlineSessionsForUser($this->Auth->user('id')); |
|
|
|
|
813
|
|
|
|
814
|
|
|
$browscap = new Browscap(); |
815
|
|
|
$sessions = []; |
816
|
|
|
|
817
|
|
|
foreach ($records as $record) { |
818
|
|
|
$infos = $browscap->getBrowser($record->user_agent); |
819
|
|
|
|
820
|
|
|
$record->infos = $infos; |
821
|
|
|
|
822
|
|
|
array_push($sessions, $record); |
823
|
|
|
} |
824
|
|
|
|
825
|
|
|
$this->loadModel('UsersLogs'); |
826
|
|
|
|
827
|
|
|
$this->paginate = [ |
828
|
|
|
'maxLimit' => 25 |
829
|
|
|
]; |
830
|
|
|
|
831
|
|
|
$logs = $this->UsersLogs |
|
|
|
|
832
|
|
|
->find() |
833
|
|
|
->where([ |
834
|
|
|
'UsersLogs.user_id' => $this->Auth->user('id') |
835
|
|
|
]) |
836
|
|
|
->order([ |
837
|
|
|
'UsersLogs.created' => 'DESC' |
838
|
|
|
]) |
839
|
|
|
->formatResults(function ($logs) use ($browscap) { |
840
|
|
|
return $logs->map(function ($log) use ($browscap) { |
841
|
|
|
$log->infos = $browscap->getBrowser($log->user_agent); |
842
|
|
|
|
843
|
|
|
return $log; |
844
|
|
|
}); |
845
|
|
|
}); |
846
|
|
|
|
847
|
|
|
$logs = $this->paginate($logs); |
848
|
|
|
|
849
|
|
|
$user = $this->Users |
|
|
|
|
850
|
|
|
->find() |
851
|
|
|
->where([ |
852
|
|
|
'Users.id' => $this->Auth->user('id') |
853
|
|
|
]) |
854
|
|
|
->select([ |
855
|
|
|
'id', |
856
|
|
|
'two_factor_auth_enabled' |
857
|
|
|
]) |
858
|
|
|
->first(); |
859
|
|
|
|
860
|
|
|
$this->set(compact('sessions', 'logs', 'user')); |
861
|
|
|
} |
862
|
|
|
} |
863
|
|
|
|
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.