Passed
Push — master ( f876b2...207b2b )
by Simon
01:38
created

HaveIBeenPwnedService::checkPwnedPassword()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 12
nc 1
nop 1
dl 0
loc 21
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
namespace Firesphere\HaveIBeenPwned\Services;
4
5
use GuzzleHttp\Client;
6
use GuzzleHttp\Exception\GuzzleException;
7
use Psr\Http\Message\ResponseInterface;
8
use SilverStripe\Core\Config\Configurable;
9
use SilverStripe\Core\Convert;
10
use SilverStripe\Core\Injector\Injector;
11
use SilverStripe\Security\Member;
12
13
/**
14
 * Class HaveIBeenPwnedService
15
 * @package Firesphere\HaveIBeenPwned\Services
16
 */
17
class HaveIBeenPwnedService
18
{
19
    use Configurable;
20
21
    /**
22
     * Api endpoint emails
23
     */
24
    const PWND_URL = 'https://haveibeenpwned.com/api/';
25
26
    /**
27
     * API endpoint passwords
28
     */
29
    const PWND_API_URL = 'https://api.pwnedpasswords.com/';
30
31
    /**
32
     * API Version
33
     */
34
    const API_VERSION = '2';
35
36
    /**
37
     * Useragent
38
     */
39
    const USER_AGENT = 'Firesphere-HaveIBeenPwned-checker/1.0';
40
41
    /**
42
     * @config
43
     * @var bool
44
     */
45
    private static $allow_pwnd = false;
46
47
    /**
48
     * @config
49
     * @var bool
50
     */
51
    private static $save_pwnd = true;
52
53
    /**
54
     * @var array
55
     */
56
    protected $args;
57
58
    /**
59
     * HaveIBeenPwnedService constructor.
60
     * @param array $args
61
     */
62
    public function __construct($args = [])
63
    {
64
        $this->args = $args;
65
    }
66
67
    /**
68
     * @param string $pwd
69
     * @return int
70
     * @throws GuzzleException
71
     */
72
    public function checkPwnedPassword($pwd)
73
    {
74
        $this->args['base_uri'] = static::PWND_API_URL;
75
76
        $sha = sha1($pwd);
77
        $shaStart = substr($sha, 0, 5);
78
        $shaEnd = substr($sha, 5);
79
        /** @var Client $client */
80
        $client = Injector::inst()->createWithArgs(Client::class, [$this->args]);
81
        $result = $client->request(
82
            'GET',
83
            'range/' . $shaStart,
84
            [
85
                'headers' => [
86
                    'user-agent'  => static::USER_AGENT,
87
                    'api-version' => static::API_VERSION
88
                ]
89
            ]
90
        );
91
92
        return $this->checkList($result, $shaEnd);
93
    }
94
95
    /**
96
     * @param ResponseInterface $result
97
     * @param $shaEnd
98
     * @return int
99
     */
100
    private function checkList($result, $shaEnd)
101
    {
102
        $count = 0;
103
        $shaEnd = strtoupper($shaEnd);
104
        $suffixes = explode("\n", $result->getBody());
105
        foreach ($suffixes as $suffix) {
106
            list($suffix, $pwnCount) = explode(':', trim($suffix));
107
            if ($suffix === $shaEnd) {
108
                $count += (int)$pwnCount;
109
                break;
110
            }
111
        }
112
113
        return $count;
114
    }
115
116
    /**
117
     * @param Member $member
118
     * @return string
119
     * @throws GuzzleException
120
     */
121
    public function checkPwnedEmail($member)
122
    {
123
        $this->args['base_uri'] = static::PWND_URL;
124
        $uniqueField = Member::config()->get('unique_identifier_field');
125
        $account = $member->{$uniqueField};
126
127
        /** @var Client $client */
128
        $client = Injector::inst()->createWithArgs(Client::class, [$this->args]);
129
130
        $result = $client->request(
131
            'GET',
132
            'breachedaccount/' . $account . '?truncateResponse=true',
133
            [
134
                'headers' => [
135
                    'user-agent'  => static::USER_AGENT,
136
                    'api-version' => static::API_VERSION
137
                ]
138
            ]
139
        );
140
141
        return $this->checkBreaches($result);
142
    }
143
144
    /**
145
     * @param ResponseInterface $result
146
     * @return string
147
     */
148
    private function checkBreaches($result)
149
    {
150
        $body = $result->getBody();
151
152
        $sites = [];
153
154
        $breaches = Convert::json2array($body);
155
        foreach ($breaches as $breach) {
156
            if (!empty($breach['Name'])) {
157
                $sites[] = $breach['Name'];
158
            }
159
        }
160
161
        if (count($sites)) {
162
            return implode(', ', $sites);
163
        }
164
165
        return '';
166
    }
167
168
    /**
169
     * @return array
170
     */
171
    public function getArgs()
172
    {
173
        return $this->args;
174
    }
175
176
    /**
177
     * @param array $args
178
     */
179
    public function setArgs($args)
180
    {
181
        $this->args = $args;
182
    }
183
}
184