Completed
Push — master ( dc0e70...13ce84 )
by Mārtiņš
02:12
created

Identification::checkCookieExpireTime()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 13
cts 13
cp 1
rs 9.0856
c 0
b 0
f 0
cc 2
eloc 14
nc 2
nop 1
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\PasswordNotMatch;
12
use Palladium\Exception\CompromisedCookie;
13
use Palladium\Exception\DenialOfServiceAttempt;
14
use Palladium\Exception\IdentityExpired;
15
use Palladium\Contract\CanCreateMapper;
16
use Psr\Log\LoggerInterface;
17
18
class Identification
19
{
20
21
    private $mapperFactory;
22
    private $logger;
23
24
25 9
    public function __construct(CanCreateMapper $mapperFactory, LoggerInterface $logger)
26
    {
27 9
        $this->mapperFactory = $mapperFactory;
28 9
        $this->logger = $logger;
29 9
    }
30
31
32
    /**
33
     * @param string $password
34
     *
35
     * @return Palladium\Entity\CookieIdentity
36
     */
37 2
    public function loginWithPassword(Entity\EmailIdentity $identity, $password)
38
    {
39 2
        if ($identity->matchPassword($password) === false) {
40 1
            $this->logWrongPasswordWarning($identity, [
41 1
                'email' => $identity->getEmailAddress(),
42 1
                'key' => md5($password),
43
            ]);
44
45 1
            throw new PasswordNotMatch;
46
        }
47
48 1
        $this->registerUsageOfIdentity($identity);
49 1
        $cookie = $this->createCookieIdentity($identity);
50
51 1
        $this->logger->info('login successful', [
52
            'input' => [
53 1
                'email' => $identity->getEmailAddress(),
54
            ],
55
            'user' => [
56 1
                'account' => $identity->getAccountId(),
57 1
                'identity' => $identity->getId(),
58
            ],
59
        ]);
60
61 1
        return $cookie;
62
    }
63
64
65 1
    private function registerUsageOfIdentity(Entity\Identity $identity)
66
    {
67 1
        $identity->setLastUsed(time());
68
69 1
        $mapper = $this->mapperFactory->create(Mapper\Identity::class);
70 1
        $mapper->store($identity);
71 1
    }
72
73
74 1
    private function createCookieIdentity(Entity\EmailIdentity $identity)
75
    {
76 1
        $cookie = new Entity\CookieIdentity;
77 1
        $mapper = $this->mapperFactory->create(Mapper\CookieIdentity::class);
78
79 1
        $cookie->setAccountId($identity->getAccountId());
80 1
        $cookie->generateNewSeries();
81
82 1
        $cookie->generateNewKey();
83 1
        $cookie->setStatus(Entity\Identity::STATUS_ACTIVE);
84 1
        $cookie->setExpiresOn(time() + Entity\Identity::COOKIE_LIFESPAN);
85
86
87 1
        $parentId = $identity->getParentId();
88
89 1
        if (null === $parentId) {
90 1
            $parentId = $identity->getId();
91
        }
92
93 1
        $cookie->setParentId($parentId);
94
95 1
        $mapper->store($cookie);
96
97 1
        return $cookie;
98
    }
99
100
101
    /**
102
     * @param string @key
103
     *
104
     * @throws \Palladium\Exception\CompromisedCookie if key does not match
105
     * @throws \Palladium\Exception\IdentityExpired if cookie is too old
106
     *
107
     * @return Palladium\Entity\CookieIdentity
108
     */
109 3
    public function loginWithCookie(Entity\CookieIdentity $identity, $key)
110
    {
111 3
        $this->checkCookieExpireTime($identity);
112 2
        $this->checkCookieKey($identity, $key);
113
114 1
        $identity->generateNewKey();
115 1
        $identity->setLastUsed(time());
116 1
        $identity->setExpiresOn(time() + Entity\Identity::COOKIE_LIFESPAN);
117
118 1
        $mapper = $this->mapperFactory->create(Mapper\CookieIdentity::class);
119 1
        $mapper->store($identity);
120
121 1
        $this->logExpectedBehaviour($identity, 'cookie updated');
122
123 1
        return $identity;
124
    }
125
126
127
    /**
128
     * @param string $key
129
     */
130 1
    public function logout(Entity\CookieIdentity $identity, $key)
131
    {
132 1
        $this->checkCookieExpireTime($identity);
133 1
        $this->checkCookieKey($identity, $key);
134
135 1
        $identity->setStatus(Entity\Identity::STATUS_DISCARDED);
136
137 1
        $mapper = $this->mapperFactory->create(Mapper\CookieIdentity::class);
138 1
        $mapper->store($identity);
139
140 1
        $this->logExpectedBehaviour($identity, 'logout successful');
141 1
    }
142
143
144 4
    private function checkCookieExpireTime(Entity\CookieIdentity $identity)
145
    {
146 4
        if ($identity->getExpiresOn() < time()) {
147 1
            $identity->setStatus(Entity\Identity::STATUS_EXPIRED);
148
149 1
            $this->logger->info('cookie expired', [
150
                'input' => [
151 1
                    'account' => $identity->getAccountId(),
152 1
                    'series' => $identity->getSeries(),
153 1
                    'key' => $identity->getKey(),
154
                ],
155
                'user' => [
156 1
                    'account' => $identity->getAccountId(),
157 1
                    'identity' => $identity->getId(),
158
                ],
159
            ]);
160
161 1
            $mapper = $this->mapperFactory->create(Mapper\CookieIdentity::class);
162 1
            $mapper->store($identity);
163
164 1
            throw new IdentityExpired;
165
        }
166 3
    }
167
168
169
    /**
170
     * Verify that the cookie based identity matches the key and,
171
     * if verification is failed, disable this given identity
172
     *
173
     * @param string $key
174
     * @throws \Palladium\Exception\CompromisedCookie if key does not match
175
     */
176 3
    private function checkCookieKey(Entity\CookieIdentity $identity, $key)
177
    {
178 3
        if ($identity->matchKey($key) === true) {
179 2
            return;
180
        }
181
182 1
        $identity->setStatus(Entity\Identity::STATUS_BLOCKED);
183
184 1
        $mapper = $this->mapperFactory->create(Mapper\CookieIdentity::class);
185 1
        $mapper->store($identity);
186
187 1
        $this->logCookieError($identity, 'compromised cookie');
188
189 1
        throw new CompromisedCookie;
190
    }
191
192
193 1
    public function discardIdentityCollection(Entity\IdentityCollection $list)
194
    {
195 1
        foreach ($list as $identity) {
196 1
            $identity->setStatus(Entity\Identity::STATUS_DISCARDED);
197
        }
198
199 1
        $mapper = $this->mapperFactory->create(Mapper\IdentityCollection::class);
200 1
        $mapper->store($list);
201 1
    }
202
203
204
    public function blockIdentity(Entity\Identity $identity)
205
    {
206
        $identity->setStatus(Entity\Identity::STATUS_BLOCKED);
207
208
        $mapper = $this->mapperFactory->create(Mapper\Identity::class);
209
        $mapper->store($identity);
210
    }
211
212
213
    /**
214
     * @param string $oldPassword
215
     * @param string $newPassword
216
     */
217 2
    public function changePassword(Entity\EmailIdentity $identity, $oldPassword, $newPassword)
218
    {
219 2
        $mapper = $this->mapperFactory->create(Mapper\EmailIdentity::class);
220
221 2
        if ($identity->matchPassword($oldPassword) === false) {
222 1
            $this->logWrongPasswordWarning($identity, [
223 1
                'account' => $identity->getAccountId(),
224 1
                'old-key' => md5($oldPassword),
225 1
                'new-key' => md5($newPassword),
226
            ]);
227
228 1
            throw new PasswordNotMatch;
229
        }
230
231 1
        $identity->setPassword($newPassword);
232 1
        $mapper->store($identity);
233
234 1
        $this->logExpectedBehaviour($identity, 'password changed');
235 1
    }
236
237
238
    /**
239
     * @param string $message
240
     */
241 1
    private function logCookieError(Entity\CookieIdentity $identity, $message)
242
    {
243 1
        $this->logger->error($message, [
244
            'input' => [
245 1
                'account' => $identity->getAccountId(),
246 1
                'series' => $identity->getSeries(),
247 1
                'key' => $identity->getKey(),
248
            ],
249
            'user' => [
250 1
                'account' => $identity->getAccountId(),
251 1
                'identity' => $identity->getId(),
252
            ],
253
        ]);
254 1
    }
255
256
257
    /**
258
     * @param array $input
259
     */
260 2
    private function logWrongPasswordWarning(Entity\EmailIdentity $identity, $input)
261
    {
262 2
        $this->logger->warning('wrong password', [
263 2
            'input' => $input,
264
            'user' => [
265 2
                'account' => $identity->getAccountId(),
266 2
                'identity' => $identity->getId(),
267
            ],
268
        ]);
269 2
    }
270
271
272 3
    private function logExpectedBehaviour(Entity\Identity $identity, $message)
273
    {
274 3
        $this->logger->info($message, [
275
            'user' => [
276 3
                'account' => $identity->getAccountId(),
277 3
                'identity' => $identity->getId(),
278
            ],
279
        ]);
280 3
    }
281
}
282