Completed
Push — master ( d22c34...cd27e7 )
by Mārtiņš
02:11
created

Identification::discardIdentities()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

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