Completed
Push — master ( 793070...34c9e2 )
by John
11:13
created
examples/exampleDNSInit.php 3 patches
Indentation   +24 added lines, -24 removed lines patch added patch discarded remove patch
@@ -22,36 +22,36 @@
 block discarded – undo
22 22
 try {
23 23
 // Initiating the client instance. In this case using the staging server (argument 2) and outputting all status
24 24
 // and debug information (argument 3).
25
-    $client = new LEClient($email, true, $logger);
25
+	$client = new LEClient($email, true, $logger);
26 26
 // Initiating the order instance. The keys and certificate will be stored in /example.org/ (argument 1) and the
27 27
 // domains in the array (argument 2) will be on the certificate.
28
-    $order = $client->getOrCreateOrder($basename, $domains);
28
+	$order = $client->getOrCreateOrder($basename, $domains);
29 29
 // Check whether there are any authorizations pending. If that is the case, try to verify the pending authorizations.
30
-    if (!$order->allAuthorizationsValid()) {
31
-        // Get the DNS challenges from the pending authorizations.
32
-        $pending = $order->getPendingAuthorizations(LEOrder::CHALLENGE_TYPE_DNS);
33
-        // Walk the list of pending authorization DNS challenges.
34
-        if (!empty($pending)) {
35
-            foreach ($pending as $challenge) {
36
-                // For the purpose of this example, a fictitious functions creates or updates the ACME challenge DNS
37
-                // record for this domain.
38
-                //setDNSRecord($challenge['identifier'], $challenge['DNSDigest']);
39
-                printf(
40
-                    "DNS Challengage identifier = %s digest = %s\n",
41
-                    $challenge['identifier'],
42
-                    $challenge['DNSDigest']
43
-                );
44
-            }
45
-        }
46
-    }
30
+	if (!$order->allAuthorizationsValid()) {
31
+		// Get the DNS challenges from the pending authorizations.
32
+		$pending = $order->getPendingAuthorizations(LEOrder::CHALLENGE_TYPE_DNS);
33
+		// Walk the list of pending authorization DNS challenges.
34
+		if (!empty($pending)) {
35
+			foreach ($pending as $challenge) {
36
+				// For the purpose of this example, a fictitious functions creates or updates the ACME challenge DNS
37
+				// record for this domain.
38
+				//setDNSRecord($challenge['identifier'], $challenge['DNSDigest']);
39
+				printf(
40
+					"DNS Challengage identifier = %s digest = %s\n",
41
+					$challenge['identifier'],
42
+					$challenge['DNSDigest']
43
+				);
44
+			}
45
+		}
46
+	}
47 47
 }
48 48
 catch (\Exception $e) {
49
-    echo $e->getMessage()."\n";
50
-    echo $e->getTraceAsString()."\n";
49
+	echo $e->getMessage()."\n";
50
+	echo $e->getTraceAsString()."\n";
51 51
 
52
-    echo "\nDiagnostic logs\n";
53
-    $logger->dumpConsole();
54
-    exit;
52
+	echo "\nDiagnostic logs\n";
53
+	$logger->dumpConsole();
54
+	exit;
55 55
 }
56 56
 
57 57
 echo "\nDiagnostic logs\n";
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -1,7 +1,7 @@  discard block
 block discarded – undo
1 1
 <?php
2 2
 namespace Elphin\LEClient;
3 3
 
4
-require_once(__DIR__.'/../vendor/autoload.php');
4
+require_once(__DIR__ . '/../vendor/autoload.php');
5 5
 
6 6
 //Sets the maximum execution time to two minutes, to be sure.
7 7
 ini_set('max_execution_time', 120);
@@ -15,7 +15,7 @@  discard block
 block discarded – undo
15 15
 
16 16
 $email = ['[email protected]'];
17 17
 $basename = 'le.dixo.net';
18
-$domains=['le.dixo.net'];
18
+$domains = ['le.dixo.net'];
19 19
 
20 20
 $logger = new DiagnosticLogger;
21 21
 
@@ -46,8 +46,8 @@  discard block
 block discarded – undo
46 46
     }
47 47
 }
48 48
 catch (\Exception $e) {
49
-    echo $e->getMessage()."\n";
50
-    echo $e->getTraceAsString()."\n";
49
+    echo $e->getMessage() . "\n";
50
+    echo $e->getTraceAsString() . "\n";
51 51
 
52 52
     echo "\nDiagnostic logs\n";
53 53
     $logger->dumpConsole();
Please login to merge, or discard this patch.
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -44,8 +44,7 @@
 block discarded – undo
44 44
             }
45 45
         }
46 46
     }
47
-}
48
-catch (\Exception $e) {
47
+} catch (\Exception $e) {
49 48
     echo $e->getMessage()."\n";
50 49
     echo $e->getTraceAsString()."\n";
51 50
 
Please login to merge, or discard this patch.
examples/exampleDNSFinish.php 3 patches
Indentation   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -49,8 +49,8 @@
 block discarded – undo
49 49
 	if($order->isFinalized()) $order->getCertificate();
50 50
 
51 51
 	//finally, here's how we revoke
52
-    //echo "REVOKING...\n";
53
-    //$order->revokeCertificate();
52
+	//echo "REVOKING...\n";
53
+	//$order->revokeCertificate();
54 54
 }
55 55
 
56 56
 
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -1,7 +1,7 @@  discard block
 block discarded – undo
1 1
 <?php
2 2
 namespace Elphin\LEClient;
3 3
 
4
-require_once(__DIR__.'/../vendor/autoload.php');
4
+require_once(__DIR__ . '/../vendor/autoload.php');
5 5
 
6 6
 //Sets the maximum execution time to two minutes, to be sure.
7 7
 ini_set('max_execution_time', 120);
@@ -15,7 +15,7 @@  discard block
 block discarded – undo
15 15
 
16 16
 $email = ['[email protected]'];
17 17
 $basename = 'le.dixo.net';
18
-$domains=['le.dixo.net'];
18
+$domains = ['le.dixo.net'];
19 19
 
20 20
 $logger = new DiagnosticLogger;
21 21
 
@@ -26,14 +26,14 @@  discard block
 block discarded – undo
26 26
 // in the array (argument 2) will be on the certificate.
27 27
 $order = $client->getOrCreateOrder($basename, $domains);
28 28
 // Check whether there are any authorizations pending. If that is the case, try to verify the pending authorizations.
29
-if(!$order->allAuthorizationsValid())
29
+if (!$order->allAuthorizationsValid())
30 30
 {
31 31
 	// Get the DNS challenges from the pending authorizations.
32 32
 	$pending = $order->getPendingAuthorizations(LEOrder::CHALLENGE_TYPE_DNS);
33 33
 	// Walk the list of pending authorization DNS challenges.
34
-	if(!empty($pending))
34
+	if (!empty($pending))
35 35
 	{
36
-		foreach($pending as $challenge)
36
+		foreach ($pending as $challenge)
37 37
 		{
38 38
 			// Let LetsEncrypt verify this challenge, which should have been fulfilled in exampleDNSStart.php.
39 39
 			$order->verifyPendingOrderAuthorization($challenge['identifier'], LEOrder::CHALLENGE_TYPE_DNS);
@@ -41,12 +41,12 @@  discard block
 block discarded – undo
41 41
 	}
42 42
 }
43 43
 // Check once more whether all authorizations are valid before we can finalize the order.
44
-if($order->allAuthorizationsValid())
44
+if ($order->allAuthorizationsValid())
45 45
 {
46 46
 	// Finalize the order first, if that is not yet done.
47
-	if(!$order->isFinalized()) $order->finalizeOrder();
47
+	if (!$order->isFinalized()) $order->finalizeOrder();
48 48
 	// Check whether the order has been finalized before we can get the certificate. If finalized, get the certificate.
49
-	if($order->isFinalized()) $order->getCertificate();
49
+	if ($order->isFinalized()) $order->getCertificate();
50 50
 
51 51
 	//finally, here's how we revoke
52 52
     //echo "REVOKING...\n";
Please login to merge, or discard this patch.
Braces   +6 added lines, -2 removed lines patch added patch discarded remove patch
@@ -44,9 +44,13 @@
 block discarded – undo
44 44
 if($order->allAuthorizationsValid())
45 45
 {
46 46
 	// Finalize the order first, if that is not yet done.
47
-	if(!$order->isFinalized()) $order->finalizeOrder();
47
+	if(!$order->isFinalized()) {
48
+		$order->finalizeOrder();
49
+	}
48 50
 	// Check whether the order has been finalized before we can get the certificate. If finalized, get the certificate.
49
-	if($order->isFinalized()) $order->getCertificate();
51
+	if($order->isFinalized()) {
52
+		$order->getCertificate();
53
+	}
50 54
 
51 55
 	//finally, here's how we revoke
52 56
     //echo "REVOKING...\n";
Please login to merge, or discard this patch.
examples/exampleHTTP.php 3 patches
Indentation   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -32,7 +32,7 @@
 block discarded – undo
32 32
 		foreach($pending as $challenge)
33 33
 		{
34 34
 			// Define the folder in which to store the challenge. For the purpose of this example, a fictitious path is
35
-            // set.
35
+			// set.
36 36
 			$folder = '/path/to/' . $challenge['identifier'] . '/.well-known/acme-challenge/';
37 37
 			// Check if that directory yet exists. If not, create it.
38 38
 			if(!file_exists($folder)) mkdir($folder, 0777, true);
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -1,7 +1,7 @@  discard block
 block discarded – undo
1 1
 <?php
2 2
 namespace Elphin\LEClient;
3 3
 
4
-require_once(__DIR__.'/../vendor/autoload.php');
4
+require_once(__DIR__ . '/../vendor/autoload.php');
5 5
 
6 6
 //Sets the maximum execution time to two minutes, to be sure.
7 7
 ini_set('max_execution_time', 120);
@@ -22,20 +22,20 @@  discard block
 block discarded – undo
22 22
 // domains in the array (argument 2) will be on the certificate.
23 23
 $order = $client->getOrCreateOrder($basename, $domains);
24 24
 // Check whether there are any authorizations pending. If that is the case, try to verify the pending authorizations.
25
-if(!$order->allAuthorizationsValid())
25
+if (!$order->allAuthorizationsValid())
26 26
 {
27 27
 	// Get the HTTP challenges from the pending authorizations.
28 28
 	$pending = $order->getPendingAuthorizations(LEOrder::CHALLENGE_TYPE_HTTP);
29 29
 	// Walk the list of pending authorization HTTP challenges.
30
-	if(!empty($pending))
30
+	if (!empty($pending))
31 31
 	{
32
-		foreach($pending as $challenge)
32
+		foreach ($pending as $challenge)
33 33
 		{
34 34
 			// Define the folder in which to store the challenge. For the purpose of this example, a fictitious path is
35 35
             // set.
36 36
 			$folder = '/path/to/' . $challenge['identifier'] . '/.well-known/acme-challenge/';
37 37
 			// Check if that directory yet exists. If not, create it.
38
-			if(!file_exists($folder)) mkdir($folder, 0777, true);
38
+			if (!file_exists($folder)) mkdir($folder, 0777, true);
39 39
 			// Store the challenge file for this domain.
40 40
 			file_put_contents($folder . $challenge['filename'], $challenge['content']);
41 41
 			// Let LetsEncrypt verify this challenge.
@@ -44,12 +44,12 @@  discard block
 block discarded – undo
44 44
 	}
45 45
 }
46 46
 // Check once more whether all authorizations are valid before we can finalize the order.
47
-if($order->allAuthorizationsValid())
47
+if ($order->allAuthorizationsValid())
48 48
 {
49 49
 	// Finalize the order first, if that is not yet done.
50
-	if(!$order->isFinalized()) $order->finalizeOrder();
50
+	if (!$order->isFinalized()) $order->finalizeOrder();
51 51
 	// Check whether the order has been finalized before we can get the certificate. If finalized, get the certificate.
52
-	if($order->isFinalized()) $order->getCertificate();
52
+	if ($order->isFinalized()) $order->getCertificate();
53 53
 }
54 54
 
55 55
 echo "\nDiagnostic logs\n";
Please login to merge, or discard this patch.
Braces   +10 added lines, -4 removed lines patch added patch discarded remove patch
@@ -35,7 +35,9 @@  discard block
 block discarded – undo
35 35
             // set.
36 36
 			$folder = '/path/to/' . $challenge['identifier'] . '/.well-known/acme-challenge/';
37 37
 			// Check if that directory yet exists. If not, create it.
38
-			if(!file_exists($folder)) mkdir($folder, 0777, true);
38
+			if(!file_exists($folder)) {
39
+				mkdir($folder, 0777, true);
40
+			}
39 41
 			// Store the challenge file for this domain.
40 42
 			file_put_contents($folder . $challenge['filename'], $challenge['content']);
41 43
 			// Let LetsEncrypt verify this challenge.
@@ -47,10 +49,14 @@  discard block
 block discarded – undo
47 49
 if($order->allAuthorizationsValid())
48 50
 {
49 51
 	// Finalize the order first, if that is not yet done.
50
-	if(!$order->isFinalized()) $order->finalizeOrder();
52
+	if(!$order->isFinalized()) {
53
+		$order->finalizeOrder();
54
+	}
51 55
 	// Check whether the order has been finalized before we can get the certificate. If finalized, get the certificate.
52
-	if($order->isFinalized()) $order->getCertificate();
53
-}
56
+	if($order->isFinalized()) {
57
+		$order->getCertificate();
58
+	}
59
+	}
54 60
 
55 61
 echo "\nDiagnostic logs\n";
56 62
 $logger->dumpConsole();
57 63
\ No newline at end of file
Please login to merge, or discard this patch.
src/LEAccount.php 2 patches
Indentation   +230 added lines, -230 removed lines patch added patch discarded remove patch
@@ -13,234 +13,234 @@
 block discarded – undo
13 13
  */
14 14
 class LEAccount
15 15
 {
16
-    private $connector;
17
-
18
-    public $id;
19
-    public $key;
20
-    public $contact;
21
-    public $agreement;
22
-    public $initialIp;
23
-    public $createdAt;
24
-    public $status;
25
-
26
-    /** @var LoggerInterface  */
27
-    private $log;
28
-
29
-    /** @var CertificateStorageInterface */
30
-    private $storage;
31
-
32
-    /**
33
-     * Initiates the LetsEncrypt Account class.
34
-     *
35
-     * @param LEConnector $connector The LetsEncrypt Connector instance to use for HTTP requests.
36
-     * @param LoggerInterface $log   PSR-3 compatible logger
37
-     * @param array $email           The array of strings containing e-mail addresses. Only used when creating a
38
-     *                               new account.
39
-     * @param CertificateStorageInterface $storage  storage for account keys
40
-     */
41
-    public function __construct($connector, LoggerInterface $log, $email, CertificateStorageInterface $storage)
42
-    {
43
-        $this->connector = $connector;
44
-        $this->storage = $storage;
45
-        $this->log = $log;
46
-
47
-        if (empty($storage->getAccountPublicKey()) || empty($storage->getAccountPrivateKey())) {
48
-            $this->log->notice("No account found for ".implode(',', $email).", attempting to create account");
49
-
50
-            $accountKey = LEFunctions::RSAgenerateKeys();
51
-            $storage->setAccountPublicKey($accountKey['public']);
52
-            $storage->setAccountPrivateKey($accountKey['private']);
53
-
54
-            $this->connector->accountURL = $this->createLEAccount($email);
55
-        } else {
56
-            $this->connector->accountURL = $this->getLEAccount();
57
-        }
58
-        if ($this->connector->accountURL === false) {
59
-            throw new RuntimeException('Account not found or deactivated.');
60
-        }
61
-        $this->getLEAccountData();
62
-    }
63
-
64
-    /**
65
-     * Creates a new LetsEncrypt account.
66
-     *
67
-     * @param array     $email  The array of strings containing e-mail addresses.
68
-     *
69
-     * @return string|bool   Returns the new account URL when the account was successfully created, false if not.
70
-     */
71
-    private function createLEAccount($email)
72
-    {
73
-        $contact = array_map(function ($addr) {
74
-            return empty($addr) ? '' : (strpos($addr, 'mailto') === false ? 'mailto:' . $addr : $addr);
75
-        }, $email);
76
-
77
-        $sign = $this->connector->signRequestJWK(
78
-            ['contact' => $contact, 'termsOfServiceAgreed' => true],
79
-            $this->connector->newAccount
80
-        );
81
-        $post = $this->connector->post($this->connector->newAccount, $sign);
82
-        if (strpos($post['header'], "201 Created") !== false) {
83
-            if (preg_match('~Location: (\S+)~i', $post['header'], $matches)) {
84
-                return trim($matches[1]);
85
-            }
86
-        }
87
-        //@codeCoverageIgnoreStart
88
-        return false;
89
-        //@codeCoverageIgnoreEnd
90
-    }
91
-
92
-    /**
93
-     * Gets the LetsEncrypt account URL associated with the stored account keys.
94
-     *
95
-     * @return string|bool   Returns the account URL if it is found, or false when none is found.
96
-     */
97
-    private function getLEAccount()
98
-    {
99
-        $sign = $this->connector->signRequestJWK(['onlyReturnExisting' => true], $this->connector->newAccount);
100
-        $post = $this->connector->post($this->connector->newAccount, $sign);
101
-
102
-        if (strpos($post['header'], "200 OK") !== false) {
103
-            if (preg_match('~Location: (\S+)~i', $post['header'], $matches)) {
104
-                return trim($matches[1]);
105
-            }
106
-        }
107
-        return false;
108
-    }
109
-
110
-    /**
111
-     * Gets the LetsEncrypt account data from the account URL.
112
-     */
113
-    private function getLEAccountData()
114
-    {
115
-        $sign = $this->connector->signRequestKid(
116
-            ['' => ''],
117
-            $this->connector->accountURL,
118
-            $this->connector->accountURL
119
-        );
120
-        $post = $this->connector->post($this->connector->accountURL, $sign);
121
-        if (strpos($post['header'], "200 OK") !== false) {
122
-            $this->id = isset($post['body']['id']) ? $post['body']['id'] : '';
123
-            $this->key = $post['body']['key'];
124
-            $this->contact = $post['body']['contact'];
125
-            $this->agreement = isset($post['body']['agreement']) ? $post['body']['agreement'] : null;
126
-            $this->initialIp = $post['body']['initialIp'];
127
-            $this->createdAt = $post['body']['createdAt'];
128
-            $this->status = $post['body']['status'];
129
-        } else {
130
-            //@codeCoverageIgnoreStart
131
-            throw new RuntimeException('Account data cannot be found.');
132
-            //@codeCoverageIgnoreEnd
133
-        }
134
-    }
135
-
136
-    /**
137
-     * Updates account data. Now just supporting new contact information.
138
-     *
139
-     * @param array     $email  The array of strings containing e-mail adresses.
140
-     *
141
-     * @return boolean  Returns true if the update is successful, false if not.
142
-     */
143
-    public function updateAccount($email)
144
-    {
145
-        $contact = array_map(function ($addr) {
146
-            return empty($addr) ? '' : (strpos($addr, 'mailto') === false ? 'mailto:' . $addr : $addr);
147
-        }, $email);
148
-
149
-        $sign = $this->connector->signRequestKid(
150
-            ['contact' => $contact],
151
-            $this->connector->accountURL,
152
-            $this->connector->accountURL
153
-        );
154
-        $post = $this->connector->post($this->connector->accountURL, $sign);
155
-        if ($post['status'] !== 200) {
156
-            //@codeCoverageIgnoreStart
157
-            throw new RuntimeException('Unable to update account');
158
-            //@codeCoverageIgnoreEnd
159
-        }
160
-
161
-        $this->id = isset($post['body']['id']) ? $post['body']['id'] : '';
162
-        $this->key = $post['body']['key'];
163
-        $this->contact = $post['body']['contact'];
164
-        $this->agreement = isset($post['body']['agreement']) ? $post['body']['agreement'] : '';
165
-        $this->initialIp = $post['body']['initialIp'];
166
-        $this->createdAt = $post['body']['createdAt'];
167
-        $this->status = $post['body']['status'];
168
-
169
-        $this->log->notice('Account data updated');
170
-        return true;
171
-    }
172
-
173
-    /**
174
-     * Creates new RSA account keys and updates the keys with LetsEncrypt.
175
-     *
176
-     * @return boolean  Returns true if the update is successful, false if not.
177
-     */
178
-    public function changeAccountKeys()
179
-    {
180
-        $new=LEFunctions::RSAgenerateKeys();
181
-
182
-        $privateKey = openssl_pkey_get_private($new['private']);
183
-        if ($privateKey === false) {
184
-            //@codeCoverageIgnoreStart
185
-            throw new RuntimeException('Failed to open newly generated private key');
186
-            //@codeCoverageIgnoreEnd
187
-        }
188
-
189
-
190
-        $details = openssl_pkey_get_details($privateKey);
191
-        $innerPayload = ['account' => $this->connector->accountURL, 'newKey' => [
192
-            "kty" => "RSA",
193
-            "n" => LEFunctions::base64UrlSafeEncode($details["rsa"]["n"]),
194
-            "e" => LEFunctions::base64UrlSafeEncode($details["rsa"]["e"])
195
-        ]];
196
-        $outerPayload = $this->connector->signRequestJWK(
197
-            $innerPayload,
198
-            $this->connector->keyChange,
199
-            $new['private']
200
-        );
201
-        $sign = $this->connector->signRequestKid(
202
-            $outerPayload,
203
-            $this->connector->accountURL,
204
-            $this->connector->keyChange
205
-        );
206
-        $post = $this->connector->post($this->connector->keyChange, $sign);
207
-        if ($post['status'] !== 200) {
208
-            //@codeCoverageIgnoreStart
209
-            throw new RuntimeException('Unable to post new account keys');
210
-            //@codeCoverageIgnoreEnd
211
-        }
212
-
213
-        $this->getLEAccountData();
214
-
215
-        $this->storage->setAccountPublicKey($new['public']);
216
-        $this->storage->setAccountPrivateKey($new['private']);
217
-
218
-        $this->log->notice('Account keys changed');
219
-        return true;
220
-    }
221
-
222
-    /**
223
-     * Deactivates the LetsEncrypt account.
224
-     *
225
-     * @return boolean  Returns true if the deactivation is successful, false if not.
226
-     */
227
-    public function deactivateAccount()
228
-    {
229
-        $sign = $this->connector->signRequestKid(
230
-            ['status' => 'deactivated'],
231
-            $this->connector->accountURL,
232
-            $this->connector->accountURL
233
-        );
234
-        $post = $this->connector->post($this->connector->accountURL, $sign);
235
-        if ($post['status'] !== 200) {
236
-            //@codeCoverageIgnoreStart
237
-            $this->log->error('Account deactivation failed');
238
-            return false;
239
-            //@codeCoverageIgnoreEnd
240
-        }
241
-
242
-        $this->connector->accountDeactivated = true;
243
-        $this->log->info('Account deactivated');
244
-        return true;
245
-    }
16
+	private $connector;
17
+
18
+	public $id;
19
+	public $key;
20
+	public $contact;
21
+	public $agreement;
22
+	public $initialIp;
23
+	public $createdAt;
24
+	public $status;
25
+
26
+	/** @var LoggerInterface  */
27
+	private $log;
28
+
29
+	/** @var CertificateStorageInterface */
30
+	private $storage;
31
+
32
+	/**
33
+	 * Initiates the LetsEncrypt Account class.
34
+	 *
35
+	 * @param LEConnector $connector The LetsEncrypt Connector instance to use for HTTP requests.
36
+	 * @param LoggerInterface $log   PSR-3 compatible logger
37
+	 * @param array $email           The array of strings containing e-mail addresses. Only used when creating a
38
+	 *                               new account.
39
+	 * @param CertificateStorageInterface $storage  storage for account keys
40
+	 */
41
+	public function __construct($connector, LoggerInterface $log, $email, CertificateStorageInterface $storage)
42
+	{
43
+		$this->connector = $connector;
44
+		$this->storage = $storage;
45
+		$this->log = $log;
46
+
47
+		if (empty($storage->getAccountPublicKey()) || empty($storage->getAccountPrivateKey())) {
48
+			$this->log->notice("No account found for ".implode(',', $email).", attempting to create account");
49
+
50
+			$accountKey = LEFunctions::RSAgenerateKeys();
51
+			$storage->setAccountPublicKey($accountKey['public']);
52
+			$storage->setAccountPrivateKey($accountKey['private']);
53
+
54
+			$this->connector->accountURL = $this->createLEAccount($email);
55
+		} else {
56
+			$this->connector->accountURL = $this->getLEAccount();
57
+		}
58
+		if ($this->connector->accountURL === false) {
59
+			throw new RuntimeException('Account not found or deactivated.');
60
+		}
61
+		$this->getLEAccountData();
62
+	}
63
+
64
+	/**
65
+	 * Creates a new LetsEncrypt account.
66
+	 *
67
+	 * @param array     $email  The array of strings containing e-mail addresses.
68
+	 *
69
+	 * @return string|bool   Returns the new account URL when the account was successfully created, false if not.
70
+	 */
71
+	private function createLEAccount($email)
72
+	{
73
+		$contact = array_map(function ($addr) {
74
+			return empty($addr) ? '' : (strpos($addr, 'mailto') === false ? 'mailto:' . $addr : $addr);
75
+		}, $email);
76
+
77
+		$sign = $this->connector->signRequestJWK(
78
+			['contact' => $contact, 'termsOfServiceAgreed' => true],
79
+			$this->connector->newAccount
80
+		);
81
+		$post = $this->connector->post($this->connector->newAccount, $sign);
82
+		if (strpos($post['header'], "201 Created") !== false) {
83
+			if (preg_match('~Location: (\S+)~i', $post['header'], $matches)) {
84
+				return trim($matches[1]);
85
+			}
86
+		}
87
+		//@codeCoverageIgnoreStart
88
+		return false;
89
+		//@codeCoverageIgnoreEnd
90
+	}
91
+
92
+	/**
93
+	 * Gets the LetsEncrypt account URL associated with the stored account keys.
94
+	 *
95
+	 * @return string|bool   Returns the account URL if it is found, or false when none is found.
96
+	 */
97
+	private function getLEAccount()
98
+	{
99
+		$sign = $this->connector->signRequestJWK(['onlyReturnExisting' => true], $this->connector->newAccount);
100
+		$post = $this->connector->post($this->connector->newAccount, $sign);
101
+
102
+		if (strpos($post['header'], "200 OK") !== false) {
103
+			if (preg_match('~Location: (\S+)~i', $post['header'], $matches)) {
104
+				return trim($matches[1]);
105
+			}
106
+		}
107
+		return false;
108
+	}
109
+
110
+	/**
111
+	 * Gets the LetsEncrypt account data from the account URL.
112
+	 */
113
+	private function getLEAccountData()
114
+	{
115
+		$sign = $this->connector->signRequestKid(
116
+			['' => ''],
117
+			$this->connector->accountURL,
118
+			$this->connector->accountURL
119
+		);
120
+		$post = $this->connector->post($this->connector->accountURL, $sign);
121
+		if (strpos($post['header'], "200 OK") !== false) {
122
+			$this->id = isset($post['body']['id']) ? $post['body']['id'] : '';
123
+			$this->key = $post['body']['key'];
124
+			$this->contact = $post['body']['contact'];
125
+			$this->agreement = isset($post['body']['agreement']) ? $post['body']['agreement'] : null;
126
+			$this->initialIp = $post['body']['initialIp'];
127
+			$this->createdAt = $post['body']['createdAt'];
128
+			$this->status = $post['body']['status'];
129
+		} else {
130
+			//@codeCoverageIgnoreStart
131
+			throw new RuntimeException('Account data cannot be found.');
132
+			//@codeCoverageIgnoreEnd
133
+		}
134
+	}
135
+
136
+	/**
137
+	 * Updates account data. Now just supporting new contact information.
138
+	 *
139
+	 * @param array     $email  The array of strings containing e-mail adresses.
140
+	 *
141
+	 * @return boolean  Returns true if the update is successful, false if not.
142
+	 */
143
+	public function updateAccount($email)
144
+	{
145
+		$contact = array_map(function ($addr) {
146
+			return empty($addr) ? '' : (strpos($addr, 'mailto') === false ? 'mailto:' . $addr : $addr);
147
+		}, $email);
148
+
149
+		$sign = $this->connector->signRequestKid(
150
+			['contact' => $contact],
151
+			$this->connector->accountURL,
152
+			$this->connector->accountURL
153
+		);
154
+		$post = $this->connector->post($this->connector->accountURL, $sign);
155
+		if ($post['status'] !== 200) {
156
+			//@codeCoverageIgnoreStart
157
+			throw new RuntimeException('Unable to update account');
158
+			//@codeCoverageIgnoreEnd
159
+		}
160
+
161
+		$this->id = isset($post['body']['id']) ? $post['body']['id'] : '';
162
+		$this->key = $post['body']['key'];
163
+		$this->contact = $post['body']['contact'];
164
+		$this->agreement = isset($post['body']['agreement']) ? $post['body']['agreement'] : '';
165
+		$this->initialIp = $post['body']['initialIp'];
166
+		$this->createdAt = $post['body']['createdAt'];
167
+		$this->status = $post['body']['status'];
168
+
169
+		$this->log->notice('Account data updated');
170
+		return true;
171
+	}
172
+
173
+	/**
174
+	 * Creates new RSA account keys and updates the keys with LetsEncrypt.
175
+	 *
176
+	 * @return boolean  Returns true if the update is successful, false if not.
177
+	 */
178
+	public function changeAccountKeys()
179
+	{
180
+		$new=LEFunctions::RSAgenerateKeys();
181
+
182
+		$privateKey = openssl_pkey_get_private($new['private']);
183
+		if ($privateKey === false) {
184
+			//@codeCoverageIgnoreStart
185
+			throw new RuntimeException('Failed to open newly generated private key');
186
+			//@codeCoverageIgnoreEnd
187
+		}
188
+
189
+
190
+		$details = openssl_pkey_get_details($privateKey);
191
+		$innerPayload = ['account' => $this->connector->accountURL, 'newKey' => [
192
+			"kty" => "RSA",
193
+			"n" => LEFunctions::base64UrlSafeEncode($details["rsa"]["n"]),
194
+			"e" => LEFunctions::base64UrlSafeEncode($details["rsa"]["e"])
195
+		]];
196
+		$outerPayload = $this->connector->signRequestJWK(
197
+			$innerPayload,
198
+			$this->connector->keyChange,
199
+			$new['private']
200
+		);
201
+		$sign = $this->connector->signRequestKid(
202
+			$outerPayload,
203
+			$this->connector->accountURL,
204
+			$this->connector->keyChange
205
+		);
206
+		$post = $this->connector->post($this->connector->keyChange, $sign);
207
+		if ($post['status'] !== 200) {
208
+			//@codeCoverageIgnoreStart
209
+			throw new RuntimeException('Unable to post new account keys');
210
+			//@codeCoverageIgnoreEnd
211
+		}
212
+
213
+		$this->getLEAccountData();
214
+
215
+		$this->storage->setAccountPublicKey($new['public']);
216
+		$this->storage->setAccountPrivateKey($new['private']);
217
+
218
+		$this->log->notice('Account keys changed');
219
+		return true;
220
+	}
221
+
222
+	/**
223
+	 * Deactivates the LetsEncrypt account.
224
+	 *
225
+	 * @return boolean  Returns true if the deactivation is successful, false if not.
226
+	 */
227
+	public function deactivateAccount()
228
+	{
229
+		$sign = $this->connector->signRequestKid(
230
+			['status' => 'deactivated'],
231
+			$this->connector->accountURL,
232
+			$this->connector->accountURL
233
+		);
234
+		$post = $this->connector->post($this->connector->accountURL, $sign);
235
+		if ($post['status'] !== 200) {
236
+			//@codeCoverageIgnoreStart
237
+			$this->log->error('Account deactivation failed');
238
+			return false;
239
+			//@codeCoverageIgnoreEnd
240
+		}
241
+
242
+		$this->connector->accountDeactivated = true;
243
+		$this->log->info('Account deactivated');
244
+		return true;
245
+	}
246 246
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -45,7 +45,7 @@  discard block
 block discarded – undo
45 45
         $this->log = $log;
46 46
 
47 47
         if (empty($storage->getAccountPublicKey()) || empty($storage->getAccountPrivateKey())) {
48
-            $this->log->notice("No account found for ".implode(',', $email).", attempting to create account");
48
+            $this->log->notice("No account found for " . implode(',', $email) . ", attempting to create account");
49 49
 
50 50
             $accountKey = LEFunctions::RSAgenerateKeys();
51 51
             $storage->setAccountPublicKey($accountKey['public']);
@@ -70,7 +70,7 @@  discard block
 block discarded – undo
70 70
      */
71 71
     private function createLEAccount($email)
72 72
     {
73
-        $contact = array_map(function ($addr) {
73
+        $contact = array_map(function($addr) {
74 74
             return empty($addr) ? '' : (strpos($addr, 'mailto') === false ? 'mailto:' . $addr : $addr);
75 75
         }, $email);
76 76
 
@@ -142,7 +142,7 @@  discard block
 block discarded – undo
142 142
      */
143 143
     public function updateAccount($email)
144 144
     {
145
-        $contact = array_map(function ($addr) {
145
+        $contact = array_map(function($addr) {
146 146
             return empty($addr) ? '' : (strpos($addr, 'mailto') === false ? 'mailto:' . $addr : $addr);
147 147
         }, $email);
148 148
 
@@ -177,7 +177,7 @@  discard block
 block discarded – undo
177 177
      */
178 178
     public function changeAccountKeys()
179 179
     {
180
-        $new=LEFunctions::RSAgenerateKeys();
180
+        $new = LEFunctions::RSAgenerateKeys();
181 181
 
182 182
         $privateKey = openssl_pkey_get_private($new['private']);
183 183
         if ($privateKey === false) {
Please login to merge, or discard this patch.
src/LEClient.php 1 patch
Indentation   +152 added lines, -152 removed lines patch added patch discarded remove patch
@@ -21,156 +21,156 @@
 block discarded – undo
21 21
  */
22 22
 class LEClient
23 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
-    }
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 176
 }
Please login to merge, or discard this patch.
src/DiagnosticLogger.php 1 patch
Indentation   +84 added lines, -84 removed lines patch added patch discarded remove patch
@@ -13,88 +13,88 @@
 block discarded – undo
13 13
  */
14 14
 class DiagnosticLogger extends AbstractLogger
15 15
 {
16
-    private $logs = [];
17
-
18
-    public function log($level, $message, array $context = [])
19
-    {
20
-        $this->logs[] = [$level, $message, $context];
21
-    }
22
-
23
-    public function dumpConsole($useColours = true)
24
-    {
25
-        $colours = [
26
-            'alert' => "\e[97m\e[41m",
27
-            'emergency' => "\e[97m\e[41m",
28
-            'critical' => "\e[97m\e[41m",
29
-            'error' => "\e[91m",
30
-            'warning' => "\e[93m",
31
-            'notice' => "\e[96m",
32
-            'info' => "\e[92m",
33
-            'debug' => "\e[2m",
34
-        ];
35
-
36
-        $reset = $useColours ? "\e[0m" : '';
37
-
38
-        foreach ($this->logs as $log) {
39
-            $col = $useColours ? $colours[$log[0]] : '';
40
-            echo $col . $log[0] . ': ' . $this->interpolateMessage($log[1], $log[2]) . $reset . "\n";
41
-        }
42
-    }
43
-
44
-    public function dumpHTML($echo = true)
45
-    {
46
-        $html = '<div class="liblynx-diagnostic-log">';
47
-        $html .= '<table class="table"><thead><tr><th>Level</th><th>Message</th></tr></thead><tbody>';
48
-        $html .= "\n";
49
-
50
-        foreach ($this->logs as $log) {
51
-            $html .= '<tr class="level-' . $log[0] . '"><td>' . $log[0] . '</td><td>' .
52
-                htmlentities($this->interpolateMessage($log[1], $log[2])) .
53
-                "</td></tr>\n";
54
-        }
55
-        $html .= "</tbody></table></div>\n";
56
-
57
-        if ($echo) {
58
-            echo $html; //@codeCoverageIgnore
59
-        }
60
-        return $html;
61
-    }
62
-
63
-    /**
64
-     * Interpolates context values into the message placeholders.
65
-     */
66
-    private function interpolateMessage($message, array $context = [])
67
-    {
68
-        // build a replacement array with braces around the context keys
69
-        $replace = [];
70
-        foreach ($context as $key => $val) {
71
-            // check that the value can be casted to string
72
-            if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
73
-                $replace['{' . $key . '}'] = $val;
74
-            }
75
-        }
76
-
77
-        // interpolate replacement values into the message and return
78
-        return strtr($message, $replace);
79
-    }
80
-
81
-
82
-    public function cleanLogs()
83
-    {
84
-        $logs = $this->logs;
85
-        $this->logs = [];
86
-
87
-        return $logs;
88
-    }
89
-
90
-    public function countLogs($level)
91
-    {
92
-        $count = 0;
93
-        foreach ($this->logs as $log) {
94
-            if ($log[0] == $level) {
95
-                $count++;
96
-            }
97
-        }
98
-        return $count;
99
-    }
16
+	private $logs = [];
17
+
18
+	public function log($level, $message, array $context = [])
19
+	{
20
+		$this->logs[] = [$level, $message, $context];
21
+	}
22
+
23
+	public function dumpConsole($useColours = true)
24
+	{
25
+		$colours = [
26
+			'alert' => "\e[97m\e[41m",
27
+			'emergency' => "\e[97m\e[41m",
28
+			'critical' => "\e[97m\e[41m",
29
+			'error' => "\e[91m",
30
+			'warning' => "\e[93m",
31
+			'notice' => "\e[96m",
32
+			'info' => "\e[92m",
33
+			'debug' => "\e[2m",
34
+		];
35
+
36
+		$reset = $useColours ? "\e[0m" : '';
37
+
38
+		foreach ($this->logs as $log) {
39
+			$col = $useColours ? $colours[$log[0]] : '';
40
+			echo $col . $log[0] . ': ' . $this->interpolateMessage($log[1], $log[2]) . $reset . "\n";
41
+		}
42
+	}
43
+
44
+	public function dumpHTML($echo = true)
45
+	{
46
+		$html = '<div class="liblynx-diagnostic-log">';
47
+		$html .= '<table class="table"><thead><tr><th>Level</th><th>Message</th></tr></thead><tbody>';
48
+		$html .= "\n";
49
+
50
+		foreach ($this->logs as $log) {
51
+			$html .= '<tr class="level-' . $log[0] . '"><td>' . $log[0] . '</td><td>' .
52
+				htmlentities($this->interpolateMessage($log[1], $log[2])) .
53
+				"</td></tr>\n";
54
+		}
55
+		$html .= "</tbody></table></div>\n";
56
+
57
+		if ($echo) {
58
+			echo $html; //@codeCoverageIgnore
59
+		}
60
+		return $html;
61
+	}
62
+
63
+	/**
64
+	 * Interpolates context values into the message placeholders.
65
+	 */
66
+	private function interpolateMessage($message, array $context = [])
67
+	{
68
+		// build a replacement array with braces around the context keys
69
+		$replace = [];
70
+		foreach ($context as $key => $val) {
71
+			// check that the value can be casted to string
72
+			if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
73
+				$replace['{' . $key . '}'] = $val;
74
+			}
75
+		}
76
+
77
+		// interpolate replacement values into the message and return
78
+		return strtr($message, $replace);
79
+	}
80
+
81
+
82
+	public function cleanLogs()
83
+	{
84
+		$logs = $this->logs;
85
+		$this->logs = [];
86
+
87
+		return $logs;
88
+	}
89
+
90
+	public function countLogs($level)
91
+	{
92
+		$count = 0;
93
+		foreach ($this->logs as $log) {
94
+			if ($log[0] == $level) {
95
+				$count++;
96
+			}
97
+		}
98
+		return $count;
99
+	}
100 100
 }
Please login to merge, or discard this patch.
src/FilesystemCertificateStorage.php 2 patches
Indentation   +157 added lines, -157 removed lines patch added patch discarded remove patch
@@ -10,161 +10,161 @@
 block discarded – undo
10 10
  */
11 11
 class FilesystemCertificateStorage implements CertificateStorageInterface
12 12
 {
13
-    private $dir;
14
-
15
-    public function __construct($dir = null)
16
-    {
17
-        $this->dir = $dir ?? getcwd().DIRECTORY_SEPARATOR.'certificates';
18
-
19
-        if (!is_dir($this->dir)) {
20
-            /** @scrutinizer ignore-unhandled */ @mkdir($this->dir);
21
-        }
22
-        if (!is_writable($this->dir)) {
23
-            throw new RuntimeException("{$this->dir} is not writable");
24
-        }
25
-    }
26
-
27
-
28
-    /**
29
-     * @inheritdoc
30
-     */
31
-    public function getAccountPublicKey()
32
-    {
33
-        return $this->getMetadata('account.public');
34
-    }
35
-
36
-    /**
37
-     * @inheritdoc
38
-     */
39
-    public function setAccountPublicKey($key)
40
-    {
41
-        $this->setMetadata('account.public', $key);
42
-    }
43
-
44
-    /**
45
-     * @inheritdoc
46
-     */
47
-    public function getAccountPrivateKey()
48
-    {
49
-        return $this->getMetadata('account.key');
50
-    }
51
-
52
-    /**
53
-     * @inheritdoc
54
-     */
55
-    public function setAccountPrivateKey($key)
56
-    {
57
-        $this->setMetadata('account.key', $key);
58
-    }
59
-
60
-    private function getDomainKey($domain, $suffix)
61
-    {
62
-        return str_replace('*', 'wildcard', $domain).'.'.$suffix;
63
-    }
64
-    /**
65
-     * @inheritdoc
66
-     */
67
-    public function getCertificate($domain)
68
-    {
69
-        return $this->getMetadata($this->getDomainKey($domain, 'crt'));
70
-    }
71
-
72
-    /**
73
-     * @inheritdoc
74
-     */
75
-    public function setCertificate($domain, $certificate)
76
-    {
77
-        $this->setMetadata($this->getDomainKey($domain, 'crt'), $certificate);
78
-    }
79
-
80
-    /**
81
-     * @inheritdoc
82
-     */
83
-    public function getFullChainCertificate($domain)
84
-    {
85
-        return $this->getMetadata($this->getDomainKey($domain, 'fullchain.crt'));
86
-    }
87
-
88
-    /**
89
-     * @inheritdoc
90
-     */
91
-    public function setFullChainCertificate($domain, $certificate)
92
-    {
93
-        $this->setMetadata($this->getDomainKey($domain, 'fullchain.crt'), $certificate);
94
-    }
95
-
96
-    /**
97
-     * @inheritdoc
98
-     */
99
-    public function getPrivateKey($domain)
100
-    {
101
-        return $this->getMetadata($this->getDomainKey($domain, 'key'));
102
-    }
103
-
104
-    /**
105
-     * @inheritdoc
106
-     */
107
-    public function setPrivateKey($domain, $key)
108
-    {
109
-        $this->setMetadata($this->getDomainKey($domain, 'key'), $key);
110
-    }
111
-
112
-    /**
113
-     * @inheritdoc
114
-     */
115
-    public function getPublicKey($domain)
116
-    {
117
-        return $this->getMetadata($this->getDomainKey($domain, 'public'));
118
-    }
119
-
120
-    /**
121
-     * @inheritdoc
122
-     */
123
-    public function setPublicKey($domain, $key)
124
-    {
125
-        $this->setMetadata($this->getDomainKey($domain, 'public'), $key);
126
-    }
127
-
128
-    private function getMetadataFilename($key)
129
-    {
130
-        $key=str_replace('*', 'wildcard', $key);
131
-        $file=$this->dir.DIRECTORY_SEPARATOR.$key;
132
-        return $file;
133
-    }
134
-    /**
135
-     * @inheritdoc
136
-     */
137
-    public function getMetadata($key)
138
-    {
139
-        $file=$this->getMetadataFilename($key);
140
-        if (!file_exists($file)) {
141
-            return null;
142
-        }
143
-        return file_get_contents($file);
144
-    }
145
-
146
-    /**
147
-     * @inheritdoc
148
-     */
149
-    public function setMetadata($key, $value)
150
-    {
151
-        $file=$this->getMetadataFilename($key);
152
-        if (is_null($value)) {
153
-            //nothing to store, ensure file is removed
154
-            if (file_exists($file)) {
155
-                unlink($file);
156
-            }
157
-        } else {
158
-            file_put_contents($file, $value);
159
-        }
160
-    }
161
-
162
-    /**
163
-     * @inheritdoc
164
-     */
165
-    public function hasMetadata($key)
166
-    {
167
-        $file=$this->getMetadataFilename($key);
168
-        return file_exists($file);
169
-    }
13
+	private $dir;
14
+
15
+	public function __construct($dir = null)
16
+	{
17
+		$this->dir = $dir ?? getcwd().DIRECTORY_SEPARATOR.'certificates';
18
+
19
+		if (!is_dir($this->dir)) {
20
+			/** @scrutinizer ignore-unhandled */ @mkdir($this->dir);
21
+		}
22
+		if (!is_writable($this->dir)) {
23
+			throw new RuntimeException("{$this->dir} is not writable");
24
+		}
25
+	}
26
+
27
+
28
+	/**
29
+	 * @inheritdoc
30
+	 */
31
+	public function getAccountPublicKey()
32
+	{
33
+		return $this->getMetadata('account.public');
34
+	}
35
+
36
+	/**
37
+	 * @inheritdoc
38
+	 */
39
+	public function setAccountPublicKey($key)
40
+	{
41
+		$this->setMetadata('account.public', $key);
42
+	}
43
+
44
+	/**
45
+	 * @inheritdoc
46
+	 */
47
+	public function getAccountPrivateKey()
48
+	{
49
+		return $this->getMetadata('account.key');
50
+	}
51
+
52
+	/**
53
+	 * @inheritdoc
54
+	 */
55
+	public function setAccountPrivateKey($key)
56
+	{
57
+		$this->setMetadata('account.key', $key);
58
+	}
59
+
60
+	private function getDomainKey($domain, $suffix)
61
+	{
62
+		return str_replace('*', 'wildcard', $domain).'.'.$suffix;
63
+	}
64
+	/**
65
+	 * @inheritdoc
66
+	 */
67
+	public function getCertificate($domain)
68
+	{
69
+		return $this->getMetadata($this->getDomainKey($domain, 'crt'));
70
+	}
71
+
72
+	/**
73
+	 * @inheritdoc
74
+	 */
75
+	public function setCertificate($domain, $certificate)
76
+	{
77
+		$this->setMetadata($this->getDomainKey($domain, 'crt'), $certificate);
78
+	}
79
+
80
+	/**
81
+	 * @inheritdoc
82
+	 */
83
+	public function getFullChainCertificate($domain)
84
+	{
85
+		return $this->getMetadata($this->getDomainKey($domain, 'fullchain.crt'));
86
+	}
87
+
88
+	/**
89
+	 * @inheritdoc
90
+	 */
91
+	public function setFullChainCertificate($domain, $certificate)
92
+	{
93
+		$this->setMetadata($this->getDomainKey($domain, 'fullchain.crt'), $certificate);
94
+	}
95
+
96
+	/**
97
+	 * @inheritdoc
98
+	 */
99
+	public function getPrivateKey($domain)
100
+	{
101
+		return $this->getMetadata($this->getDomainKey($domain, 'key'));
102
+	}
103
+
104
+	/**
105
+	 * @inheritdoc
106
+	 */
107
+	public function setPrivateKey($domain, $key)
108
+	{
109
+		$this->setMetadata($this->getDomainKey($domain, 'key'), $key);
110
+	}
111
+
112
+	/**
113
+	 * @inheritdoc
114
+	 */
115
+	public function getPublicKey($domain)
116
+	{
117
+		return $this->getMetadata($this->getDomainKey($domain, 'public'));
118
+	}
119
+
120
+	/**
121
+	 * @inheritdoc
122
+	 */
123
+	public function setPublicKey($domain, $key)
124
+	{
125
+		$this->setMetadata($this->getDomainKey($domain, 'public'), $key);
126
+	}
127
+
128
+	private function getMetadataFilename($key)
129
+	{
130
+		$key=str_replace('*', 'wildcard', $key);
131
+		$file=$this->dir.DIRECTORY_SEPARATOR.$key;
132
+		return $file;
133
+	}
134
+	/**
135
+	 * @inheritdoc
136
+	 */
137
+	public function getMetadata($key)
138
+	{
139
+		$file=$this->getMetadataFilename($key);
140
+		if (!file_exists($file)) {
141
+			return null;
142
+		}
143
+		return file_get_contents($file);
144
+	}
145
+
146
+	/**
147
+	 * @inheritdoc
148
+	 */
149
+	public function setMetadata($key, $value)
150
+	{
151
+		$file=$this->getMetadataFilename($key);
152
+		if (is_null($value)) {
153
+			//nothing to store, ensure file is removed
154
+			if (file_exists($file)) {
155
+				unlink($file);
156
+			}
157
+		} else {
158
+			file_put_contents($file, $value);
159
+		}
160
+	}
161
+
162
+	/**
163
+	 * @inheritdoc
164
+	 */
165
+	public function hasMetadata($key)
166
+	{
167
+		$file=$this->getMetadataFilename($key);
168
+		return file_exists($file);
169
+	}
170 170
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -14,7 +14,7 @@  discard block
 block discarded – undo
14 14
 
15 15
     public function __construct($dir = null)
16 16
     {
17
-        $this->dir = $dir ?? getcwd().DIRECTORY_SEPARATOR.'certificates';
17
+        $this->dir = $dir ?? getcwd() . DIRECTORY_SEPARATOR . 'certificates';
18 18
 
19 19
         if (!is_dir($this->dir)) {
20 20
             /** @scrutinizer ignore-unhandled */ @mkdir($this->dir);
@@ -59,7 +59,7 @@  discard block
 block discarded – undo
59 59
 
60 60
     private function getDomainKey($domain, $suffix)
61 61
     {
62
-        return str_replace('*', 'wildcard', $domain).'.'.$suffix;
62
+        return str_replace('*', 'wildcard', $domain) . '.' . $suffix;
63 63
     }
64 64
     /**
65 65
      * @inheritdoc
@@ -127,8 +127,8 @@  discard block
 block discarded – undo
127 127
 
128 128
     private function getMetadataFilename($key)
129 129
     {
130
-        $key=str_replace('*', 'wildcard', $key);
131
-        $file=$this->dir.DIRECTORY_SEPARATOR.$key;
130
+        $key = str_replace('*', 'wildcard', $key);
131
+        $file = $this->dir . DIRECTORY_SEPARATOR . $key;
132 132
         return $file;
133 133
     }
134 134
     /**
@@ -136,7 +136,7 @@  discard block
 block discarded – undo
136 136
      */
137 137
     public function getMetadata($key)
138 138
     {
139
-        $file=$this->getMetadataFilename($key);
139
+        $file = $this->getMetadataFilename($key);
140 140
         if (!file_exists($file)) {
141 141
             return null;
142 142
         }
@@ -148,7 +148,7 @@  discard block
 block discarded – undo
148 148
      */
149 149
     public function setMetadata($key, $value)
150 150
     {
151
-        $file=$this->getMetadataFilename($key);
151
+        $file = $this->getMetadataFilename($key);
152 152
         if (is_null($value)) {
153 153
             //nothing to store, ensure file is removed
154 154
             if (file_exists($file)) {
@@ -164,7 +164,7 @@  discard block
 block discarded – undo
164 164
      */
165 165
     public function hasMetadata($key)
166 166
     {
167
-        $file=$this->getMetadataFilename($key);
167
+        $file = $this->getMetadataFilename($key);
168 168
         return file_exists($file);
169 169
     }
170 170
 }
Please login to merge, or discard this patch.
src/CertificateStorageInterface.php 1 patch
Indentation   +104 added lines, -104 removed lines patch added patch discarded remove patch
@@ -9,108 +9,108 @@
 block discarded – undo
9 9
  */
10 10
 interface CertificateStorageInterface
11 11
 {
12
-    /**
13
-     * Get the public key for the ACME account
14
-     * @return string
15
-     */
16
-    public function getAccountPublicKey();
17
-
18
-    /**
19
-     * Set the public key for the ACME account
20
-     * @return string
21
-     */
22
-    public function setAccountPublicKey($key);
23
-
24
-    /**
25
-     * Get the private key for the ACME account
26
-     * @return string
27
-     */
28
-    public function getAccountPrivateKey();
29
-
30
-    /**
31
-     * Set the private key for the ACME account
32
-     * @return string
33
-     */
34
-    public function setAccountPrivateKey($key);
35
-
36
-    /**
37
-     * Get the certificate for the given domain
38
-     *
39
-     * @param $domain string given base domain of certificate (which might include *.wildcard)
40
-     * @return string|null returns null if no certificate is available for domain
41
-     */
42
-    public function getCertificate($domain);
43
-
44
-    /**
45
-     * Set the certificate for the given domain
46
-     *
47
-     * @param $domain string given base domain of certificate (which might include *.wildcard)
48
-     * @param $certificate string containing certificate
49
-     */
50
-    public function setCertificate($domain, $certificate);
51
-
52
-    /**
53
-     * Get the full chain certificate for the given domain
54
-     *
55
-     * @param $domain string given base domain of certificate (which might include *.wildcard)
56
-     * @return string|null returns null if no certificate is available for domain
57
-     */
58
-    public function getFullChainCertificate($domain);
59
-
60
-    /**
61
-     * Set the full chain certificate for the given domain
62
-     *
63
-     * @param $domain string given base domain of certificate (which might include *.wildcard)
64
-     * @param $certificate string containing certificate with any necessary chained certificates
65
-     */
66
-    public function setFullChainCertificate($domain, $certificate);
67
-
68
-    /**
69
-     * Get public key for given certificate
70
-     * @param $domain string given base domain of certificate (which might include *.wildcard)
71
-     * @return string|null returns null if no certificate is available for domain
72
-     */
73
-    public function getPublicKey($domain);
74
-
75
-    /**
76
-     * Set public key for domain
77
-     * @param $domain string given base domain of certificate (which might include *.wildcard)
78
-     * @param $key string containing private key for domain
79
-     */
80
-    public function setPublicKey($domain, $key);
81
-
82
-    /**
83
-     * Get private key for given certificate
84
-     * @param $domain string given base domain of certificate (which might include *.wildcard)
85
-     * @return string|null returns null if no certificate is available for domain
86
-     */
87
-    public function getPrivateKey($domain);
88
-
89
-    /**
90
-     * Set private key for domain
91
-     * @param $domain string given base domain of certificate (which might include *.wildcard)
92
-     * @param $key string containing private key for domain
93
-     */
94
-    public function setPrivateKey($domain, $key);
95
-
96
-    /**
97
-     * Get arbitrary persistent metadata
98
-     * @param $key string unique key
99
-     * @return mixed
100
-     */
101
-    public function getMetadata($key);
102
-
103
-    /**
104
-     * Store persistent metadata
105
-     * @param $key string unique key
106
-     * @param $value string value to store under given key
107
-     */
108
-    public function setMetadata($key, $value);
109
-
110
-    /**
111
-     * Check if persistent metadata for given key is available
112
-     * @param $key
113
-     * @return string|null
114
-     */
115
-    public function hasMetadata($key);
12
+	/**
13
+	 * Get the public key for the ACME account
14
+	 * @return string
15
+	 */
16
+	public function getAccountPublicKey();
17
+
18
+	/**
19
+	 * Set the public key for the ACME account
20
+	 * @return string
21
+	 */
22
+	public function setAccountPublicKey($key);
23
+
24
+	/**
25
+	 * Get the private key for the ACME account
26
+	 * @return string
27
+	 */
28
+	public function getAccountPrivateKey();
29
+
30
+	/**
31
+	 * Set the private key for the ACME account
32
+	 * @return string
33
+	 */
34
+	public function setAccountPrivateKey($key);
35
+
36
+	/**
37
+	 * Get the certificate for the given domain
38
+	 *
39
+	 * @param $domain string given base domain of certificate (which might include *.wildcard)
40
+	 * @return string|null returns null if no certificate is available for domain
41
+	 */
42
+	public function getCertificate($domain);
43
+
44
+	/**
45
+	 * Set the certificate for the given domain
46
+	 *
47
+	 * @param $domain string given base domain of certificate (which might include *.wildcard)
48
+	 * @param $certificate string containing certificate
49
+	 */
50
+	public function setCertificate($domain, $certificate);
51
+
52
+	/**
53
+	 * Get the full chain certificate for the given domain
54
+	 *
55
+	 * @param $domain string given base domain of certificate (which might include *.wildcard)
56
+	 * @return string|null returns null if no certificate is available for domain
57
+	 */
58
+	public function getFullChainCertificate($domain);
59
+
60
+	/**
61
+	 * Set the full chain certificate for the given domain
62
+	 *
63
+	 * @param $domain string given base domain of certificate (which might include *.wildcard)
64
+	 * @param $certificate string containing certificate with any necessary chained certificates
65
+	 */
66
+	public function setFullChainCertificate($domain, $certificate);
67
+
68
+	/**
69
+	 * Get public key for given certificate
70
+	 * @param $domain string given base domain of certificate (which might include *.wildcard)
71
+	 * @return string|null returns null if no certificate is available for domain
72
+	 */
73
+	public function getPublicKey($domain);
74
+
75
+	/**
76
+	 * Set public key for domain
77
+	 * @param $domain string given base domain of certificate (which might include *.wildcard)
78
+	 * @param $key string containing private key for domain
79
+	 */
80
+	public function setPublicKey($domain, $key);
81
+
82
+	/**
83
+	 * Get private key for given certificate
84
+	 * @param $domain string given base domain of certificate (which might include *.wildcard)
85
+	 * @return string|null returns null if no certificate is available for domain
86
+	 */
87
+	public function getPrivateKey($domain);
88
+
89
+	/**
90
+	 * Set private key for domain
91
+	 * @param $domain string given base domain of certificate (which might include *.wildcard)
92
+	 * @param $key string containing private key for domain
93
+	 */
94
+	public function setPrivateKey($domain, $key);
95
+
96
+	/**
97
+	 * Get arbitrary persistent metadata
98
+	 * @param $key string unique key
99
+	 * @return mixed
100
+	 */
101
+	public function getMetadata($key);
102
+
103
+	/**
104
+	 * Store persistent metadata
105
+	 * @param $key string unique key
106
+	 * @param $value string value to store under given key
107
+	 */
108
+	public function setMetadata($key, $value);
109
+
110
+	/**
111
+	 * Check if persistent metadata for given key is available
112
+	 * @param $key
113
+	 * @return string|null
114
+	 */
115
+	public function hasMetadata($key);
116 116
 }
Please login to merge, or discard this patch.
src/LEConnector.php 2 patches
Indentation   +331 added lines, -331 removed lines patch added patch discarded remove patch
@@ -21,335 +21,335 @@
 block discarded – undo
21 21
  */
22 22
 class LEConnector
23 23
 {
24
-    public $baseURL;
25
-
26
-    private $nonce;
27
-
28
-    public $keyChange;
29
-    public $newAccount;
30
-    public $newNonce;
31
-    public $newOrder;
32
-    public $revokeCert;
33
-
34
-    public $accountURL;
35
-    public $accountDeactivated = false;
36
-
37
-    /** @var LoggerInterface */
38
-    private $log;
39
-
40
-    /** @var ClientInterface */
41
-    private $httpClient;
42
-
43
-    /** @var CertificateStorageInterface */
44
-    private $storage;
45
-
46
-    /**
47
-     * Initiates the LetsEncrypt Connector class.
48
-     *
49
-     * @param LoggerInterface $log
50
-     * @param ClientInterface $httpClient
51
-     * @param string $baseURL The LetsEncrypt server URL to make requests to.
52
-     * @param CertificateStorageInterface $storage
53
-     */
54
-    public function __construct(
55
-        LoggerInterface $log,
56
-        ClientInterface $httpClient,
57
-        $baseURL,
58
-        CertificateStorageInterface $storage
59
-    ) {
60
-
61
-        $this->baseURL = $baseURL;
62
-        $this->storage = $storage;
63
-        $this->log = $log;
64
-        $this->httpClient = $httpClient;
65
-
66
-        $this->getLEDirectory();
67
-        $this->getNewNonce();
68
-    }
69
-
70
-    /**
71
-     * Requests the LetsEncrypt Directory and stores the necessary URLs in this LetsEncrypt Connector instance.
72
-     */
73
-    private function getLEDirectory()
74
-    {
75
-        $req = $this->get('/directory');
76
-        $this->keyChange = $req['body']['keyChange'];
77
-        $this->newAccount = $req['body']['newAccount'];
78
-        $this->newNonce = $req['body']['newNonce'];
79
-        $this->newOrder = $req['body']['newOrder'];
80
-        $this->revokeCert = $req['body']['revokeCert'];
81
-    }
82
-
83
-    /**
84
-     * Requests a new nonce from the LetsEncrypt server and stores it in this LetsEncrypt Connector instance.
85
-     */
86
-    private function getNewNonce()
87
-    {
88
-        $result = $this->head($this->newNonce);
89
-
90
-        if ($result['status'] !== 200) {
91
-            //@codeCoverageIgnoreStart
92
-            throw new RuntimeException("No new nonce - fetched {$this->newNonce} got " . $result['header']);
93
-            //@codeCoverageIgnoreEnd
94
-        }
95
-    }
96
-
97
-    /**
98
-     * Makes a request to the HTTP challenge URL and checks whether the authorization is valid for the given $domain.
99
-     *
100
-     * @param string $domain The domain to check the authorization for.
101
-     * @param string $token The token (filename) to request.
102
-     * @param string $keyAuthorization the keyAuthorization (file content) to compare.
103
-     *
104
-     * @return boolean  Returns true if the challenge is valid, false if not.
105
-     */
106
-    public function checkHTTPChallenge($domain, $token, $keyAuthorization)
107
-    {
108
-        $requestURL = $domain . '/.well-known/acme-challenge/' . $token;
109
-
110
-        $request = new Request('GET', $requestURL);
111
-
112
-        try {
113
-            $response = $this->httpClient->send($request);
114
-        } catch (\Exception $e) {
115
-            $this->log->warning(
116
-                "HTTP check on $requestURL failed ({msg})",
117
-                ['msg' => $e->getMessage()]
118
-            );
119
-            return false;
120
-        }
121
-
122
-        $content = $response->getBody()->getContents();
123
-        return $content == $keyAuthorization;
124
-    }
125
-
126
-    /**
127
-     * Makes a Curl request.
128
-     *
129
-     * @param string $method The HTTP method to use. Accepting GET, POST and HEAD requests.
130
-     * @param string $URL The URL or partial URL to make the request to.
131
-     *                       If it is partial, the baseURL will be prepended.
132
-     * @param string $data The body to attach to a POST request. Expected as a JSON encoded string.
133
-     *
134
-     * @return array Returns an array with the keys 'request', 'header' and 'body'.
135
-     */
136
-    private function request($method, $URL, $data = null)
137
-    {
138
-        if ($this->accountDeactivated) {
139
-            throw new LogicException('The account was deactivated. No further requests can be made.');
140
-        }
141
-
142
-        $requestURL = preg_match('~^http~', $URL) ? $URL : $this->baseURL . $URL;
143
-
144
-        $hdrs = ['Accept' => 'application/json'];
145
-        if (!empty($data)) {
146
-            $hdrs['Content-Type'] = 'application/jose+json';
147
-        }
148
-
149
-        $request = new Request($method, $requestURL, $hdrs, $data);
150
-
151
-        try {
152
-            $response = $this->httpClient->send($request);
153
-        } catch (BadResponseException $e) {
154
-            //4xx/5xx failures are not expected and we throw exceptions for them
155
-            $msg = "$method $URL failed";
156
-            if ($e->hasResponse()) {
157
-                $body = (string)$e->getResponse()->getBody();
158
-                $json = json_decode($body, true);
159
-                if (!empty($json) && isset($json['detail'])) {
160
-                    $msg .= " ({$json['detail']})";
161
-                }
162
-            }
163
-            throw new RuntimeException($msg, 0, $e);
164
-        } catch (GuzzleException $e) {
165
-            //@codeCoverageIgnoreStart
166
-            throw new RuntimeException("$method $URL failed", 0, $e);
167
-            //@codeCoverageIgnoreEnd
168
-        }
169
-
170
-        //uncomment this to generate a test simulation of this request
171
-        //TestResponseGenerator::dumpTestSimulation($method, $requestURL, $response);
172
-
173
-        $this->maintainNonce($method, $response);
174
-
175
-        return $this->formatResponse($method, $requestURL, $response);
176
-    }
177
-
178
-    private function formatResponse($method, $requestURL, ResponseInterface $response)
179
-    {
180
-        $body = $response->getBody();
181
-
182
-        $header = $response->getStatusCode() . ' ' . $response->getReasonPhrase() . "\n";
183
-        $allHeaders = $response->getHeaders();
184
-        foreach ($allHeaders as $name => $values) {
185
-            foreach ($values as $value) {
186
-                $header .= "$name: $value\n";
187
-            }
188
-        }
189
-
190
-        $decoded = $body;
191
-        if ($response->getHeaderLine('Content-Type') === 'application/json') {
192
-            $decoded = json_decode($body, true);
193
-            if (!$decoded) {
194
-                //@codeCoverageIgnoreStart
195
-                throw new RuntimeException('Bad JSON received ' . $body);
196
-                //@codeCoverageIgnoreEnd
197
-            }
198
-        }
199
-
200
-        $jsonresponse = [
201
-            'request' => $method . ' ' . $requestURL,
202
-            'header' => $header,
203
-            'body' => $decoded,
204
-            'raw' => $body,
205
-            'status' => $response->getStatusCode()
206
-        ];
207
-
208
-        //$this->log->debug('{request} got {status} header = {header} body = {raw}', $jsonresponse);
209
-
210
-        return $jsonresponse;
211
-    }
212
-
213
-    private function maintainNonce($requestMethod, ResponseInterface $response)
214
-    {
215
-        if ($response->hasHeader('Replay-Nonce')) {
216
-            $this->nonce = $response->getHeader('Replay-Nonce')[0];
217
-            $this->log->debug("got new nonce " . $this->nonce);
218
-        } elseif ($requestMethod == 'POST') {
219
-            $this->getNewNonce(); // Not expecting a new nonce with GET and HEAD requests.
220
-        }
221
-    }
222
-
223
-    /**
224
-     * Makes a GET request.
225
-     *
226
-     * @param string $url The URL or partial URL to make the request to.
227
-     *                    If it is partial, the baseURL will be prepended.
228
-     *
229
-     * @return array Returns an array with the keys 'request', 'header' and 'body'.
230
-     */
231
-    public function get($url)
232
-    {
233
-        return $this->request('GET', $url);
234
-    }
235
-
236
-    /**
237
-     * Makes a POST request.
238
-     *
239
-     * @param string $url The URL or partial URL for the request to. If it is partial, the baseURL will be prepended.
240
-     * @param string $data The body to attach to a POST request. Expected as a json string.
241
-     *
242
-     * @return array Returns an array with the keys 'request', 'header' and 'body'.
243
-     */
244
-    public function post($url, $data = null)
245
-    {
246
-        return $this->request('POST', $url, $data);
247
-    }
248
-
249
-    /**
250
-     * Makes a HEAD request.
251
-     *
252
-     * @param string $url The URL or partial URL to make the request to.
253
-     *                    If it is partial, the baseURL will be prepended.
254
-     *
255
-     * @return array Returns an array with the keys 'request', 'header' and 'body'.
256
-     */
257
-    public function head($url)
258
-    {
259
-        return $this->request('HEAD', $url);
260
-    }
261
-
262
-    /**
263
-     * Generates a JSON Web Key signature to attach to the request.
264
-     *
265
-     * @param array|string $payload The payload to add to the signature.
266
-     * @param string $url The URL to use in the signature.
267
-     * @param string $privateKey The private key to sign the request with.
268
-     *
269
-     * @return string   Returns a JSON encoded string containing the signature.
270
-     */
271
-    public function signRequestJWK($payload, $url, $privateKey = '')
272
-    {
273
-        if ($privateKey == '') {
274
-            $privateKey = $this->storage->getAccountPrivateKey();
275
-        }
276
-        $privateKey = openssl_pkey_get_private($privateKey);
277
-        if ($privateKey === false) {
278
-            //@codeCoverageIgnoreStart
279
-            throw new RuntimeException('LEConnector::signRequestJWK failed to get private key');
280
-            //@codeCoverageIgnoreEnd
281
-        }
282
-
283
-        $details = openssl_pkey_get_details($privateKey);
284
-
285
-        $protected = [
286
-            "alg" => "RS256",
287
-            "jwk" => [
288
-                "kty" => "RSA",
289
-                "n" => LEFunctions::base64UrlSafeEncode($details["rsa"]["n"]),
290
-                "e" => LEFunctions::base64UrlSafeEncode($details["rsa"]["e"]),
291
-            ],
292
-            "nonce" => $this->nonce,
293
-            "url" => $url
294
-        ];
295
-
296
-        $payload64 = LEFunctions::base64UrlSafeEncode(
297
-            str_replace('\\/', '/', is_array($payload) ? json_encode($payload) : $payload)
298
-        );
299
-        $protected64 = LEFunctions::base64UrlSafeEncode(json_encode($protected));
300
-
301
-        openssl_sign($protected64 . '.' . $payload64, $signed, $privateKey, OPENSSL_ALGO_SHA256);
302
-        $signed64 = LEFunctions::base64UrlSafeEncode($signed);
303
-
304
-        $data = [
305
-            'protected' => $protected64,
306
-            'payload' => $payload64,
307
-            'signature' => $signed64
308
-        ];
309
-
310
-        return json_encode($data);
311
-    }
312
-
313
-    /**
314
-     * Generates a Key ID signature to attach to the request.
315
-     *
316
-     * @param array|string $payload The payload to add to the signature.
317
-     * @param string $kid The Key ID to use in the signature.
318
-     * @param string $url The URL to use in the signature.
319
-     * @param string $privateKey The private key to sign the request with. Defaults to account key
320
-     *
321
-     * @return string   Returns a JSON encoded string containing the signature.
322
-     */
323
-    public function signRequestKid($payload, $kid, $url, $privateKey = '')
324
-    {
325
-        if ($privateKey == '') {
326
-            $privateKey = $this->storage->getAccountPrivateKey();
327
-        }
328
-        $privateKey = openssl_pkey_get_private($privateKey);
329
-
330
-        //$details = openssl_pkey_get_details($privateKey);
331
-
332
-        $protected = [
333
-            "alg" => "RS256",
334
-            "kid" => $kid,
335
-            "nonce" => $this->nonce,
336
-            "url" => $url
337
-        ];
338
-
339
-        $payload64 = LEFunctions::base64UrlSafeEncode(
340
-            str_replace('\\/', '/', is_array($payload) ? json_encode($payload) : $payload)
341
-        );
342
-        $protected64 = LEFunctions::base64UrlSafeEncode(json_encode($protected));
343
-
344
-        openssl_sign($protected64 . '.' . $payload64, $signed, $privateKey, OPENSSL_ALGO_SHA256);
345
-        $signed64 = LEFunctions::base64UrlSafeEncode($signed);
346
-
347
-        $data = [
348
-            'protected' => $protected64,
349
-            'payload' => $payload64,
350
-            'signature' => $signed64
351
-        ];
352
-
353
-        return json_encode($data);
354
-    }
24
+	public $baseURL;
25
+
26
+	private $nonce;
27
+
28
+	public $keyChange;
29
+	public $newAccount;
30
+	public $newNonce;
31
+	public $newOrder;
32
+	public $revokeCert;
33
+
34
+	public $accountURL;
35
+	public $accountDeactivated = false;
36
+
37
+	/** @var LoggerInterface */
38
+	private $log;
39
+
40
+	/** @var ClientInterface */
41
+	private $httpClient;
42
+
43
+	/** @var CertificateStorageInterface */
44
+	private $storage;
45
+
46
+	/**
47
+	 * Initiates the LetsEncrypt Connector class.
48
+	 *
49
+	 * @param LoggerInterface $log
50
+	 * @param ClientInterface $httpClient
51
+	 * @param string $baseURL The LetsEncrypt server URL to make requests to.
52
+	 * @param CertificateStorageInterface $storage
53
+	 */
54
+	public function __construct(
55
+		LoggerInterface $log,
56
+		ClientInterface $httpClient,
57
+		$baseURL,
58
+		CertificateStorageInterface $storage
59
+	) {
60
+
61
+		$this->baseURL = $baseURL;
62
+		$this->storage = $storage;
63
+		$this->log = $log;
64
+		$this->httpClient = $httpClient;
65
+
66
+		$this->getLEDirectory();
67
+		$this->getNewNonce();
68
+	}
69
+
70
+	/**
71
+	 * Requests the LetsEncrypt Directory and stores the necessary URLs in this LetsEncrypt Connector instance.
72
+	 */
73
+	private function getLEDirectory()
74
+	{
75
+		$req = $this->get('/directory');
76
+		$this->keyChange = $req['body']['keyChange'];
77
+		$this->newAccount = $req['body']['newAccount'];
78
+		$this->newNonce = $req['body']['newNonce'];
79
+		$this->newOrder = $req['body']['newOrder'];
80
+		$this->revokeCert = $req['body']['revokeCert'];
81
+	}
82
+
83
+	/**
84
+	 * Requests a new nonce from the LetsEncrypt server and stores it in this LetsEncrypt Connector instance.
85
+	 */
86
+	private function getNewNonce()
87
+	{
88
+		$result = $this->head($this->newNonce);
89
+
90
+		if ($result['status'] !== 200) {
91
+			//@codeCoverageIgnoreStart
92
+			throw new RuntimeException("No new nonce - fetched {$this->newNonce} got " . $result['header']);
93
+			//@codeCoverageIgnoreEnd
94
+		}
95
+	}
96
+
97
+	/**
98
+	 * Makes a request to the HTTP challenge URL and checks whether the authorization is valid for the given $domain.
99
+	 *
100
+	 * @param string $domain The domain to check the authorization for.
101
+	 * @param string $token The token (filename) to request.
102
+	 * @param string $keyAuthorization the keyAuthorization (file content) to compare.
103
+	 *
104
+	 * @return boolean  Returns true if the challenge is valid, false if not.
105
+	 */
106
+	public function checkHTTPChallenge($domain, $token, $keyAuthorization)
107
+	{
108
+		$requestURL = $domain . '/.well-known/acme-challenge/' . $token;
109
+
110
+		$request = new Request('GET', $requestURL);
111
+
112
+		try {
113
+			$response = $this->httpClient->send($request);
114
+		} catch (\Exception $e) {
115
+			$this->log->warning(
116
+				"HTTP check on $requestURL failed ({msg})",
117
+				['msg' => $e->getMessage()]
118
+			);
119
+			return false;
120
+		}
121
+
122
+		$content = $response->getBody()->getContents();
123
+		return $content == $keyAuthorization;
124
+	}
125
+
126
+	/**
127
+	 * Makes a Curl request.
128
+	 *
129
+	 * @param string $method The HTTP method to use. Accepting GET, POST and HEAD requests.
130
+	 * @param string $URL The URL or partial URL to make the request to.
131
+	 *                       If it is partial, the baseURL will be prepended.
132
+	 * @param string $data The body to attach to a POST request. Expected as a JSON encoded string.
133
+	 *
134
+	 * @return array Returns an array with the keys 'request', 'header' and 'body'.
135
+	 */
136
+	private function request($method, $URL, $data = null)
137
+	{
138
+		if ($this->accountDeactivated) {
139
+			throw new LogicException('The account was deactivated. No further requests can be made.');
140
+		}
141
+
142
+		$requestURL = preg_match('~^http~', $URL) ? $URL : $this->baseURL . $URL;
143
+
144
+		$hdrs = ['Accept' => 'application/json'];
145
+		if (!empty($data)) {
146
+			$hdrs['Content-Type'] = 'application/jose+json';
147
+		}
148
+
149
+		$request = new Request($method, $requestURL, $hdrs, $data);
150
+
151
+		try {
152
+			$response = $this->httpClient->send($request);
153
+		} catch (BadResponseException $e) {
154
+			//4xx/5xx failures are not expected and we throw exceptions for them
155
+			$msg = "$method $URL failed";
156
+			if ($e->hasResponse()) {
157
+				$body = (string)$e->getResponse()->getBody();
158
+				$json = json_decode($body, true);
159
+				if (!empty($json) && isset($json['detail'])) {
160
+					$msg .= " ({$json['detail']})";
161
+				}
162
+			}
163
+			throw new RuntimeException($msg, 0, $e);
164
+		} catch (GuzzleException $e) {
165
+			//@codeCoverageIgnoreStart
166
+			throw new RuntimeException("$method $URL failed", 0, $e);
167
+			//@codeCoverageIgnoreEnd
168
+		}
169
+
170
+		//uncomment this to generate a test simulation of this request
171
+		//TestResponseGenerator::dumpTestSimulation($method, $requestURL, $response);
172
+
173
+		$this->maintainNonce($method, $response);
174
+
175
+		return $this->formatResponse($method, $requestURL, $response);
176
+	}
177
+
178
+	private function formatResponse($method, $requestURL, ResponseInterface $response)
179
+	{
180
+		$body = $response->getBody();
181
+
182
+		$header = $response->getStatusCode() . ' ' . $response->getReasonPhrase() . "\n";
183
+		$allHeaders = $response->getHeaders();
184
+		foreach ($allHeaders as $name => $values) {
185
+			foreach ($values as $value) {
186
+				$header .= "$name: $value\n";
187
+			}
188
+		}
189
+
190
+		$decoded = $body;
191
+		if ($response->getHeaderLine('Content-Type') === 'application/json') {
192
+			$decoded = json_decode($body, true);
193
+			if (!$decoded) {
194
+				//@codeCoverageIgnoreStart
195
+				throw new RuntimeException('Bad JSON received ' . $body);
196
+				//@codeCoverageIgnoreEnd
197
+			}
198
+		}
199
+
200
+		$jsonresponse = [
201
+			'request' => $method . ' ' . $requestURL,
202
+			'header' => $header,
203
+			'body' => $decoded,
204
+			'raw' => $body,
205
+			'status' => $response->getStatusCode()
206
+		];
207
+
208
+		//$this->log->debug('{request} got {status} header = {header} body = {raw}', $jsonresponse);
209
+
210
+		return $jsonresponse;
211
+	}
212
+
213
+	private function maintainNonce($requestMethod, ResponseInterface $response)
214
+	{
215
+		if ($response->hasHeader('Replay-Nonce')) {
216
+			$this->nonce = $response->getHeader('Replay-Nonce')[0];
217
+			$this->log->debug("got new nonce " . $this->nonce);
218
+		} elseif ($requestMethod == 'POST') {
219
+			$this->getNewNonce(); // Not expecting a new nonce with GET and HEAD requests.
220
+		}
221
+	}
222
+
223
+	/**
224
+	 * Makes a GET request.
225
+	 *
226
+	 * @param string $url The URL or partial URL to make the request to.
227
+	 *                    If it is partial, the baseURL will be prepended.
228
+	 *
229
+	 * @return array Returns an array with the keys 'request', 'header' and 'body'.
230
+	 */
231
+	public function get($url)
232
+	{
233
+		return $this->request('GET', $url);
234
+	}
235
+
236
+	/**
237
+	 * Makes a POST request.
238
+	 *
239
+	 * @param string $url The URL or partial URL for the request to. If it is partial, the baseURL will be prepended.
240
+	 * @param string $data The body to attach to a POST request. Expected as a json string.
241
+	 *
242
+	 * @return array Returns an array with the keys 'request', 'header' and 'body'.
243
+	 */
244
+	public function post($url, $data = null)
245
+	{
246
+		return $this->request('POST', $url, $data);
247
+	}
248
+
249
+	/**
250
+	 * Makes a HEAD request.
251
+	 *
252
+	 * @param string $url The URL or partial URL to make the request to.
253
+	 *                    If it is partial, the baseURL will be prepended.
254
+	 *
255
+	 * @return array Returns an array with the keys 'request', 'header' and 'body'.
256
+	 */
257
+	public function head($url)
258
+	{
259
+		return $this->request('HEAD', $url);
260
+	}
261
+
262
+	/**
263
+	 * Generates a JSON Web Key signature to attach to the request.
264
+	 *
265
+	 * @param array|string $payload The payload to add to the signature.
266
+	 * @param string $url The URL to use in the signature.
267
+	 * @param string $privateKey The private key to sign the request with.
268
+	 *
269
+	 * @return string   Returns a JSON encoded string containing the signature.
270
+	 */
271
+	public function signRequestJWK($payload, $url, $privateKey = '')
272
+	{
273
+		if ($privateKey == '') {
274
+			$privateKey = $this->storage->getAccountPrivateKey();
275
+		}
276
+		$privateKey = openssl_pkey_get_private($privateKey);
277
+		if ($privateKey === false) {
278
+			//@codeCoverageIgnoreStart
279
+			throw new RuntimeException('LEConnector::signRequestJWK failed to get private key');
280
+			//@codeCoverageIgnoreEnd
281
+		}
282
+
283
+		$details = openssl_pkey_get_details($privateKey);
284
+
285
+		$protected = [
286
+			"alg" => "RS256",
287
+			"jwk" => [
288
+				"kty" => "RSA",
289
+				"n" => LEFunctions::base64UrlSafeEncode($details["rsa"]["n"]),
290
+				"e" => LEFunctions::base64UrlSafeEncode($details["rsa"]["e"]),
291
+			],
292
+			"nonce" => $this->nonce,
293
+			"url" => $url
294
+		];
295
+
296
+		$payload64 = LEFunctions::base64UrlSafeEncode(
297
+			str_replace('\\/', '/', is_array($payload) ? json_encode($payload) : $payload)
298
+		);
299
+		$protected64 = LEFunctions::base64UrlSafeEncode(json_encode($protected));
300
+
301
+		openssl_sign($protected64 . '.' . $payload64, $signed, $privateKey, OPENSSL_ALGO_SHA256);
302
+		$signed64 = LEFunctions::base64UrlSafeEncode($signed);
303
+
304
+		$data = [
305
+			'protected' => $protected64,
306
+			'payload' => $payload64,
307
+			'signature' => $signed64
308
+		];
309
+
310
+		return json_encode($data);
311
+	}
312
+
313
+	/**
314
+	 * Generates a Key ID signature to attach to the request.
315
+	 *
316
+	 * @param array|string $payload The payload to add to the signature.
317
+	 * @param string $kid The Key ID to use in the signature.
318
+	 * @param string $url The URL to use in the signature.
319
+	 * @param string $privateKey The private key to sign the request with. Defaults to account key
320
+	 *
321
+	 * @return string   Returns a JSON encoded string containing the signature.
322
+	 */
323
+	public function signRequestKid($payload, $kid, $url, $privateKey = '')
324
+	{
325
+		if ($privateKey == '') {
326
+			$privateKey = $this->storage->getAccountPrivateKey();
327
+		}
328
+		$privateKey = openssl_pkey_get_private($privateKey);
329
+
330
+		//$details = openssl_pkey_get_details($privateKey);
331
+
332
+		$protected = [
333
+			"alg" => "RS256",
334
+			"kid" => $kid,
335
+			"nonce" => $this->nonce,
336
+			"url" => $url
337
+		];
338
+
339
+		$payload64 = LEFunctions::base64UrlSafeEncode(
340
+			str_replace('\\/', '/', is_array($payload) ? json_encode($payload) : $payload)
341
+		);
342
+		$protected64 = LEFunctions::base64UrlSafeEncode(json_encode($protected));
343
+
344
+		openssl_sign($protected64 . '.' . $payload64, $signed, $privateKey, OPENSSL_ALGO_SHA256);
345
+		$signed64 = LEFunctions::base64UrlSafeEncode($signed);
346
+
347
+		$data = [
348
+			'protected' => $protected64,
349
+			'payload' => $payload64,
350
+			'signature' => $signed64
351
+		];
352
+
353
+		return json_encode($data);
354
+	}
355 355
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -154,7 +154,7 @@
 block discarded – undo
154 154
             //4xx/5xx failures are not expected and we throw exceptions for them
155 155
             $msg = "$method $URL failed";
156 156
             if ($e->hasResponse()) {
157
-                $body = (string)$e->getResponse()->getBody();
157
+                $body = (string) $e->getResponse()->getBody();
158 158
                 $json = json_decode($body, true);
159 159
                 if (!empty($json) && isset($json['detail'])) {
160 160
                     $msg .= " ({$json['detail']})";
Please login to merge, or discard this patch.