Passed
Pull Request — master (#1)
by John
02:11
created

LEClient::getAccount()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 6
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
51
    private $email;
52
53
    /**
54
     * Initiates the LetsEncrypt main client.
55
     *
56
     * @param array $email The array of strings containing e-mail addresses. Only used in this function when
57
     *                                creating a new account.
58
     * @param string|bool $acmeURL ACME URL, can be string or one of predefined values: LE_STAGING or LE_PRODUCTION.
59
     *                                Defaults to LE_STAGING. Can also pass true/false for staging/production
60
     * @param LoggerInterface $logger PSR-3 compatible logger
61
     * @param ClientInterface|null $httpClient you can pass a custom client used for HTTP requests, if null is passed
62
     *                                one will be created
63
     * @param CertificateStorageInterface|null $storage service for certificates. If not supplied, a default
64
     *                                storage object will retain certificates in the local filesystem in a directory
65
     *                                called certificates in the current working directory
66
     * @param DNSValidatorInterface|null $dnsValidator service for checking DNS challenges. By default, this will use
67
     *                                Google's DNS over HTTPs service, which should insulate you from cached entries,
68
     *                                but this can be swapped for 'NativeDNS' or other alternative implementation
69
     */
70
    public function __construct(
71
        $email,
72
        $acmeURL = LEClient::LE_STAGING,
73
        LoggerInterface $logger = null,
74
        ClientInterface $httpClient = null,
75
        CertificateStorageInterface $storage = null,
76
        DNSValidatorInterface $dnsValidator = null
77
    ) {
78
        $this->log = $logger ?? new NullLogger();
79
80
        $this->initBaseUrl($acmeURL);
81
82
        $this->httpClient = $httpClient ?? new Client();
83
84
        $this->storage = $storage ?? new FilesystemCertificateStorage();
85
        $this->dns = $dnsValidator ?? new DNSOverHTTPS();
86
        $this->sleep = new Sleep;
87
        $this->email = $email;
88
    }
89
90
    private function initBaseUrl($acmeURL)
91
    {
92
        if (is_bool($acmeURL)) {
93
            $this->baseURL = $acmeURL ? LEClient::LE_STAGING : LEClient::LE_PRODUCTION;
94
        } elseif (is_string($acmeURL)) {
95
            $this->baseURL = $acmeURL;
96
        } else {
97
            throw new LogicException('acmeURL must be set to string or bool (legacy)');
98
        }
99
    }
100
101
    public function getBaseUrl()
102
    {
103
        return $this->baseURL;
104
    }
105
106
    /**
107
     * Inject alternative DNS resolver for testing
108
     * @param DNSValidatorInterface $dns
109
     */
110
    public function setDNS(DNSValidatorInterface $dns)
111
    {
112
        $this->dns = $dns;
113
    }
114
115
    /**
116
     * Inject alternative sleep service for testing
117
     * @param Sleep $sleep
118
     */
119
    public function setSleep(Sleep $sleep)
120
    {
121
        $this->sleep = $sleep;
122
    }
123
124
    private function getConnector()
125
    {
126
        if (!isset($this->connector)) {
127
            $this->connector = new LEConnector($this->log, $this->httpClient, $this->baseURL, $this->storage);
128
129
            //we need to initialize an account before using the connector
130
            $this->getAccount();
131
        }
132
133
        return $this->connector;
134
    }
135
136
    /**
137
     * Returns the LetsEncrypt account used in the current client.
138
     *
139
     * @return LEAccount    The LetsEncrypt Account instance used by the client.
140
     */
141
    public function getAccount()
142
    {
143
        if (!isset($this->account)) {
144
            $this->account = new LEAccount($this->getConnector(), $this->log, $this->email, $this->storage);
145
        }
146
        return $this->account;
147
    }
148
149
    /**
150
     * Returns a LetsEncrypt order. If an order exists, this one is returned. If not, a new order is created and
151
     * returned.
152
     *
153
     * @param string $basename The base name for the order. Preferable the top domain (example.org). Will be the
154
     *                          directory in which the keys are stored. Used for the CommonName in the certificate as
155
     *                          well.
156
     * @param array $domains The array of strings containing the domain names on the certificate.
157
     * @param string $keyType Type of the key we want to use for certificate. Can be provided in ALGO-SIZE format
158
     *                          (ex. rsa-4096 or ec-256) or simple "rsa" and "ec" (using default sizes)
159
     * @param string $notBefore A date string formatted like 0000-00-00T00:00:00Z (yyyy-mm-dd hh:mm:ss) at which the
160
     *                          certificate becomes valid. Defaults to the moment the order is finalized. (optional)
161
     * @param string $notAfter A date string formatted like 0000-00-00T00:00:00Z (yyyy-mm-dd hh:mm:ss) until which the
162
     *                          certificate is valid. Defaults to 90 days past the moment the order is finalized.
163
     *                          (optional)
164
     *
165
     * @return LEOrder  The LetsEncrypt Order instance which is either retrieved or created.
166
     */
167
    public function getOrCreateOrder($basename, $domains, $keyType = 'rsa-4096', $notBefore = '', $notAfter = '')
168
    {
169
        $this->log->info("LEClient::getOrCreateOrder($basename,...)");
170
171
        $order = new LEOrder($this->getConnector(), $this->storage, $this->log, $this->dns, $this->sleep);
172
        $order->loadOrder($basename, $domains, $keyType, $notBefore, $notAfter);
173
174
        return $order;
175
    }
176
}
177