LEClient::getConnector()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 10
ccs 5
cts 5
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Zwartpet\PHPCertificateToolbox;
4
5
use Zwartpet\PHPCertificateToolbox\DNSValidator\DNSOverHTTPS;
6
use Zwartpet\PHPCertificateToolbox\DNSValidator\DNSValidatorInterface;
7
use Zwartpet\PHPCertificateToolbox\DNSValidator\NativeDNS;
8
use Zwartpet\PHPCertificateToolbox\Exception\LogicException;
9
use GuzzleHttp\Client;
10
use GuzzleHttp\ClientInterface;
11
use Psr\Log\LoggerInterface;
12
use Psr\Log\NullLogger;
13
14
/**
15
 * Main LetsEncrypt Client class, works as a framework for the LEConnector, LEAccount, LEOrder and
16
 * LEAuthorization classes.
17
 *
18
 * @author     Youri van Weegberg <[email protected]>
19
 * @copyright  2018 Youri van Weegberg
20
 * @license    https://opensource.org/licenses/mit-license.php  MIT License
21
 */
22
class LEClient
23
{
24
    const LE_PRODUCTION = 'https://acme-v02.api.letsencrypt.org';
25
    const LE_STAGING = 'https://acme-staging-v02.api.letsencrypt.org';
26
27
    /** @var LEConnector */
28
    private $connector;
29
30
    /** @var LEAccount */
31
    private $account;
32
33
    private $baseURL;
34
35
    /** @var LoggerInterface */
36
    private $log;
37
38
    /** @var ClientInterface */
39
    private $httpClient;
40
41
    /** @var DNSValidatorInterface */
42
    private $dns;
43
44
    /** @var Sleep */
45
    private $sleep;
46
47
    /** @var CertificateStorageInterface */
48
    private $storage;
49
50
    /** @var AccountStorageInterface */
51
    private $accountStorage;
52
53
    private $email;
54
55
    /**
56
     * Initiates the LetsEncrypt main client.
57
     *
58
     * @param array $email The array of strings containing e-mail addresses. Only used in this function when
59
     *                                creating a new account.
60
     * @param string|bool $acmeURL ACME URL, can be string or one of predefined values: LE_STAGING or LE_PRODUCTION.
61
     *                                Defaults to LE_STAGING. Can also pass true/false for staging/production
62
     * @param LoggerInterface $logger PSR-3 compatible logger
63
     * @param ClientInterface|null $httpClient you can pass a custom client used for HTTP requests, if null is passed
64
     *                                one will be created
65
     * @param CertificateStorageInterface|null $storage service for certificates. If not supplied, a default
66
     *                                storage object will retain certificates in the local filesystem in a directory
67
     *                                called certificates in the current working directory
68
     * @param AccountStorageInterface|null $accountStorage same as storage but for the account
69
     * @param DNSValidatorInterface|null $dnsValidator service for checking DNS challenges. By default, this will use
70
     *                                Google's DNS over HTTPs service, which should insulate you from cached entries,
71
     *                                but this can be swapped for 'NativeDNS' or other alternative implementation
72
     */
73 8
    public function __construct(
74
        $email,
75
        $acmeURL = LEClient::LE_STAGING,
76
        LoggerInterface $logger = null,
77
        ClientInterface $httpClient = null,
78
        CertificateStorageInterface $storage = null,
79
        AccountStorageInterface $accountStorage = null,
80
        DNSValidatorInterface $dnsValidator = null
81
    ) {
82 8
        $this->log = $logger ?? new NullLogger();
83
84 8
        $this->initBaseUrl($acmeURL);
85
86 6
        $this->httpClient = $httpClient ?? new Client();
87
88 6
        $this->storage = $storage ?? new FilesystemCertificateStorage();
89 6
        $this->accountStorage = $accountStorage ?? new FilesystemAccountStorage();
90 6
        $this->dns = $dnsValidator ?? new DNSOverHTTPS();
91 6
        $this->sleep = new Sleep;
92 6
        $this->email = $email;
93 6
    }
94
95 8
    private function initBaseUrl($acmeURL)
96
    {
97 8
        if (is_bool($acmeURL)) {
98 4
            $this->baseURL = $acmeURL ? LEClient::LE_STAGING : LEClient::LE_PRODUCTION;
99 4
        } elseif (is_string($acmeURL)) {
100 2
            $this->baseURL = $acmeURL;
101
        } else {
102 2
            throw new LogicException('acmeURL must be set to string or bool (legacy)');
103
        }
104 6
    }
105
106 2
    public function getBaseUrl()
107
    {
108 2
        return $this->baseURL;
109
    }
110
111
    /**
112
     * Inject alternative DNS resolver for testing
113
     * @param DNSValidatorInterface $dns
114
     */
115 2
    public function setDNS(DNSValidatorInterface $dns)
116
    {
117 2
        $this->dns = $dns;
118 2
    }
119
120
    /**
121
     * Inject alternative sleep service for testing
122
     * @param Sleep $sleep
123
     */
124 2
    public function setSleep(Sleep $sleep)
125
    {
126 2
        $this->sleep = $sleep;
127 2
    }
128
129 2
    private function getConnector()
130
    {
131 2
        if (!isset($this->connector)) {
132 2
            $this->connector = new LEConnector($this->log, $this->httpClient, $this->baseURL, $this->accountStorage);
133
134
            //we need to initialize an account before using the connector
135 2
            $this->getAccount();
136
        }
137
138 2
        return $this->connector;
139
    }
140
141
    /**
142
     * Returns the LetsEncrypt account used in the current client.
143
     *
144
     * @return LEAccount    The LetsEncrypt Account instance used by the client.
145
     */
146 2
    public function getAccount()
147
    {
148 2
        if (!isset($this->account)) {
149 2
            $this->account = new LEAccount($this->getConnector(), $this->log, $this->email, $this->accountStorage);
150
        }
151 2
        return $this->account;
152
    }
153
154
    /**
155
     * Returns a LetsEncrypt order. If an order exists, this one is returned. If not, a new order is created and
156
     * returned.
157
     *
158
     * @param string $basename The base name for the order. Preferable the top domain (example.org). Will be the
159
     *                          directory in which the keys are stored. Used for the CommonName in the certificate as
160
     *                          well.
161
     * @param array $domains The array of strings containing the domain names on the certificate.
162
     * @param string $keyType Type of the key we want to use for certificate. Can be provided in ALGO-SIZE format
163
     *                          (ex. rsa-4096 or ec-256) or simple "rsa" and "ec" (using default sizes)
164
     * @param string $notBefore A date string formatted like 0000-00-00T00:00:00Z (yyyy-mm-dd hh:mm:ss) at which the
165
     *                          certificate becomes valid. Defaults to the moment the order is finalized. (optional)
166
     * @param string $notAfter A date string formatted like 0000-00-00T00:00:00Z (yyyy-mm-dd hh:mm:ss) until which the
167
     *                          certificate is valid. Defaults to 90 days past the moment the order is finalized.
168
     *                          (optional)
169
     *
170
     * @return LEOrder  The LetsEncrypt Order instance which is either retrieved or created.
171
     */
172 2
    public function getOrCreateOrder($basename, $domains, $keyType = 'rsa-4096', $notBefore = '', $notAfter = '')
173
    {
174 2
        $this->log->info("LEClient::getOrCreateOrder($basename,...)");
175
176 2
        $order = new LEOrder(
177 2
            $this->getConnector(),
178 2
            $this->storage,
179 2
            $this->accountStorage,
180 2
            $this->log,
181 2
            $this->dns,
182 2
            $this->sleep
183
        );
184 2
        $order->loadOrder($basename, $domains, $keyType, $notBefore, $notAfter);
185
186 2
        return $order;
187
    }
188
}
189