Completed
Pull Request — 1.x (#637)
by Daniel
14:10 queued 04:01
created

Db::updateUserPasswordHash()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 2 Features 0
Metric Value
c 2
b 2
f 0
dl 0
loc 10
rs 9.4285
cc 2
eloc 7
nc 2
nop 3
1
<?php
2
3
namespace ZfcUser\Authentication\Adapter;
4
5
use DateTime;
6
use Zend\Authentication\Result as AuthenticationResult;
7
use Zend\ServiceManager\ServiceManagerAwareInterface;
8
use Zend\ServiceManager\ServiceManager;
9
use Zend\Crypt\Password\Bcrypt;
10
use Zend\Session\Container as SessionContainer;
11
use ZfcUser\Authentication\Adapter\AdapterChainEvent as AuthEvent;
12
use ZfcUser\Mapper\User as UserMapperInterface;
13
use ZfcUser\Options\AuthenticationOptionsInterface;
14
15
class Db extends AbstractAdapter
16
{
17
    /**
18
     * @var UserMapperInterface
19
     */
20
    protected $mapper;
21
22
    /**
23
     * @var closure / invokable object
24
     */
25
    protected $credentialPreprocessor;
26
27
    /**
28
     * @var ServiceManager
29
     */
30
    protected $serviceManager;
31
32
    /**
33
     * @var AuthenticationOptionsInterface
34
     */
35
    protected $options;
36
37
    /**
38
     * Called when user id logged out
39
     * @param  AuthEvent $e event passed
40
     */
41
    public function logout(AuthEvent $e)
42
    {
43
        $this->getStorage()->clear();
44
    }
45
46
    public function authenticate(AuthEvent $e)
47
    {
48
        if ($this->isSatisfied()) {
49
            $storage = $this->getStorage()->read();
50
            $e->setIdentity($storage['identity'])
51
              ->setCode(AuthenticationResult::SUCCESS)
52
              ->setMessages(array('Authentication successful.'));
53
            return;
54
        }
55
56
        $identity   = $e->getRequest()->getPost()->get('identity');
57
        $credential = $e->getRequest()->getPost()->get('credential');
58
        $credential = $this->preProcessCredential($credential);
59
        $userObject = null;
60
61
        // Cycle through the configured identity sources and test each
62
        $fields = $this->getOptions()->getAuthIdentityFields();
63
        while (!is_object($userObject) && count($fields) > 0) {
64
            $mode = array_shift($fields);
65
            switch ($mode) {
66
                case 'username':
67
                    $userObject = $this->getMapper()->findByUsername($identity);
68
                    break;
69
                case 'email':
70
                    $userObject = $this->getMapper()->findByEmail($identity);
71
                    break;
72
            }
73
        }
74
75
        if (!$userObject) {
76
            $e->setCode(AuthenticationResult::FAILURE_IDENTITY_NOT_FOUND)
77
              ->setMessages(array('A record with the supplied identity could not be found.'));
78
            $this->setSatisfied(false);
79
            return false;
80
        }
81
82
        if ($this->getOptions()->getEnableUserState()) {
83
            // Don't allow user to login if state is not in allowed list
84
            if (!in_array($userObject->getState(), $this->getOptions()->getAllowedLoginStates())) {
85
                $e->setCode(AuthenticationResult::FAILURE_UNCATEGORIZED)
86
                  ->setMessages(array('A record with the supplied identity is not active.'));
87
                $this->setSatisfied(false);
88
                return false;
89
            }
90
        }
91
92
        $bcrypt = new Bcrypt();
93
        $bcrypt->setCost($this->getOptions()->getPasswordCost());
94
        if (!$bcrypt->verify($credential, $userObject->getPassword())) {
95
            // Password does not match
96
            $e->setCode(AuthenticationResult::FAILURE_CREDENTIAL_INVALID)
97
              ->setMessages(array('Supplied credential is invalid.'));
98
            $this->setSatisfied(false);
99
            return false;
100
        }
101
102
        // regen the id
103
        $session = new SessionContainer($this->getStorage()->getNameSpace());
104
        $session->getManager()->regenerateId();
105
106
        // Success!
107
        $e->setIdentity($userObject->getId());
108
        // Update user's password hash if the cost parameter has changed
109
        $this->updateUserPasswordHash($userObject, $credential, $bcrypt);
110
        $this->setSatisfied(true);
111
        $storage = $this->getStorage()->read();
112
        $storage['identity'] = $e->getIdentity();
113
        $this->getStorage()->write($storage);
114
        $e->setCode(AuthenticationResult::SUCCESS)
115
          ->setMessages(array('Authentication successful.'));
116
    }
117
118
    protected function updateUserPasswordHash($userObject, $password, $bcrypt)
119
    {
120
        $hash = explode('$', $userObject->getPassword());
121
        if ($hash[2] === $bcrypt->getCost()) {
122
            return;
123
        }
124
        $userObject->setPassword($bcrypt->create($password));
125
        $this->getMapper()->update($userObject);
126
        return $this;
127
    }
128
129
    public function preprocessCredential($credential)
130
    {
131
        $processor = $this->getCredentialPreprocessor();
132
        if (is_callable($processor)) {
133
            return $processor($credential);
134
        }
135
        return $credential;
136
    }
137
138
    /**
139
     * getMapper
140
     *
141
     * @return UserMapperInterface
142
     */
143
    public function getMapper()
144
    {
145
        if (null === $this->mapper) {
146
            $this->mapper = $this->getServiceManager()->get('zfcuser_user_mapper');
147
        }
148
        return $this->mapper;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->mapper; of type object|array adds the type array to the return on line 148 which is incompatible with the return type documented by ZfcUser\Authentication\Adapter\Db::getMapper of type ZfcUser\Mapper\User.
Loading history...
149
    }
150
151
    /**
152
     * setMapper
153
     *
154
     * @param UserMapperInterface $mapper
155
     * @return Db
156
     */
157
    public function setMapper(UserMapperInterface $mapper)
158
    {
159
        $this->mapper = $mapper;
160
        return $this;
161
    }
162
163
    /**
164
     * Get credentialPreprocessor.
165
     *
166
     * @return \callable
167
     */
168
    public function getCredentialPreprocessor()
169
    {
170
        return $this->credentialPreprocessor;
171
    }
172
173
    /**
174
     * Set credentialPreprocessor.
175
     *
176
     * @param $credentialPreprocessor the value to be set
177
     */
178
    public function setCredentialPreprocessor($credentialPreprocessor)
179
    {
180
        $this->credentialPreprocessor = $credentialPreprocessor;
181
        return $this;
182
    }
183
184
    /**
185
     * Retrieve service manager instance
186
     *
187
     * @return ServiceManager
188
     */
189
    public function getServiceManager()
190
    {
191
        return $this->serviceManager;
192
    }
193
194
    /**
195
     * Set service manager instance
196
     *
197
     * @param ServiceManager $locator
198
     * @return void
199
     */
200
    public function setServiceManager(ServiceManager $serviceManager)
201
    {
202
        $this->serviceManager = $serviceManager;
203
    }
204
205
    /**
206
     * @param AuthenticationOptionsInterface $options
207
     */
208
    public function setOptions(AuthenticationOptionsInterface $options)
209
    {
210
        $this->options = $options;
211
    }
212
213
    /**
214
     * @return AuthenticationOptionsInterface
215
     */
216
    public function getOptions()
217
    {
218
        if (!$this->options instanceof AuthenticationOptionsInterface) {
219
            $this->setOptions($this->getServiceManager()->get('zfcuser_module_options'));
220
        }
221
        return $this->options;
222
    }
223
}
224