Completed
Pull Request — 1.x (#627)
by Dylan
08:40
created

Db::authenticate()   C

Complexity

Conditions 10
Paths 25

Size

Total Lines 71
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 2 Features 0
Metric Value
c 2
b 2
f 0
dl 0
loc 71
rs 5.9513
cc 10
eloc 49
nc 25
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace ZfcUser\Authentication\Adapter;
4
5
use Zend\Authentication\Result as AuthenticationResult;
6
use Zend\Crypt\Password\Bcrypt;
7
use Zend\Session\Container as SessionContainer;
8
use ZfcUser\Authentication\Adapter\AdapterChainEvent as AuthEvent;
9
use ZfcUser\Mapper\UserInterface as UserMapperInterface;
10
use ZfcUser\Options\AuthenticationOptionsInterface;
11
12
class Db extends AbstractAdapter
13
{
14
    /**
15
     * @var UserMapperInterface
16
     */
17
    protected $mapper;
18
19
    /**
20
     * @var closure / invokable object
21
     */
22
    protected $credentialPreprocessor;
23
24
    /**
25
     * @var AuthenticationOptionsInterface
26
     */
27
    protected $options;
28
29
    public function __construct(UserMapperInterface $mapper, AuthenticationOptionsInterface $options)
30
    {
31
        $this->mapper = $mapper;
32
        $this->options = $options;
33
    }
34
35
    /**
36
     * Called when user id logged out
37
     * @param  AuthEvent $e event passed
38
     */
39
    public function logout(AuthEvent $e)
40
    {
41
        $this->getStorage()->clear();
42
    }
43
44
    public function authenticate(AuthEvent $e)
45
    {
46
        if ($this->isSatisfied()) {
47
            $storage = $this->getStorage()->read();
48
            $e->setIdentity($storage['identity'])
49
              ->setCode(AuthenticationResult::SUCCESS)
50
              ->setMessages(array('Authentication successful.'));
51
            return;
52
        }
53
54
        $identity   = $e->getRequest()->getPost()->get('identity');
55
        $credential = $e->getRequest()->getPost()->get('credential');
56
        $credential = $this->preProcessCredential($credential);
57
        $userObject = null;
58
59
        // Cycle through the configured identity sources and test each
60
        $fields = $this->getOptions()->getAuthIdentityFields();
61
        while (!is_object($userObject) && count($fields) > 0) {
62
            $mode = array_shift($fields);
63
            switch ($mode) {
64
                case 'username':
65
                    $userObject = $this->getMapper()->findByUsername($identity);
66
                    break;
67
                case 'email':
68
                    $userObject = $this->getMapper()->findByEmail($identity);
69
                    break;
70
            }
71
        }
72
73
        if (!$userObject) {
74
            $e->setCode(AuthenticationResult::FAILURE_IDENTITY_NOT_FOUND)
75
              ->setMessages(array('A record with the supplied identity could not be found.'));
76
            $this->setSatisfied(false);
77
            return false;
78
        }
79
80
        if ($this->getOptions()->getEnableUserState()) {
81
            // Don't allow user to login if state is not in allowed list
82
            if (!in_array($userObject->getState(), $this->getOptions()->getAllowedLoginStates())) {
83
                $e->setCode(AuthenticationResult::FAILURE_UNCATEGORIZED)
84
                  ->setMessages(array('A record with the supplied identity is not active.'));
85
                $this->setSatisfied(false);
86
                return false;
87
            }
88
        }
89
90
        $bcrypt = new Bcrypt();
91
        $bcrypt->setCost($this->getOptions()->getPasswordCost());
92
        if (!$bcrypt->verify($credential, $userObject->getPassword())) {
93
            // Password does not match
94
            $e->setCode(AuthenticationResult::FAILURE_CREDENTIAL_INVALID)
95
              ->setMessages(array('Supplied credential is invalid.'));
96
            $this->setSatisfied(false);
97
            return false;
98
        }
99
100
        // regen the id
101
        $session = new SessionContainer($this->getStorage()->getNameSpace());
102
        $session->getManager()->regenerateId();
103
104
        // Success!
105
        $e->setIdentity($userObject->getId());
106
        // Update user's password hash if the cost parameter has changed
107
        $this->updateUserPasswordHash($userObject, $credential, $bcrypt);
108
        $this->setSatisfied(true);
109
        $storage = $this->getStorage()->read();
110
        $storage['identity'] = $e->getIdentity();
111
        $this->getStorage()->write($storage);
112
        $e->setCode(AuthenticationResult::SUCCESS)
113
          ->setMessages(array('Authentication successful.'));
114
    }
115
116
    protected function updateUserPasswordHash($userObject, $password, $bcrypt)
117
    {
118
        $hash = explode('$', $userObject->getPassword());
119
        if ($hash[2] === $bcrypt->getCost()) {
120
            return;
121
        }
122
        $userObject->setPassword($bcrypt->create($password));
123
        $this->getMapper()->update($userObject);
124
        return $this;
125
    }
126
127
    public function preprocessCredential($credential)
128
    {
129
        $processor = $this->getCredentialPreprocessor();
130
        if (is_callable($processor)) {
131
            return $processor($credential);
132
        }
133
        return $credential;
134
    }
135
136
    /**
137
     * getMapper
138
     *
139
     * @return UserMapperInterface
140
     */
141
    public function getMapper()
142
    {
143
        return $this->mapper;
144
    }
145
146
    /**
147
     * Get credentialPreprocessor.
148
     *
149
     * @return \callable
150
     */
151
    public function getCredentialPreprocessor()
152
    {
153
        return $this->credentialPreprocessor;
154
    }
155
156
    /**
157
     * Set credentialPreprocessor.
158
     *
159
     * @param $credentialPreprocessor the value to be set
160
     */
161
    public function setCredentialPreprocessor($credentialPreprocessor)
162
    {
163
        $this->credentialPreprocessor = $credentialPreprocessor;
164
        return $this;
165
    }
166
167
    /**
168
     * @return AuthenticationOptionsInterface
169
     */
170
    public function getOptions()
171
    {
172
        return $this->options;
173
    }
174
}
175