UserSetup   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 162
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 13
lcom 1
cbo 4
dl 0
loc 162
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 2
A getPWHashFunction() 0 23 3
A possibleGenSalt() 0 9 2
A doGenerateHash() 0 10 3
A getSalt() 0 10 2
A addEvents() 0 18 1
1
<?php
2
3
/*
4
 * This file is part of the CRUDlexUser package.
5
 *
6
 * (c) Philip Lehmann-Böhm <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace CRUDlex;
13
14
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
15
use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder;
16
use CRUDlex\AbstractData;
17
use CRUDlex\Entity;
18
19
/**
20
 * This class setups CRUDlex with some events so the passwords get salted and
21
 * hashed properly.
22
 */
23
class UserSetup
24
{
25
26
    /**
27
     * The encoder to use.
28
     */
29
    protected $encoder;
30
31
    /**
32
     * Gets a closure for possibly generating a password hash in the entity.
33
     *
34
     * @param AbstractData $data
35
     * the AbstractData instance managing the users
36
     *
37
     * @param string $passwordField
38
     * the Entity fieldname of the password hash
39
     *
40
     * @param string $saltField
41
     * the Entity fieldname of the password hash salt
42
     */
43
    protected function getPWHashFunction(AbstractData $data, $passwordField, $saltField)
44
    {
45
        $that = $this;
46
        return function(Entity $entity) use ($data, $passwordField, $saltField, $that) {
47
            $password = $entity->get($passwordField);
48
49
            if (!$password) {
50
                return true;
51
            }
52
53
            $salt = $entity->get($saltField);
54
            $newSalt = $that->possibleGenSalt($salt, $entity, $saltField);
55
56
            $passwordHash = $this->encoder->encodePassword($password, $salt);
57
58
            $doGenerateHash = $that->doGenerateHash($data, $entity, $passwordField, $password, $newSalt);
59
60
            if ($doGenerateHash) {
61
                $entity->set($passwordField, $passwordHash);
62
            }
63
            return true;
64
        };
65
    }
66
67
    /**
68
     * Constructor.
69
     *
70
     * @param PasswordEncoderInterface $encoder
71
     * the encoder to use, defaults to BCryptPasswordEncoder if null is given
72
     */
73
    public function __construct(PasswordEncoderInterface $encoder = null)
74
    {
75
        $this->encoder = $encoder;
76
        if ($this->encoder === null) {
77
            $this->encoder = new BCryptPasswordEncoder(13);
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Securi...r\BCryptPasswordEncoder has been deprecated with message: since Symfony 4.3, use NativePasswordEncoder instead

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
78
        }
79
    }
80
81
    /**
82
     * Generates a new salt if the given salt is null.
83
     *
84
     * @param string $salt
85
     * the salt to override if null
86
     * @param Entity
87
     * the entity getting the new salt
88
     * @param string $saltField
89
     * the field holding the salt in the entity
90
     *
91
     * @return boolean
92
     * true if a new salt was generated
93
     */
94
    public function possibleGenSalt(&$salt, Entity $entity, $saltField)
95
    {
96
        if (!$salt) {
97
            $salt = $this->getSalt(40);
98
            $entity->set($saltField, $salt);
99
            return true;
100
        }
101
        return false;
102
    }
103
104
    /**
105
     * Determines whether the entity needs a new hash generated.
106
     *
107
     * @param AbstractData $data
108
     * the CRUDlex data instance of the user entity
109
     * @param Entity $entity
110
     * the entity
111
     * @param string $passwordField
112
     * the field holding the password hash in the entity
113
     * @param string $password
114
     * the current password hash
115
     * @param boolean $newSalt
116
     * whether a new password hash salt was generated
117
     *
118
     * @return boolean
119
     * true if the entity needs a new hash
120
     */
121
    public function doGenerateHash(AbstractData $data, Entity $entity, $passwordField, $password, $newSalt)
122
    {
123
        $doGenerateHash = true;
124
        $id = $entity->get('id');
125
        if ($id !== null) {
126
            $oldEntity = $data->get($entity->get('id'));
127
            $doGenerateHash = $oldEntity->get($passwordField) !== $password || $newSalt;
128
        }
129
        return $doGenerateHash;
130
    }
131
132
    /**
133
     * Generates a random salt of the given length.
134
     *
135
     * @param int $len
136
     * the desired length
137
     *
138
     * @return string
139
     * a random salt of the given length
140
     */
141
    public function getSalt($len)
142
    {
143
        $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`~!@#$%^&*()-=_+';
144
        $l = strlen($chars) - 1;
145
        $str = '';
146
        for ($i = 0; $i < $len; ++$i) {
147
            $str .= $chars[mt_rand(0, $l)];
148
        }
149
        return $str;
150
    }
151
152
    /**
153
     * Setups CRUDlex with some events so the passwords get salted and
154
     * hashed properly.
155
     *
156
     * @param AbstractData $data
157
     * the AbstractData instance managing the users
158
     *
159
     * @param string $passwordField
160
     * the Entity fieldname of the password hash
161
     *
162
     * @param string $saltField
163
     * the Entity fieldname of the password hash salt
164
     */
165
    public function addEvents(AbstractData $data, $passwordField = 'password', $saltField = 'salt')
166
    {
167
168
        $that = $this;
169
        $saltGenFunction = function(Entity $entity) use ($saltField, $that) {
170
            $salt = $that->getSalt(40);
171
            $entity->set($saltField, $salt);
172
            return true;
173
        };
174
175
        $data->getEvents()->push('before', 'create', $saltGenFunction);
176
177
        $pwHashFunction = $this->getPWHashFunction($data, $passwordField, $saltField);
178
179
        $data->getEvents()->push('before', 'create', $pwHashFunction);
180
        $data->getEvents()->push('before', 'update', $pwHashFunction);
181
182
    }
183
184
}
185