Coveralls logob
Coveralls logo
  • Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

burzum / cakephp-user-tools / 318

7 Sep 2017 - 14:12 coverage: 37.811% (-0.6%) from 38.365%
318

Pull #46

travis-ci

9181eb84f9c35729a3bad740fb7f9d93?size=18&default=identiconweb-flow
Improving the code and doc blocks
Pull Request #46: Updating the plugin to CakePHP 3.4 changes

43 of 114 new or added lines in 7 files covered. (37.72%)

21 existing lines in 4 files now uncovered.

304 of 804 relevant lines covered (37.81%)

2.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.29
/src/Model/Behavior/UserBehavior.php
1
<?php
2
/**
3
 * UserBehavior
4
 *
5
 * @author Florian Krämer
6
 * @copyright 2013 - 2017 Florian Krämer
7
 * @copyright 2012 Cake Development Corporation
8
 * @license MIT
9
 */
10
namespace Burzum\UserTools\Model\Behavior;
11

12
use Burzum\UserTools\Mailer\UsersMailer;
13
use Burzum\UserTools\Model\PasswordAndTokenTrait;
14
use Burzum\UserTools\Model\PasswordHasherTrait;
15
use Burzum\UserTools\Model\UserValidationTrait;
16
use Cake\Core\Configure;
17
use Cake\Datasource\EntityInterface;
18
use Cake\Datasource\Exception\RecordNotFoundException;
19
use Cake\Event\Event;
20
use Cake\Event\EventDispatcherTrait;
21
use Cake\I18n\Time;
22
use Cake\Mailer\MailerAwareTrait;
23
use Cake\ORM\Behavior;
24
use Cake\ORM\Entity;
25
use Cake\ORM\Query;
26
use Cake\ORM\Table;
27
use Cake\Utility\Hash;
28
use Cake\Utility\Text;
29
use RuntimeException;
30

31
/**
32
 * User Behavior
33
 *
34
 * Options:
35
 * - `emailConfig` - Email configuration to use, default is `default`
36
 * - `defaultValidation` - Set up the default validation rules of the plugin.
37
 *    Default is true.
38
 * - `useUuid` - If your app is using ints instead of UUIDs set this to false.
39
 * - `passwordHasher` - Password hasher to use, default is `Default`
40
 * - `mailer` - Mailer class to use, default is
41
 *   `Burzum\UserTools\Mailer\UsersMailer`
42
 * - `passwordMinLength` - Minimum password length for validation, default is 6
43
 * - `getUser` - An array of options, please see UserBehavior::getUser()
44
 * - `register` - An array of options, please see UserBehavior::register()
45
 * - `loginFields` -
46
 * - `fieldMap` -
47
 * - `beforeSave` -
48
 * - `changePassword` -
49
 * - `updateLastActivity` -
50
 * - `initPasswordReset` -
51
 * - `sendVerificationEmail` -
52
 * - `sendNewPasswordEmail` -
53
 * - `sendPasswordResetToken` -
54
 * - `implementedFinders` - List of implemented finders, `active`
55
 *    and `emailVerified`
56
 */
57
class UserBehavior extends Behavior {
58

59
        use EventDispatcherTrait;
60
        use MailerAwareTrait;
61
        use PasswordAndTokenTrait;
62
        use PasswordHasherTrait;
63
        use UserValidationTrait;
64

65
        /**
66
         * Default config
67
         *
68
         * @var array
69
         */
70
        protected $_defaultConfig = [
71
                'emailConfig' => 'default',
72
                'defaultValidation' => true,
73
                'useUuid' => true,
74
                'passwordHasher' => 'Default',
75
                'mailer' => UsersMailer::class,
76
                'passwordMinLength' => 6,
77
                'getUser' => [],
78
                'register' => [
79
                        'defaultRole' => null,
80
                        'hashPassword' => true,
81
                        'userActive' => true,
82
                        'generatePassword' => false,
83
                        'emailVerification' => true,
84
                        'verificationExpirationTime' => '+1 day',
85
                        'beforeRegister' => true,
86
                        'afterRegister' => true,
87
                        'tokenLength' => 32,
88
                        'saveOptions' => []
89
                ],
90
                'loginFields' => [
91
                        'username' => 'email',
92
                        'password' => 'password'
93
                ],
94
                'fieldMap' => [
95
                        'username' => 'username',
96
                        'password' => 'password',
97
                        'email' => 'email',
98
                        'passwordCheck' => 'confirm_password',
99
                        'oldPassword' => 'old_password',
100
                        'lastAction' => 'last_action',
101
                        'lastLogin' => 'last_login',
102
                        'role' => 'role',
103
                        'emailToken' => 'email_token',
104
                        'emailVerified' => 'email_verified',
105
                        'emailTokenExpires' => 'email_token_expires',
106
                        'passwordToken' => 'password_token',
107
                        'passwordTokenExpires' => 'password_token_expires',
108
                        'active' => 'active',
109
                        'slug' => 'slug',
110
                ],
111
                'beforeSave' => [
112
                        // Enable this only if you're not using the built in password change
113
                        // and want the password hash to be updated automatically
114
                        'handleNewPasswordByOldPassword' => false
115
                ],
116
                'changePassword' => [
117
                        'hashPassword' => true
118
                ],
119
                'updateLastActivity' => [
120
                        'dateFormat' => 'Y-m-d H:i:s',
121
                ],
122
                'initPasswordReset' => [
123
                        'tokenLength' => 32,
124
                        'expires' => '+1 day'
125
                ],
126
                'sendVerificationEmail' => [
127
                        'template' => 'Burzum/UserTools.Users/verification_email',
128
                ],
129
                'sendNewPasswordEmail' => [
130
                        'template' => 'Burzum/UserTools.Users/new_password',
131
                ],
132
                'sendPasswordResetToken' => [
133
                        'template' => 'Burzum/UserTools.Users/password_reset',
134
                ],
135
                'implementedFinders' => [
136
                        'active' => 'findActive',
137
                        'emailVerified' => 'findEmailVerified'
138
                ]
139
        ];
140

141
        /**
142
         * Keeping a reference to the table in order to be able to retrieve associations
143
         * and fetch records for counting.
144
         *
145
         * @var \Cake\ORM\Table
146
         */
147
        protected $_table;
148

149
        /**
150
         * Constructor
151
         *
152
         * @param \Cake\ORM\Table $table The table this behavior is attached to.
153
         * @param array $config The settings for this behavior.
154
         */
155
        public function __construct(Table $table, array $config = []) {
156
                $this->_defaultConfig = Hash::merge($this->_defaultConfig, (array)Configure::read('UserTools.Behavior'));
21×
157
                parent::__construct($table, $config);
21×
158

159
                $this->_defaultPasswordHasher = $this->getConfig('passwordHasher');
21×
160
                $this->_table = $table;
21×
161

162
                if ($this->_config['defaultValidation'] === true) {
21×
163
                        $this->setupDefaultValidation();
21×
164
                }
165

166
                $this->eventManager()->on($this->_table);
21×
167
        }
21×
168

169
        /**
170
         * Gets the mapped field name of the table
171
         *
172
         * @param string $field Field name to get the mapped field for
173
         * @throws \RuntimeException
174
         * @return string field name of the table
175
         */
176
        protected function _field($field) {
177
                if (!isset($this->_config['fieldMap'][$field])) {
21×
178
                        throw new RuntimeException(__d('user_tools', 'Invalid field "%s"!', $field));
!
179
                }
180

181
                return $this->_config['fieldMap'][$field];
21×
182
        }
183

184
        /**
185
         * Sets validation rules for users up.
186
         *
187
         * @return void
188
         */
189
        public function setupDefaultValidation() {
190
                $validator = $this->_table->validator('default');
21×
191
                $validator = $this->validationUserName($validator);
21×
192
                $validator = $this->validationPassword($validator);
21×
193
                $validator = $this->validationConfirmPassword($validator);
21×
194
                $this->validationEmail($validator);
21×
195
        }
21×
196

197
        /**
198
         * Returns a datetime in the format Y-m-d H:i:s
199
         *
200
         * @param string $time strtotime() compatible string, default is "+1 day"
201
         * @param string $dateFormat date() compatible date format string
202
         * @return string
203
         */
204
        public function expirationTime($time = '+1 day', $dateFormat = 'Y-m-d H:i:s') {
205
                return date($dateFormat, strtotime($time));
3×
206
        }
207

208
        /**
209
         * Updates a given field with the current date time in the format Y-m-d H:i:s
210
         *
211
         * @param string $userId User id
212
         * @param string $field Default is "last_action", changing it allows you to use this method also for "last_login" for example
213
         * @param array $options Options array
214
         * @return bool True on success
215
         */
216
        public function updateLastActivity($userId = null, $field = 'last_action', $options = []) {
217
                $options = Hash::merge($this->_config['updateLastActivity'], $options);
1×
218
                if ($this->_table->exists([
1×
219
                        $this->_table->aliasField($this->_table->primaryKey()) => $userId
1×
220
                ])) {
221
                        return $this->_table->updateAll(
1×
222
                                [$field => date($options['dateFormat'])],
1×
223
                                [$this->_table->primaryKey() => $userId]
1×
224
                        );
225
                }
226

UNCOV
227
                return false;
!
228
        }
229

230
        /**
231
         * Handles the email verification if required
232
         *
233
         * @param \Cake\Datasource\EntityInterface $entity User entity
234
         * @param array $options Options array
235
         * @return void
236
         */
237
        protected function _emailVerification(EntityInterface &$entity, $options) {
238
                if ($options['emailVerification'] === true) {
2×
239
                        $entity->set($this->_field('emailToken'), $this->generateToken($options['tokenLength']));
2×
240
                        if ($options['verificationExpirationTime'] !== false) {
2×
241
                                $entity->set($this->_field('emailTokenExpires'), $this->expirationTime($options['verificationExpirationTime']));
2×
242
                        }
243
                        $entity->set($this->_field('emailVerified'), false);
2×
244
                } else {
245
                        $entity->set($this->_field('emailVerified'), true);
!
246
                }
247
        }
2×
248

249
        /**
250
         * Behavior internal before registration callback
251
         *
252
         * This method deals with most of the settings for the registration that can be
253
         * applied before the actual user record is saved.
254
         *
255
         * @param \Cake\Datasource\EntityInterface $entity Users entity
256
         * @param array $options Options
257
         * @return \Cake\Datasource\EntityInterface
258
         */
259
        protected function _beforeRegister(EntityInterface $entity, $options = []) {
260
                $options = Hash::merge($this->_config['register'], $options);
2×
261

262
                $schema = $this->_table->getSchema();
2×
263
                $columnType = $schema->columnType($this->_table->getPrimaryKey());
2×
264

265
                if ($this->_config['useUuid'] === true && $columnType !== 'integer') {
2×
266
                        $primaryKey = $this->_table->getPrimaryKey();
2×
267
                        $entity->set($primaryKey, Text::uuid());
2×
268
                }
269

270
                if ($options['userActive'] === true) {
2×
271
                        $entity->set($this->_field('active'), true);
2×
272
                }
273

274
                $this->_emailVerification($entity, $options);
2×
275

276
                if (!isset($entity->{$this->_field('role')})) {
2×
277
                        $entity->set($this->_field('role'), $options['defaultRole']);
2×
278
                }
279

280
                if ($options['generatePassword'] === true) {
2×
281
                        $password = $this->generatePassword();
!
282
                        $entity->set($this->_field('password'), $password);
!
283
                        $entity->set('clear_password', $password);
!
284
                }
285

286
                if ($options['hashPassword'] === true) {
2×
287
                        $entity->set($this->_field('password'), $this->hashPassword($entity->get($this->_field('password'))));
2×
288
                }
289

290
                return $entity;
2×
291
        }
292

293
        /**
294
         * Find users with verified emails.
295
         *
296
         * @param \Cake\ORM\Query $query Query object
297
         * @param array $options Options
298
         * @return Query
299
         */
300
        public function findEmailVerified(Query $query, array $options) {
301
                $query->where([
!
302
                        $this->_table->aliasField($this->_field('emailVerified')) => true,
!
303
                ]);
304

UNCOV
305
                return $query;
!
306
        }
307

308
        /**
309
         * Find Active Users.
310
         *
311
         * @param \Cake\ORM\Query $query Query object
312
         * @param array $options Options
313
         * @return \Cake\ORM\Query Modified query
314
         */
315
        public function findActive(Query $query, array $options) {
316
                $query->where([
!
317
                        $this->_table->aliasField($this->_field('active')) => true,
!
318
                ]);
319

UNCOV
320
                return $query;
!
321
        }
322

323
        /**
324
         * Registers a new user
325
         *
326
         * You can modify the registration process through implementing an event
327
         * listener for the User.beforeRegister and User.afterRegister events.
328
         *
329
         * If you stop the events the result of the event will be returned.
330
         *
331
         * Flow:
332
         * - calls the behaviors _beforeRegister method if not disabled via config
333
         * - Fires the User.beforeRegister event
334
         * - Attempt to save the user data
335
         * - calls the behaviors _afterRegister method if not disabled via config
336
         * - Fires the User.afterRegister event
337
         *
338
         * Options:
339
         * - `beforeRegister` - Bool to call the internal _beforeRegister() method.
340
         *    Default is true
341
         * - `afterRegister` - Bool to call the internal _beforeRegister() method.
342
         *    Default is true
343
         * - `tokenLength` - Length of the verification token, default is 32
344
         * - `saveOptions` Optional save options to be passed to the save() call.
345
         * - `verificationExpirationTime` - Default is `+1 day`
346
         * - `emailVerification` - Use email verification or not, default is true.
347
         * - `defaultRole` - Default role to set in the mapped `role` field.
348
         * - `userActive` - Set the user to active by default on registration.
349
         *    Default is true
350
         * - `generatePassword` - To generate a password or not. Default is false.
351
         * - `hashPassword` - To has the password or not, default is true.
352
         *
353
         * @param \Cake\Datasource\EntityInterface $entity User Entity
354
         * @param array $options Options
355
         * @throws \InvalidArgumentException
356
         * @return \Cake\Datasource\EntityInterface|bool Returns bool false if the user could not be saved.
357
         */
358
        public function register(EntityInterface $entity, $options = []) {
359
                $options = array_merge($this->_config['register'], $options);
2×
360

361
                if ($options['beforeRegister'] === true) {
2×
362
                        $entity = $this->_beforeRegister($entity, $options);
2×
363
                }
364

365
                $event = $this->dispatchEvent('User.beforeRegister', [
2×
366
                        'data' => $entity,
2×
367
                        'table' => $this->_table
2×
368
                ]);
369

370
                if ($event->isStopped()) {
2×
371
                        return $event->result;
!
372
                }
373

374
                $result = $this->_table->save($entity, $options['saveOptions']);
2×
375

376
                if (!$result) {
2×
377
                        return $result;
1×
378
                }
379

380
                if ($options['afterRegister'] === true) {
1×
381
                        $this->_afterRegister($result, $options);
1×
382
                }
383

384
                $event = $this->dispatchEvent('user.afterRegister', [
1×
385
                        'data' => $result,
1×
386
                        'table' => $this->_table
1×
387
                ]);
388

389
                if ($event->isStopped()) {
1×
UNCOV
390
                        return $event->result;
!
391
                }
392

393
                return $result;
1×
394
        }
395

396
        /**
397
         * _afterRegister
398
         *
399
         * @param \Cake\Datasource\EntityInterface $entity User entity
400
         * @param array $options Options
401
         * @return \Cake\Datasource\EntityInterface
402
         */
403
        protected function _afterRegister(EntityInterface $entity, $options) {
404
                if ($entity) {
1×
405
                        if ($options['emailVerification'] === true) {
1×
406
                                $this->sendVerificationEmail($entity, [
1×
407
                                        'to' => $entity->get($this->_field('email'))
1×
408
                                ]);
409
                        }
410
                }
411

412
                return $entity;
1×
413
        }
414

415
        /**
416
         * Verify the email token
417
         *
418
         * @param string $token The token to check.
419
         * @param array $options Options array.
420
         * @throws \Cake\Datasource\Exception\RecordNotFoundException if the token was not found at all
421
         * @return bool|\Cake\ORM\Entity Returns false if the token has expired
422
         */
423
        public function verifyToken($token, $options = []) {
424
                $defaults = [
425
                        'tokenField' => $this->_field('emailToken'),
2×
426
                        'expirationField' => $this->_field('emailTokenExpires'),
2×
427
                        'returnData' => false,
428
                ];
429
                $options = Hash::merge($defaults, $options);
2×
430

431
                $result = $this->_getUser($token, [
2×
432
                        'field' => $options['tokenField'],
2×
433
                        'notFoundErrorMessage' => __d('burzum/user_tools', 'Invalid token!')
2×
434
                ]);
435

436
                $time = new Time();
1×
437
                $result->set(
1×
438
                        'token_is_expired',
1×
439
                        $result->get($options['expirationField']) <= $time
1×
440
                );
441

442
                $this->afterTokenVerification($result, $options);
1×
443

444
                $event = $this->dispatchEvent('User.afterTokenVerification', [
1×
445
                        'data' => $result,
1×
446
                        'options' => $options
1×
447
                ]);
448

449
                $this->eventManager()->dispatch($event);
1×
450
                if ($event->isStopped()) {
1×
NEW
451
                        return (bool)$event->result;
!
452
                }
453

454
                if ($options['returnData'] === true) {
1×
455
                        return $result;
1×
456
                }
457

458
                return $result->get('token_is_expired');
1×
459
        }
460

461
        /**
462
         * afterTokenVerification
463
         *
464
         * @param \Cake\ORM\Entity $user Entity object.
465
         * @param array $options Options array.
466
         * @return mixed
467
         */
468
        public function afterTokenVerification(Entity $user, $options = []) {
469
                if ($user->get('token_is_expired') === true) {
1×
470
                        return false;
1×
471
                }
472

473
                if ($options['tokenField'] === $this->_field('emailToken')) {
!
474
                        $user->set([
!
475
                                $this->_field('emailVerified') => true,
!
476
                                $this->_field('emailToken') => null,
!
477
                                $this->_field('emailTokenExpires') => null
!
478
                        ], [
479
                                'guard' => false
!
480
                        ]);
481
                }
482

483
                return $this->_table->save($user, ['validate' => false]);
!
484
        }
485

486
        /**
487
         * Verify the email token
488
         *
489
         * @param string $token Token string to check.
490
         * @param array $options Options array.
491
         * @throws \Cake\Datasource\Exception\RecordNotFoundException if the token was not found at all
492
         * @return bool Returns false if the token has expired
493
         */
494
        public function verifyEmailToken($token, $options = []) {
495
                $defaults = [
496
                        'tokenField' => $this->_field('emailToken'),
!
497
                        'expirationField' => $this->_field('emailTokenExpires'),
!
498
                ];
499

500
                return $this->verifyToken($token, Hash::merge($defaults, $options));
!
501
        }
502

503
        /**
504
         * Verify the password reset token
505
         *
506
         * - `tokenField` - The field to check for the token
507
         * - `expirationField` - The field to check for the token expiration date
508
         *
509
         * @param string $token Token string
510
         * @param array $options Options
511
         * @throws \Cake\Datasource\Exception\RecordNotFoundException if the token was not found at all
512
         * @return bool Returns false if the token has expired
513
         */
514
        public function verifyPasswordResetToken($token, $options = []) {
515
                $defaults = [
516
                        'tokenField' => $this->_field('passwordToken'),
!
517
                        'expirationField' => $this->_field('passwordTokenExpires'),
!
518
                        'returnData' => true,
519
                ];
520

521
                return $this->verifyToken($token, Hash::merge($defaults, $options));
!
522
        }
523

524
        /**
525
         * Password reset, compares the two passwords and saves the new password.
526
         *
527
         * @param \Cake\Datasource\EntityInterface $user Entity object.
528
         * @return bool|\Cake\Datasource\EntityInterface
529
         */
530
        public function resetPassword(Entity $user) {
531
                if (!$user->getErrors()) {
1×
532
                        $user->{$this->_field('password')} = $this->hashPassword($user->{$this->_field('password')});
1×
533
                        $user->{$this->_field('passwordToken')} = null;
1×
534
                        $user->{$this->_field('passwordTokenExpires')} = null;
1×
535

536
                        return $this->_table->save($user, ['checkRules' => false]);
1×
537
                }
538

UNCOV
539
                return false;
!
540
        }
541

542
        /**
543
         * Removes all users from the user table that did not complete the registration
544
         *
545
         * @param array $conditions Find conditions passed to where()
546
         * @return int Number of removed records
547
         */
548
        public function removeExpiredRegistrations($conditions = []) {
549
                $defaults = [
550
                        $this->_table->aliasField($this->_field('emailVerified')) => 0,
1×
551
                        $this->_table->aliasField($this->_field('emailTokenExpires')) . ' <' => date('Y-m-d H:i:s')
1×
552
                ];
553

554
                $conditions = Hash::merge($defaults, $conditions);
1×
555
                $count = $this->_table
1×
556
                        ->find()
1×
557
                        ->where($conditions)
1×
558
                        ->count();
1×
559

560
                if ($count > 0) {
1×
561
                        $this->_table->deleteAll($conditions);
1×
562
                }
563

564
                return $count;
1×
565
        }
566

567
        /**
568
         * Changes the password for an user.
569
         *
570
         * @param \Cake\Datasource\EntityInterface $entity User entity
571
         * @param array $options Options
572
         * @return bool
573
         */
574
        public function changePassword(EntityInterface $entity, array $options = []) {
575
                $options = Hash::merge($this->_config['changePassword'], $options);
!
576

577
                if ($entity->errors()) {
!
578
                        return false;
!
579
                }
580

581
                if ($options['hashPassword'] === true) {
!
582
                        $field = $this->_field('password');
!
583
                        $entity->set($field, $this->hashPassword($entity->get($field)));
!
584
                }
585

586
                return (bool)$this->_table->save($entity);
!
587
        }
588

589
        /**
590
         * Initializes a password reset process.
591
         *
592
         * @param mixed $value User id or other value to look the user up
593
         * @param array $options Options
594
         * @return void
595
         */
596
        public function initPasswordReset($value, $options = []) {
597
                $defaults = [
598
                        'field' => [
599
                                $this->_table->aliasField($this->_field('email')),
1×
600
                                $this->_table->aliasField($this->_field('username'))
1×
601
                        ]
602
                ];
603

604
                $options = Hash::merge($defaults, $this->_config['initPasswordReset'], $options);
1×
605
                $result = $this->_getUser($value, $options);
1×
606

607
                if (empty($result)) {
!
608
                        throw new RecordNotFoundException(__d('burzum/user_tools', 'User not found.'));
!
609
                }
610

611
                $result->set([
!
612
                        $this->_field('passwordToken') => $this->generateToken($options['tokenLength']),
!
613
                        $this->_field('passwordTokenExpires') => $this->expirationTime($options['expires'])
!
614
                ], [
615
                        'guard' => false
!
616
                ]);
617

618
                if (!$this->_table->save($result, ['checkRules' => false])) {
!
619
                        new RuntimeException('Could not initialize password reset. Data could not be saved.');
!
620
                }
621

622
                $this->sendPasswordResetToken($result, [
!
623
                        'to' => $result->get($this->_field('email'))
!
624
                ]);
625
        }
!
626

627
        /**
628
         * Finds a single user, convenience method.
629
         *
630
         * @param mixed $value User ID or other value to look the user up
631
         * @param array $options Options
632
         * @throws \Cake\Datasource\Exception\RecordNotFoundException;
633
         * @return \Cake\ORM\Entity
634
         */
635
        public function getUser($value, $options = []) {
636
                return $this->_getUser($value, $options);
3×
637
        }
638

639
        /**
640
         * Finds the user for the password reset.
641
         *
642
         * Extend the behavior and override this method if the configuration options
643
         * are not sufficient.
644
         *
645
         * @param mixed $value User lookup value
646
         * @param array $options Options
647
         * @throws \Cake\Datasource\Exception\RecordNotFoundException
648
         * @return \Cake\Datasource\EntityInterface
649
         */
650
        protected function _getUser($value, $options = []) {
651
                $defaults = [
652
                        'notFoundErrorMessage' => __d('user_tools', 'User not found.'),
6×
653
                        'field' => $this->_table->aliasField($this->_table->getPrimaryKey())
6×
654
                ];
655
                $defaults = Hash::merge($defaults, $this->getConfig('getUser'));
6×
656

657
                if (isset($options['field'])) {
6×
658
                        $defaults['field'] = $options['field'];
3×
659
                }
660
                $options = Hash::merge($defaults, $options);
6×
661

662
                $query = $this->_getFindUserQuery($value, $options);
6×
663

664
                if (isset($options['queryCallback']) && is_callable($options['queryCallback'])) {
6×
665
                        $query = $options['queryCallback']($query, $options);
!
666
                }
667

668
                $result = $query->first();
6×
669

670
                if (empty($result)) {
6×
671
                        throw new RecordNotFoundException($options['notFoundErrorMessage']);
3×
672
                }
673

674
                return $result;
3×
675
        }
676

677
        /**
678
         * Sets the query object for the _getUser() method up.
679
         *
680
         * @param array|string $value Value
681
         * @param array $options Options.
682
         * @return \Cake\ORM\Query
683
         */
684
        protected function _getFindUserQuery($value, $options) {
685
                if (is_string($value) && $this->_table->hasFinder($value)) {
6×
NEW
686
                        $query = $this->_table->find($value, ['getUserOptions' => $options]);
!
687
                } else {
688
                        $query = $this->_table->find();
6×
689
                }
690

691
                if (is_array($options['field'])) {
6×
692
                        foreach ($options['field'] as $field) {
1×
693
                                $query->orWhere([$field => $value]);
1×
694
                        }
695

696
                        return $query;
1×
697
                }
698

699
                return $query->where([$options['field'] => $value]);
5×
700
        }
701

702
        /**
703
         * Sends a new password to an user by email.
704
         *
705
         * Note that this is *not* a recommended way to reset an user password. A much
706
         * more secure approach is to have the user manually enter a new password and
707
         * only send him an URL with a token.
708
         *
709
         * @param string|EntityInterface $email The user entity or the email
710
         * @param array $options Optional options array, use it to pass config options.
711
         * @throws \Cake\Datasource\Exception\RecordNotFoundException
712
         * @return bool
713
         */
714
        public function sendNewPassword($email, $options = []) {
715
                if ($email instanceof EntityInterface) {
!
716
                        $result = $email;
!
717
                        $email = $result->get($this->_field('email'));
!
718
                } else {
719
                        $result = $this->_table->find()
!
720
                                ->where([
!
721
                                        $this->_table->aliasField($this->_field('email')) => $email
!
722
                                ])
723
                                ->first();
!
724

725
                        if (empty($result)) {
!
726
                                throw new RecordNotFoundException(__d('user_tools', 'Invalid user'));
!
727
                        }
728
                }
729

730
                $password = $this->generatePassword();
!
731
                $result->set([
!
732
                        $this->_field('password') => $this->hashPassword($password),
!
733
                        'clear_password' => $password
!
734
                ], [
735
                        'guard' => false
!
736
                ]);
737

738
                $this->_table->save($result, ['validate' => false]);
!
739

740
                return $this->sendNewPasswordEmail($result, ['to' => $result->get($this->_field('email'))]);
!
741
        }
742

743
        /**
744
         * beforeSave callback
745
         *
746
         * @param \Cake\Event\Event $event Event object
747
         * @param \Cake\Datasource\EntityInterface $entity Entity
748
         * @return void
749
         */
750
        public function beforeSave(Event $event, EntityInterface $entity) {
751
                $config = (array)$this->getConfig('beforeSave');
3×
752
                if ($config['handleNewPasswordByOldPassword'] === true) {
3×
753
                        $this->handleNewPasswordByOldPassword($entity);
!
754
                }
755
        }
3×
756

757
        /**
758
         * Handles the hashing of the password after the old password of the user
759
         * was verified and the new password was validated as well.
760
         *
761
         * Call this in your models beforeSave() or wherever else you want.
762
         *
763
         * This method will unset by default the `password` and `confirm_password`
764
         * fields if no `old_password` was provided or the entity has validation
765
         * errors.
766
         *
767
         * @param \Cake\Datasource\EntityInterface $user User Entity
768
         * @return void
769
         */
770
        public function handleNewPasswordByOldPassword(EntityInterface $user) {
771
                // Don't do it for new users or the password will end up empty
772
                if ($user->isNew()) {
!
773
                        return;
!
774
                }
775

776
                $oldPassword = $user->get($this->_field('oldPassword'));
!
777

778
                if (empty($oldPassword) || $user->errors()) {
!
779
                        $user->unsetProperty($this->_field('password'));
!
780
                        $user->unsetProperty($this->_field('passwordCheck'));
!
781

782
                        return;
!
783
                }
784

785
                $newPassword = $this->hashPassword($user->get($this->_field('password')));
!
786
                $user->set($this->_field('password'), $newPassword, ['guard' => false]);
!
787
        }
!
788

789
        /**
790
         * sendNewPasswordEmail
791
         *
792
         * @param \Cake\Datasource\EntityInterface $user User entity
793
         * @param array $options Options
794
         * @return void
795
         */
796
        public function sendPasswordResetToken(EntityInterface $user, $options = []) {
797
                $options = Hash::merge($this->_config['sendPasswordResetToken'], $options);
!
NEW
798
                $this->getMailer($this->getConfig('mailer'))
!
799
                        ->send('passwordResetToken', [$user, $options]);
!
800
        }
!
801

802
        /**
803
         * sendNewPasswordEmail
804
         *
805
         * @param \Cake\Datasource\EntityInterface $user User entity
806
         * @param array $options Options
807
         * @return void
808
         */
809
        public function sendNewPasswordEmail(EntityInterface $user, $options = []) {
810
                $options = Hash::merge($this->_config['sendNewPasswordEmail'], $options);
!
NEW
811
                $this->getMailer($this->getConfig('mailer'))
!
812
                        ->send('verificationEmail', [$user, $options]);
!
813
        }
!
814

815
        /**
816
         * sendVerificationEmail
817
         *
818
         * @param \Cake\Datasource\EntityInterface $user User entity
819
         * @param array $options Options
820
         * @return void
821
         */
822
        public function sendVerificationEmail(EntityInterface $user, $options = []) {
823
                $options = Hash::merge($this->_config['sendVerificationEmail'], $options);
1×
824
                $this->getMailer($this->getConfig('mailer'))
1×
825
                        ->send('verificationEmail', [$user, $options]);
1×
826
        }
1×
827
}
Troubleshooting · Open an Issue · Sales · Support · ENTERPRISE · CAREERS · STATUS
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2023 Coveralls, Inc