Db::setServiceManager()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace ZfcUser\Authentication\Adapter;
4
5
use InvalidArgumentException;
6
use Zend\Authentication\Result as AuthenticationResult;
7
use Zend\Crypt\Password\Bcrypt;
8
use Zend\ServiceManager\ServiceManager;
9
use Zend\ServiceManager\ServiceManagerAwareInterface;
10
use Zend\Session\Container as SessionContainer;
11
use ZfcUser\Authentication\Adapter\AdapterChainEvent as AuthenticationEvent;
12
use ZfcUser\Entity\UserInterface as UserEntity;
13
use ZfcUser\Mapper\HydratorInterface as Hydrator;
14
use ZfcUser\Mapper\UserInterface as UserMapper;
15
use ZfcUser\Options\AuthenticationOptionsInterface as AuthenticationOptions;
16
17
class Db extends AbstractAdapter implements ServiceManagerAwareInterface
18
{
19
    /**
20
     * @var UserMapper
21
     */
22
    protected $mapper;
23
24
    /**
25
     * @var Hydrator
26
     */
27
    protected $hydrator;
28
29
    /**
30
     * @var callable
31
     */
32
    protected $credentialPreprocessor;
33
34
    /**
35
     * @var ServiceManager
36
     */
37
    protected $serviceManager;
38
39
    /**
40
     * @var AuthenticationOptions
41
     */
42
    protected $options;
43
44
    /**
45
     * Called when user id logged out
46
     */
47
    public function logout()
48
    {
49
        $this->getStorage()->clear();
50
    }
51
52
    public function authenticate(AuthenticationEvent $event)
53
    {
54
        if ($this->isSatisfied()) {
55
            $storage = $this->getStorage()->read();
56
            $event->setIdentity($storage['identity'])
57
                  ->setCode(AuthenticationResult::SUCCESS)
58
                  ->setMessages(array('Authentication successful.'));
59
            return;
60
        }
61
62
        $identity   = $event->getRequest()->getPost()->get('identity');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Zend\Stdlib\RequestInterface as the method getPost() does only exist in the following implementations of said interface: Zend\Http\PhpEnvironment\Request, Zend\Http\Request.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
63
        $credential = $event->getRequest()->getPost()->get('credential');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Zend\Stdlib\RequestInterface as the method getPost() does only exist in the following implementations of said interface: Zend\Http\PhpEnvironment\Request, Zend\Http\Request.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
64
        $credential = $this->preProcessCredential($credential);
65
        $userObject = null;
66
67
        // Cycle through the configured identity sources and test each
68
        $fields = $this->getOptions()->getAuthIdentityFields();
69
        while (!is_object($userObject) && count($fields) > 0) {
70
            $mode = array_shift($fields);
71
            switch ($mode) {
72
                case 'username':
73
                    $userObject = $this->getMapper()->findByUsername($identity);
74
                    break;
75
                case 'email':
76
                    $userObject = $this->getMapper()->findByEmail($identity);
77
                    break;
78
            }
79
        }
80
81
        if (!$userObject) {
82
            $event->setCode(AuthenticationResult::FAILURE_IDENTITY_NOT_FOUND)
83
                  ->setMessages(array('A record with the supplied identity could not be found.'));
84
            $this->setSatisfied(false);
85
            return false;
86
        }
87
88
        if ($this->getOptions()->getEnableUserState()) {
89
            // Don't allow user to login if state is not in allowed list
90
            if (!in_array($userObject->getState(), $this->getOptions()->getAllowedLoginStates())) {
91
                $event->setCode(AuthenticationResult::FAILURE_UNCATEGORIZED)
92
                      ->setMessages(array('A record with the supplied identity is not active.'));
93
                $this->setSatisfied(false);
94
                return false;
95
            }
96
        }
97
98
        $cryptoService = $this->getHydrator()->getCryptoService();
99
        if (!$cryptoService->verify($credential, $userObject->getPassword())) {
100
            // Password does not match
101
            $event->setCode(AuthenticationResult::FAILURE_CREDENTIAL_INVALID)
102
                  ->setMessages(array('Supplied credential is invalid.'));
103
            $this->setSatisfied(false);
104
            return false;
105
        } elseif ($cryptoService instanceof Bcrypt) {
106
            // Update user's password hash if the cost parameter has changed
107
            $this->updateUserPasswordHash($userObject, $credential, $cryptoService);
108
        }
109
110
        // regen the id
111
        SessionContainer::getDefaultManager()->regenerateId();
112
113
        // Success!
114
        $event->setIdentity($userObject->getId());
115
116
        $this->setSatisfied(true);
117
        $storage = $this->getStorage()->read();
118
        $storage['identity'] = $event->getIdentity();
119
        $this->getStorage()->write($storage);
120
        $event->setCode(AuthenticationResult::SUCCESS)
121
              ->setMessages(array('Authentication successful.'));
122
    }
123
124
    protected function updateUserPasswordHash(UserEntity $user, $password, Bcrypt $bcrypt)
125
    {
126
        $hash = explode('$', $user->getPassword());
127
        if ($hash[2] === $bcrypt->getCost()) {
128
            return;
129
        }
130
        $user = $this->getHydrator()->hydrate(compact('password'), $user);
131
        $this->getMapper()->update($user);
132
    }
133
134
    public function preprocessCredential($credential)
135
    {
136
        if (is_callable($this->credentialPreprocessor)) {
137
            return call_user_func($this->credentialPreprocessor, $credential);
138
        }
139
        return $credential;
140
    }
141
142
    /**
143
     * getMapper
144
     *
145
     * @return UserMapper
146
     */
147
    public function getMapper()
148
    {
149
        if (!$this->mapper instanceof UserMapper) {
150
            $this->setMapper($this->serviceManager->get('zfcuser_user_mapper'));
0 ignored issues
show
Documentation introduced by
$this->serviceManager->get('zfcuser_user_mapper') is of type object|array, but the function expects a object<ZfcUser\Mapper\UserInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
151
        }
152
        return $this->mapper;
153
    }
154
155
    /**
156
     * setMapper
157
     *
158
     * @param UserMapper $mapper
159
     * @return Db
160
     */
161
    public function setMapper(UserMapper $mapper)
162
    {
163
        $this->mapper = $mapper;
164
        return $this;
165
    }
166
167
    /**
168
     * Lazy-loads a hydrator from the service manager
169
     *
170
     * @return Hydrator
171
     */
172
    public function getHydrator()
173
    {
174
        if (!$this->hydrator instanceof Hydrator) {
175
            $this->setHydrator($this->serviceManager->get('zfcuser_user_hydrator'));
0 ignored issues
show
Documentation introduced by
$this->serviceManager->g...zfcuser_user_hydrator') is of type object|array, but the function expects a object<ZfcUser\Mapper\HydratorInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
176
        }
177
        return $this->hydrator;
178
    }
179
180
    /**
181
     * Set the hydrator
182
     *
183
     * @param Hydrator $hydrator
184
     */
185
    public function setHydrator(Hydrator $hydrator)
186
    {
187
        $this->hydrator = $hydrator;
188
    }
189
190
    /**
191
     * Get credentialPreprocessor.
192
     *
193
     * @return callable
194
     */
195
    public function getCredentialPreprocessor()
196
    {
197
        return $this->credentialPreprocessor;
198
    }
199
200
    /**
201
     * Set credentialPreprocessor.
202
     *
203
     * @param  callable $credentialPreprocessor the value to be set
204
     * @throws InvalidArgumentException when argument is not callable
205
     */
206
    public function setCredentialPreprocessor($credentialPreprocessor)
207
    {
208
        if (!is_callable($credentialPreprocessor)) {
209
            $message = sprintf(
210
                "Credential Preprocessor must be callable, [%s] given",
211
                gettype($credentialPreprocessor)
212
            );
213
            throw new InvalidArgumentException($message);
214
        }
215
        $this->credentialPreprocessor = $credentialPreprocessor;
216
    }
217
218
    /**
219
     * Retrieve service manager instance
220
     *
221
     * @return ServiceManager
222
     */
223
    public function getServiceManager()
224
    {
225
        return $this->serviceManager;
226
    }
227
228
    /**
229
     * Set service manager instance
230
     *
231
     * @param ServiceManager $locator
232
     * @return void
233
     */
234
    public function setServiceManager(ServiceManager $serviceManager)
235
    {
236
        $this->serviceManager = $serviceManager;
237
    }
238
239
    /**
240
     * @param AuthenticationOptions $options
241
     */
242
    public function setOptions(AuthenticationOptions $options)
243
    {
244
        $this->options = $options;
245
    }
246
247
    /**
248
     * @return AuthenticationOptions
249
     */
250
    public function getOptions()
251
    {
252
        if (!$this->options instanceof AuthenticationOptions) {
253
            $this->setOptions($this->serviceManager->get('zfcuser_module_options'));
0 ignored issues
show
Documentation introduced by
$this->serviceManager->g...fcuser_module_options') is of type object|array, but the function expects a object<ZfcUser\Options\A...cationOptionsInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
254
        }
255
        return $this->options;
256
    }
257
}
258