Completed
Push — master ( 13ce84...26c17f )
by Mārtiņš
02:10
created

Identification::loginWithPassword()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 26
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 26
ccs 13
cts 13
cp 1
rs 8.8571
c 1
b 0
f 0
cc 2
eloc 15
nc 2
nop 2
crap 2
1
<?php
2
3
namespace Palladium\Service;
4
5
/**
6
 * Retrieval and handling of identities for registered users
7
 */
8
9
use Palladium\Mapper as Mapper;
10
use Palladium\Entity as Entity;
11
use Palladium\Exception\PasswordMismatch;
12
use Palladium\Exception\CompromisedCookie;
13
use Palladium\Exception\IdentityExpired;
14
use Palladium\Contract\CanCreateMapper;
15
use Psr\Log\LoggerInterface;
16
17
class Identification
18
{
19
20
    private $mapperFactory;
21
    private $logger;
22
23
24 10
    public function __construct(CanCreateMapper $mapperFactory, LoggerInterface $logger)
25
    {
26 10
        $this->mapperFactory = $mapperFactory;
27 10
        $this->logger = $logger;
28 10
    }
29
30
31
    /**
32
     * @param string $password
33
     *
34
     * @return Palladium\Entity\CookieIdentity
35
     */
36 2
    public function loginWithPassword(Entity\EmailIdentity $identity, $password)
37
    {
38 2
        if ($identity->matchPassword($password) === false) {
39 1
            $this->logWrongPasswordNotice($identity, [
40 1
                'email' => $identity->getEmailAddress(),
41 1
                'key' => md5($password),
42
            ]);
43
44 1
            throw new PasswordMismatch;
45
        }
46
47 1
        $this->registerUsageOfIdentity($identity);
48 1
        $cookie = $this->createCookieIdentity($identity);
49
50 1
        $this->logger->info('login successful', [
51
            'input' => [
52 1
                'email' => $identity->getEmailAddress(),
53
            ],
54
            'user' => [
55 1
                'account' => $identity->getAccountId(),
56 1
                'identity' => $identity->getId(),
57
            ],
58
        ]);
59
60 1
        return $cookie;
61
    }
62
63
64 1
    private function registerUsageOfIdentity(Entity\Identity $identity)
65
    {
66 1
        $identity->setLastUsed(time());
67
68 1
        $mapper = $this->mapperFactory->create(Mapper\Identity::class);
69 1
        $mapper->store($identity);
70 1
    }
71
72
73 1
    private function createCookieIdentity(Entity\EmailIdentity $identity)
74
    {
75 1
        $cookie = new Entity\CookieIdentity;
76 1
        $mapper = $this->mapperFactory->create(Mapper\CookieIdentity::class);
77
78 1
        $cookie->setAccountId($identity->getAccountId());
79 1
        $cookie->generateNewSeries();
80
81 1
        $cookie->generateNewKey();
82 1
        $cookie->setStatus(Entity\Identity::STATUS_ACTIVE);
83 1
        $cookie->setExpiresOn(time() + Entity\Identity::COOKIE_LIFESPAN);
84
85
86 1
        $parentId = $identity->getParentId();
87
88 1
        if (null === $parentId) {
89 1
            $parentId = $identity->getId();
90
        }
91
92 1
        $cookie->setParentId($parentId);
93
94 1
        $mapper->store($cookie);
95
96 1
        return $cookie;
97
    }
98
99
100
    /**
101
     * @param string @key
102
     *
103
     * @throws \Palladium\Exception\CompromisedCookie if key does not match
104
     * @throws \Palladium\Exception\IdentityExpired if cookie is too old
105
     *
106
     * @return Palladium\Entity\CookieIdentity
107
     */
108 3
    public function loginWithCookie(Entity\CookieIdentity $identity, $key)
109
    {
110 3
        $this->checkCookieExpireTime($identity);
111 2
        $this->checkCookieKey($identity, $key);
112
113 1
        $identity->generateNewKey();
114 1
        $identity->setLastUsed(time());
115 1
        $identity->setExpiresOn(time() + Entity\Identity::COOKIE_LIFESPAN);
116
117 1
        $mapper = $this->mapperFactory->create(Mapper\CookieIdentity::class);
118 1
        $mapper->store($identity);
119
120 1
        $this->logExpectedBehaviour($identity, 'cookie updated');
121
122 1
        return $identity;
123
    }
124
125
126
    /**
127
     * @param string $key
128
     */
129 1
    public function logout(Entity\CookieIdentity $identity, $key)
130
    {
131 1
        $this->checkCookieExpireTime($identity);
132 1
        $this->checkCookieKey($identity, $key);
133
134 1
        $identity->setStatus(Entity\Identity::STATUS_DISCARDED);
135
136 1
        $mapper = $this->mapperFactory->create(Mapper\CookieIdentity::class);
137 1
        $mapper->store($identity);
138
139 1
        $this->logExpectedBehaviour($identity, 'logout successful');
140 1
    }
141
142
143 4 View Code Duplication
    private function checkCookieExpireTime(Entity\CookieIdentity $identity)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
144
    {
145 4
        if ($identity->getExpiresOn() < time()) {
146 1
            $identity->setStatus(Entity\Identity::STATUS_EXPIRED);
147
148 1
            $this->logger->info('cookie expired', [
149
                'input' => [
150 1
                    'account' => $identity->getAccountId(),
151 1
                    'series' => $identity->getSeries(),
152 1
                    'key' => $identity->getKey(),
153
                ],
154
                'user' => [
155 1
                    'account' => $identity->getAccountId(),
156 1
                    'identity' => $identity->getId(),
157
                ],
158
            ]);
159
160 1
            $mapper = $this->mapperFactory->create(Mapper\CookieIdentity::class);
161 1
            $mapper->store($identity);
162
163 1
            throw new IdentityExpired;
164
        }
165 3
    }
166
167
168
    /**
169
     * Verify that the cookie based identity matches the key and,
170
     * if verification is failed, disable this given identity
171
     *
172
     * @param string $key
173
     * @throws \Palladium\Exception\CompromisedCookie if key does not match
174
     */
175 3 View Code Duplication
    private function checkCookieKey(Entity\CookieIdentity $identity, $key)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
176
    {
177 3
        if ($identity->matchKey($key) === true) {
178 2
            return;
179
        }
180
181 1
        $identity->setStatus(Entity\Identity::STATUS_BLOCKED);
182
183 1
        $mapper = $this->mapperFactory->create(Mapper\CookieIdentity::class);
184 1
        $mapper->store($identity);
185
186 1
        $this->logger->warning('compromised cookie', [
187
            'input' => [
188 1
                'account' => $identity->getAccountId(),
189 1
                'series' => $identity->getSeries(),
190 1
                'key' => $identity->getKey(),
191
            ],
192
            'user' => [
193 1
                'account' => $identity->getAccountId(),
194 1
                'identity' => $identity->getId(),
195
            ],
196
        ]);
197
198 1
        throw new CompromisedCookie;
199
    }
200
201
202 1
    public function discardIdentityCollection(Entity\IdentityCollection $list)
203
    {
204 1
        foreach ($list as $identity) {
205 1
            $identity->setStatus(Entity\Identity::STATUS_DISCARDED);
206
        }
207
208 1
        $mapper = $this->mapperFactory->create(Mapper\IdentityCollection::class);
209 1
        $mapper->store($list);
210 1
    }
211
212
213 1
    public function blockIdentity(Entity\Identity $identity)
214
    {
215 1
        $identity->setStatus(Entity\Identity::STATUS_BLOCKED);
216
217 1
        $mapper = $this->mapperFactory->create(Mapper\Identity::class);
218 1
        $mapper->store($identity);
219 1
    }
220
221
222
    /**
223
     * @param string $oldPassword
224
     * @param string $newPassword
225
     */
226 2
    public function changePassword(Entity\EmailIdentity $identity, $oldPassword, $newPassword)
227
    {
228 2
        $mapper = $this->mapperFactory->create(Mapper\EmailIdentity::class);
229
230 2
        if ($identity->matchPassword($oldPassword) === false) {
231 1
            $this->logWrongPasswordNotice($identity, [
232 1
                'account' => $identity->getAccountId(),
233 1
                'old-key' => md5($oldPassword),
234 1
                'new-key' => md5($newPassword),
235
            ]);
236
237 1
            throw new PasswordMismatch;
238
        }
239
240 1
        $identity->setPassword($newPassword);
241 1
        $mapper->store($identity);
242
243 1
        $this->logExpectedBehaviour($identity, 'password changed');
244 1
    }
245
246
247
    /**
248
     * @param array $input
249
     */
250 2
    private function logWrongPasswordNotice(Entity\EmailIdentity $identity, $input)
251
    {
252 2
        $this->logger->notice('wrong password', [
253 2
            'input' => $input,
254
            'user' => [
255 2
                'account' => $identity->getAccountId(),
256 2
                'identity' => $identity->getId(),
257
            ],
258
        ]);
259 2
    }
260
261
262 3
    private function logExpectedBehaviour(Entity\Identity $identity, $message)
263
    {
264 3
        $this->logger->info($message, [
265
            'user' => [
266 3
                'account' => $identity->getAccountId(),
267 3
                'identity' => $identity->getId(),
268
            ],
269
        ]);
270 3
    }
271
}
272