AuthorizationService   A
last analyzed

Complexity

Total Complexity 27

Size/Duplication

Total Lines 177
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 27
eloc 83
c 1
b 0
f 0
dl 0
loc 177
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getPendingHttpAuthorizations() 0 17 3
A verifyDnsChallenge() 0 10 1
A getPendingDnsAuthorizations() 0 19 3
A verifyHttpChallenge() 0 5 1
A __construct() 0 3 1
B verifyPendingDnsAuthorization() 0 34 8
B verifyPendingHttpAuthorization() 0 34 8
A getAuthorizations() 0 5 1
A updateAuthorization() 0 10 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the LetsEncrypt ACME client.
7
 *
8
 * @author    Ivanov Aleksandr <[email protected]>
9
 * @copyright 2019-2020
10
 * @license   https://github.com/misantron/letsencrypt-client/blob/master/LICENSE MIT License
11
 */
12
13
namespace LetsEncrypt\Service;
14
15
use LetsEncrypt\Entity\Account;
16
use LetsEncrypt\Entity\Authorization;
17
use LetsEncrypt\Http\ConnectorAwareTrait;
18
use LetsEncrypt\Http\DnsCheckerInterface;
19
use LetsEncrypt\Http\GooglePublicDNS;
20
21
class AuthorizationService
22
{
23
    use ConnectorAwareTrait;
24
25
    /**
26
     * @var DnsCheckerInterface
27
     */
28
    private $dnsChecker;
29
30
    public function __construct(DnsCheckerInterface $dnsChecker = null)
31
    {
32
        $this->dnsChecker = $dnsChecker ?? new GooglePublicDNS();
33
    }
34
35
    /**
36
     * @return Authorization[]
37
     */
38
    public function getAuthorizations(Account $account, array $urls): array
39
    {
40
        return array_map(function (string $url) use ($account) {
41
            return $this->updateAuthorization($account, $url);
42
        }, $urls);
43
    }
44
45
    /**
46
     * @param Authorization[] $authorizations
47
     */
48
    public function getPendingHttpAuthorizations(array $authorizations, string $digest): array
49
    {
50
        $pendingAuthorizations = [];
51
52
        foreach ($authorizations as $authorization) {
53
            $challenge = $authorization->getHttpChallenge();
54
            if ($challenge->isPending()) {
55
                $keyAuthorization = $challenge->getToken() . '.' . $digest;
56
                $pendingAuthorizations[] = [
57
                    'identifier' => $authorization->getIdentifierValue(),
58
                    'filename' => $challenge->getToken(),
59
                    'content' => $keyAuthorization,
60
                ];
61
            }
62
        }
63
64
        return $pendingAuthorizations;
65
    }
66
67
    /**
68
     * @param Authorization[] $authorizations
69
     */
70
    public function getPendingDnsAuthorizations(array $authorizations, string $digest): array
71
    {
72
        $pendingAuthorizations = [];
73
74
        foreach ($authorizations as $authorization) {
75
            $challenge = $authorization->getDnsChallenge();
76
            if ($challenge->isPending()) {
77
                $keyAuthorization = $challenge->getToken() . '.' . $digest;
78
                $pendingAuthorizations[] = [
79
                    'identifier' => $authorization->getIdentifierValue(),
80
                    'dnsDigest' => $this->connector
81
                        ->getSigner()
82
                        ->getBase64Encoder()
83
                        ->hashEncode($keyAuthorization),
84
                ];
85
            }
86
        }
87
88
        return $pendingAuthorizations;
89
    }
90
91
    /**
92
     * @param Authorization[] $authorizations
93
     */
94
    public function verifyPendingHttpAuthorization(Account $account, array $authorizations, string $identifier): bool
95
    {
96
        $digest = $this->connector->getSigner()->kty($account->getPrivateKeyPath());
97
98
        foreach ($authorizations as $authorization) {
99
            if ($authorization->isPending() && $authorization->isIdentifierValueEqual($identifier)) {
100
                $challenge = $authorization->getHttpChallenge();
101
                if ($challenge->isPending()) {
102
                    $keyAuthorization = $challenge->getToken() . '.' . $digest;
103
104
                    if ($this->verifyHttpChallenge($identifier, $challenge->getToken(), $keyAuthorization)) {
105
                        $payload = [
106
                            'keyAuthorization' => $keyAuthorization,
107
                        ];
108
                        $response = $this->connector->signedKIDRequest(
109
                            $account->getUrl(),
110
                            $challenge->getUrl(),
111
                            $payload,
112
                            $account->getPrivateKeyPath()
113
                        );
114
                        if ($response->isStatusOk()) {
115
                            while ($authorization->isPending()) {
116
                                sleep(1);
117
                                $authorization = $this->updateAuthorization($account, $authorization->getUrl());
118
                            }
119
120
                            return true;
121
                        }
122
                    }
123
                }
124
            }
125
        }
126
127
        return false;
128
    }
129
130
    /**
131
     * @param Authorization[] $authorizations
132
     */
133
    public function verifyPendingDnsAuthorization(Account $account, array $authorizations, string $identifier): bool
134
    {
135
        $digest = $this->connector->getSigner()->kty($account->getPrivateKeyPath());
136
137
        foreach ($authorizations as $authorization) {
138
            if ($authorization->isPending() && $authorization->isIdentifierValueEqual($identifier)) {
139
                $challenge = $authorization->getDnsChallenge();
140
                if ($challenge->isPending()) {
141
                    $keyAuthorization = $challenge->getToken() . '.' . $digest;
142
143
                    if ($this->verifyDnsChallenge($identifier, $keyAuthorization)) {
144
                        $payload = [
145
                            'keyAuthorization' => $keyAuthorization,
146
                        ];
147
                        $response = $this->connector->signedKIDRequest(
148
                            $account->getUrl(),
149
                            $challenge->getUrl(),
150
                            $payload,
151
                            $account->getPrivateKeyPath()
152
                        );
153
                        if ($response->isStatusOk()) {
154
                            while ($authorization->isPending()) {
155
                                sleep(1);
156
                                $authorization = $this->updateAuthorization($account, $authorization->getUrl());
157
                            }
158
159
                            return true;
160
                        }
161
                    }
162
                }
163
            }
164
        }
165
166
        return false;
167
    }
168
169
    private function verifyHttpChallenge(string $domain, string $token, string $key): bool
170
    {
171
        $response = $this->connector->get($domain . '/.well-known/acme-challenge/' . $token);
172
173
        return trim($response->getRawContent()) === $key;
174
    }
175
176
    private function verifyDnsChallenge(string $domain, string $keyAuthorization): bool
177
    {
178
        $dnsDigest = $this->connector
179
            ->getSigner()
180
            ->getBase64Encoder()
181
            ->hashEncode($keyAuthorization);
182
183
        return $this->dnsChecker
184
            ->setConnector($this->connector)
0 ignored issues
show
Bug introduced by
The method setConnector() does not exist on LetsEncrypt\Http\DnsCheckerInterface. It seems like you code against a sub-type of LetsEncrypt\Http\DnsCheckerInterface such as LetsEncrypt\Http\GooglePublicDNS. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

184
            ->/** @scrutinizer ignore-call */ setConnector($this->connector)
Loading history...
185
            ->verify($domain, $dnsDigest);
186
    }
187
188
    private function updateAuthorization(Account $account, string $url): Authorization
189
    {
190
        $response = $this->connector->signedKIDRequest(
191
            $account->getUrl(),
192
            $url,
193
            [],
194
            $account->getPrivateKeyPath()
195
        );
196
197
        return new Authorization($response->getDecodedContent(), $url);
198
    }
199
}
200