Completed
Push — master ( cd27e7...dbd271 )
by Mārtiņš
04:09
created

Identification::logExpectedBehaviour()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 2
crap 1
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 11
    public function __construct(CanCreateMapper $mapperFactory, LoggerInterface $logger)
26
    {
27 11
        $this->mapperFactory = $mapperFactory;
28 11
        $this->logger = $logger;
29 11
    }
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
                'identifier' => $identity->getIdentifier(),
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
                'identifier' => $identity->getIdentifier(),
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 1
        $mapper->store($cookie);
87
88 1
        return $cookie;
89
    }
90
91
92
    /**
93
     * @param string @key
94
     *
95
     * @return Palladium\Entity\CookieIdentity
96
     */
97 4
    public function loginWithCookie(Entity\CookieIdentity $identity, $key)
98
    {
99 4
        if ($identity->getId() === null) {
100 1
            $this->logCookieError($identity, 'denial of service');
101 1
            throw new DenialOfServiceAttempt;
102
        }
103
104 3
        $mapper = $this->mapperFactory->create(Mapper\CookieIdentity::class);
105
106 3
        if ($identity->getExpiresOn() < time()) {
107 1
            $identity->setStatus(Entity\Identity::STATUS_EXPIRED);
108 1
            $mapper->store($identity);
109 1
            $this->logger->info('cookie expired', [
110
                'input' => [
111 1
                    'account' => $identity->getAccountId(),
112 1
                    'series' => $identity->getSeries(),
113 1
                    'key' => $identity->getKey(),
114
                ],
115
                'user' => [
116 1
                    'account' => $identity->getAccountId(),
117 1
                    'identity' => $identity->getId(),
118
                ],
119
            ]);
120
121 1
            throw new IdentityExpired;
122
        }
123
124 2
        $this->checkCookieKey($identity, $key);
125
126 1
        $identity->generateNewKey();
127 1
        $identity->setLastUsed(time());
128 1
        $identity->setExpiresOn(time() + Entity\Identity::COOKIE_LIFESPAN);
129
130 1
        $mapper->store($identity);
131 1
        $this->logExpectedBehaviour($identity, 'cookie updated');
132
133 1
        return $identity;
134
    }
135
136
137
    /**
138
     * @param string $key
139
     */
140 2
    public function logout(Entity\CookieIdentity $identity, $key)
141
    {
142 2
        if ($identity->getId() === null) {
143 1
            $this->logCookieError($identity, 'denial of service');
144 1
            throw new DenialOfServiceAttempt;
145
        }
146
147 1
        $this->checkCookieKey($identity, $key);
148 1
        $identity->setStatus(Entity\Identity::STATUS_DISCARDED);
149
150 1
        $mapper = $this->mapperFactory->create(Mapper\CookieIdentity::class);
151 1
        $mapper->store($identity);
152
153 1
        $this->logExpectedBehaviour($identity, 'logout successful');
154 1
    }
155
156
157
    /**
158
     * Verify that the cookie based identity matches the key and,
159
     * if verification is failed, disable this given identity
160
     *
161
     * @param string $key
162
     * @throws \Palladium\Exception\CompromisedCookie if key does not match
163
     */
164 3
    private function checkCookieKey(Entity\CookieIdentity $identity, $key)
165
    {
166 3
        if ($identity->matchKey($key) === true) {
167 2
            return;
168
        }
169
170 1
        $identity->setStatus(Entity\Identity::STATUS_BLOCKED);
171
172 1
        $mapper = $this->mapperFactory->create(Mapper\CookieIdentity::class);
173 1
        $mapper->store($identity);
174
175 1
        $this->logCookieError($identity, 'compromised cookie');
176
177 1
        throw new CompromisedCookie;
178
    }
179
180
181 1
    public function discardIdentities(Entity\IdentityCollection $list)
182
    {
183 1
        foreach ($list as $identity) {
184 1
            $identity->setStatus(Entity\Identity::STATUS_DISCARDED);
185
        }
186
187 1
        $mapper = $this->mapperFactory->create(Mapper\IdentityCollection::class);
188 1
        $mapper->store($list);
189 1
    }
190
191
192
    /**
193
     * @param string $oldPassword
194
     * @param string $newPassword
195
     */
196 2
    public function changePassword(Entity\EmailIdentity $identity, $oldPassword, $newPassword)
197
    {
198 2
        $mapper = $this->mapperFactory->create(Mapper\EmailIdentity::class);
199
200 2
        if ($identity->matchPassword($oldPassword) === false) {
201 1
            $this->logWrongPasswordWarning($identity, [
202 1
                'account' => $identity->getAccountId(),
203 1
                'old-key' => md5($oldPassword),
204 1
                'new-key' => md5($newPassword),
205
            ]);
206
207 1
            throw new PasswordNotMatch;
208
        }
209
210 1
        $identity->setPassword($newPassword);
211 1
        $mapper->store($identity);
212
213 1
        $this->logExpectedBehaviour($identity, 'password changed');
214 1
    }
215
216
217
    /**
218
     * @param string $message
219
     */
220 3
    private function logCookieError(Entity\CookieIdentity $identity, $message)
221
    {
222 3
        $this->logger->error($message, [
223
            'input' => [
224 3
                'account' => $identity->getAccountId(),
225 3
                'series' => $identity->getSeries(),
226 3
                'key' => $identity->getKey(),
227
            ],
228
            'user' => [
229 3
                'account' => $identity->getAccountId(),
230 3
                'identity' => $identity->getId(),
231
            ],
232
        ]);
233 3
    }
234
235
236
    /**
237
     * @param array $input
238
     */
239 2
    private function logWrongPasswordWarning(Entity\EmailIdentity $identity, $input)
240
    {
241 2
        $this->logger->warning('wrong password', [
242 2
            'input' => $input,
243
            'user' => [
244 2
                'account' => $identity->getAccountId(),
245 2
                'identity' => $identity->getId(),
246
            ],
247
        ]);
248 2
    }
249
250
251 3
    private function logExpectedBehaviour(Entity\Identity $identity, $message)
252
    {
253 3
        $this->logger->info($message, [
254
            'user' => [
255 3
                'account' => $identity->getAccountId(),
256 3
                'identity' => $identity->getId(),
257
            ],
258
        ]);
259 3
    }
260
}
261