From 852d90c10264ecd3fb790c35ee73bcf7d5c26e4d Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Mon, 13 Apr 2020 10:46:25 +0200 Subject: [PATCH 001/121] Handle processing status case --- src/Core/AcmeClient.php | 8 ++++---- src/Core/Protocol/AuthorizationChallenge.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 0b21e706..7d62b47e 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -189,17 +189,17 @@ public function challengeAuthorization(AuthorizationChallenge $challenge, $timeo $client = $this->getHttpClient(); $challengeUrl = $challenge->getUrl(); $response = (array) $client->request('POST', $challengeUrl, $client->signKidPayload($challengeUrl, $this->getResourceAccount(), null)); - if ('pending' === $response['status']) { + if ('pending' === $response['status'] || 'processing' === $response['status']) { $response = (array) $client->request('POST', $challengeUrl, $client->signKidPayload($challengeUrl, $this->getResourceAccount(), [])); } // Waiting loop - while (time() <= $endTime && (!isset($response['status']) || 'pending' === $response['status'])) { + while (time() <= $endTime && (!isset($response['status']) || 'pending' === $response['status'] || 'processing' === $response['status'])) { sleep(1); $response = (array) $client->request('POST', $challengeUrl, $client->signKidPayload($challengeUrl, $this->getResourceAccount(), null)); } - if (isset($response['status']) && 'pending' === $response['status']) { + if (isset($response['status']) && ('pending' === $response['status'] || 'processing' === $response['status'])) { throw new ChallengeTimedOutException($response); } if (!isset($response['status']) || 'valid' !== $response['status']) { @@ -233,7 +233,7 @@ public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, $client = $this->getHttpClient(); $orderEndpoint = $order->getOrderEndpoint(); $response = $client->request('POST', $orderEndpoint, $client->signKidPayload($orderEndpoint, $this->getResourceAccount(), null)); - if (\in_array($response['status'], ['pending', 'ready'])) { + if (\in_array($response['status'], ['pending', 'processing', 'ready'])) { $humanText = ['-----BEGIN CERTIFICATE REQUEST-----', '-----END CERTIFICATE REQUEST-----']; $csrContent = $this->csrSigner->signCertificateRequest($csr); diff --git a/src/Core/Protocol/AuthorizationChallenge.php b/src/Core/Protocol/AuthorizationChallenge.php index d0e47586..c9fa71d5 100644 --- a/src/Core/Protocol/AuthorizationChallenge.php +++ b/src/Core/Protocol/AuthorizationChallenge.php @@ -134,7 +134,7 @@ public function isValid() */ public function isPending() { - return 'pending' === $this->status; + return 'pending' === $this->status || 'processing' === $this->status; } /** From 765dd868f7661e0b27e8e5b6b97422a76317c202 Mon Sep 17 00:00:00 2001 From: Alex Plekhanov Date: Wed, 24 Jun 2020 23:45:47 +0200 Subject: [PATCH 002/121] Added RejectedIdentifierServerException --- .../RejectedIdentifierServerException.php | 21 +++++++++++++++++++ src/Core/Http/ServerErrorHandler.php | 4 +++- tests/Core/Http/ServerErrorHandlerTest.php | 2 ++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/Core/Exception/Server/RejectedIdentifierServerException.php diff --git a/src/Core/Exception/Server/RejectedIdentifierServerException.php b/src/Core/Exception/Server/RejectedIdentifierServerException.php new file mode 100644 index 00000000..5c079929 --- /dev/null +++ b/src/Core/Exception/Server/RejectedIdentifierServerException.php @@ -0,0 +1,21 @@ + + */ +class RejectedIdentifierServerException extends AcmeCoreServerException +{ + public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + { + parent::__construct( + $request, + '[rejectedIdentifier] Domain is forbidden by policy: '.$detail, + $previous + ); + } +} diff --git a/src/Core/Http/ServerErrorHandler.php b/src/Core/Http/ServerErrorHandler.php index dea5e6dc..3f21dd45 100644 --- a/src/Core/Http/ServerErrorHandler.php +++ b/src/Core/Http/ServerErrorHandler.php @@ -20,6 +20,7 @@ use AcmePhp\Core\Exception\Server\MalformedServerException; use AcmePhp\Core\Exception\Server\OrderNotReadyServerException; use AcmePhp\Core\Exception\Server\RateLimitedServerException; +use AcmePhp\Core\Exception\Server\RejectedIdentifierServerException; use AcmePhp\Core\Exception\Server\TlsServerException; use AcmePhp\Core\Exception\Server\UnauthorizedServerException; use AcmePhp\Core\Exception\Server\UnknownHostServerException; @@ -39,11 +40,12 @@ class ServerErrorHandler 'badCSR' => BadCsrServerException::class, 'badNonce' => BadNonceServerException::class, 'connection' => ConnectionServerException::class, - 'serverInternal' => InternalServerException::class, 'invalidEmail' => InvalidEmailServerException::class, 'malformed' => MalformedServerException::class, 'orderNotReady' => OrderNotReadyServerException::class, 'rateLimited' => RateLimitedServerException::class, + 'rejectedIdentifier' => RejectedIdentifierServerException::class, + 'serverInternal' => InternalServerException::class, 'tls' => TlsServerException::class, 'unauthorized' => UnauthorizedServerException::class, 'unknownHost' => UnknownHostServerException::class, diff --git a/tests/Core/Http/ServerErrorHandlerTest.php b/tests/Core/Http/ServerErrorHandlerTest.php index 9aacf987..679f2bca 100644 --- a/tests/Core/Http/ServerErrorHandlerTest.php +++ b/tests/Core/Http/ServerErrorHandlerTest.php @@ -19,6 +19,7 @@ use AcmePhp\Core\Exception\Server\InvalidEmailServerException; use AcmePhp\Core\Exception\Server\MalformedServerException; use AcmePhp\Core\Exception\Server\RateLimitedServerException; +use AcmePhp\Core\Exception\Server\RejectedIdentifierServerException; use AcmePhp\Core\Exception\Server\TlsServerException; use AcmePhp\Core\Exception\Server\UnauthorizedServerException; use AcmePhp\Core\Exception\Server\UnknownHostServerException; @@ -39,6 +40,7 @@ public function getErrorTypes() ['invalidEmail', InvalidEmailServerException::class], ['malformed', MalformedServerException::class], ['rateLimited', RateLimitedServerException::class], + ['rejectedIdentifier', RejectedIdentifierServerException::class], ['tls', TlsServerException::class], ['unauthorized', UnauthorizedServerException::class], ['unknownHost', UnknownHostServerException::class], From 47c21396e95a049e11e2d0b81e23301f27ae7cb3 Mon Sep 17 00:00:00 2001 From: Alex Plekhanov Date: Wed, 24 Jun 2020 23:54:16 +0200 Subject: [PATCH 003/121] Added class comment --- .../Server/RejectedIdentifierServerException.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Core/Exception/Server/RejectedIdentifierServerException.php b/src/Core/Exception/Server/RejectedIdentifierServerException.php index 5c079929..00a4d489 100644 --- a/src/Core/Exception/Server/RejectedIdentifierServerException.php +++ b/src/Core/Exception/Server/RejectedIdentifierServerException.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace AcmePhp\Core\Exception\Server; use AcmePhp\Core\Exception\AcmeCoreServerException; From 3749f9690905c3ca17f52520ae1f16e4f28c3ee9 Mon Sep 17 00:00:00 2001 From: Alex Plekhanov Date: Thu, 25 Jun 2020 00:04:21 +0200 Subject: [PATCH 004/121] Reordered alphabetically --- tests/Core/Http/ServerErrorHandlerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Core/Http/ServerErrorHandlerTest.php b/tests/Core/Http/ServerErrorHandlerTest.php index 679f2bca..74f02af1 100644 --- a/tests/Core/Http/ServerErrorHandlerTest.php +++ b/tests/Core/Http/ServerErrorHandlerTest.php @@ -36,11 +36,11 @@ public function getErrorTypes() ['badCSR', BadCsrServerException::class], ['badNonce', BadNonceServerException::class], ['connection', ConnectionServerException::class], - ['serverInternal', InternalServerException::class], ['invalidEmail', InvalidEmailServerException::class], ['malformed', MalformedServerException::class], ['rateLimited', RateLimitedServerException::class], ['rejectedIdentifier', RejectedIdentifierServerException::class], + ['serverInternal', InternalServerException::class], ['tls', TlsServerException::class], ['unauthorized', UnauthorizedServerException::class], ['unknownHost', UnknownHostServerException::class], From c0d3f0dff8f7669613099e1639e753700bb9c91f Mon Sep 17 00:00:00 2001 From: Alex Plekhanov Date: Sun, 28 Jun 2020 20:46:20 +0200 Subject: [PATCH 005/121] Added more exceptions --- .../Exception/Server/CaaServerException.php | 30 +++++++++++++++++++ .../Exception/Server/DnsServerException.php | 30 +++++++++++++++++++ .../IncorrectResponseServerException.php | 30 +++++++++++++++++++ .../Server/InvalidContactServerException.php | 30 +++++++++++++++++++ .../RejectedIdentifierServerException.php | 2 +- .../UnsupportedContactServerException.php | 30 +++++++++++++++++++ .../UnsupportedIdentifierServerException.php | 30 +++++++++++++++++++ .../UserActionRequiredServerException.php | 30 +++++++++++++++++++ src/Core/Http/ServerErrorHandler.php | 14 +++++++++ tests/Core/Http/ServerErrorHandlerTest.php | 16 ++++++++++ 10 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 src/Core/Exception/Server/CaaServerException.php create mode 100644 src/Core/Exception/Server/DnsServerException.php create mode 100644 src/Core/Exception/Server/IncorrectResponseServerException.php create mode 100644 src/Core/Exception/Server/InvalidContactServerException.php create mode 100644 src/Core/Exception/Server/UnsupportedContactServerException.php create mode 100644 src/Core/Exception/Server/UnsupportedIdentifierServerException.php create mode 100644 src/Core/Exception/Server/UserActionRequiredServerException.php diff --git a/src/Core/Exception/Server/CaaServerException.php b/src/Core/Exception/Server/CaaServerException.php new file mode 100644 index 00000000..3725b2a6 --- /dev/null +++ b/src/Core/Exception/Server/CaaServerException.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Core\Exception\Server; + +use AcmePhp\Core\Exception\AcmeCoreServerException; +use Psr\Http\Message\RequestInterface; + +/** + * @author Alex Plekhanov + */ +class CaaServerException extends AcmeCoreServerException +{ + public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + { + parent::__construct( + $request, + '[caa] Certification Authority Authorization (CAA) records forbid the CA from issuing a certificate: '.$detail, + $previous + ); + } +} diff --git a/src/Core/Exception/Server/DnsServerException.php b/src/Core/Exception/Server/DnsServerException.php new file mode 100644 index 00000000..f739ca75 --- /dev/null +++ b/src/Core/Exception/Server/DnsServerException.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Core\Exception\Server; + +use AcmePhp\Core\Exception\AcmeCoreServerException; +use Psr\Http\Message\RequestInterface; + +/** + * @author Alex Plekhanov + */ +class DnsServerException extends AcmeCoreServerException +{ + public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + { + parent::__construct( + $request, + '[dns] There was a problem with a DNS query during identifier validation: '.$detail, + $previous + ); + } +} diff --git a/src/Core/Exception/Server/IncorrectResponseServerException.php b/src/Core/Exception/Server/IncorrectResponseServerException.php new file mode 100644 index 00000000..046c51f1 --- /dev/null +++ b/src/Core/Exception/Server/IncorrectResponseServerException.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Core\Exception\Server; + +use AcmePhp\Core\Exception\AcmeCoreServerException; +use Psr\Http\Message\RequestInterface; + +/** + * @author Alex Plekhanov + */ +class IncorrectResponseServerException extends AcmeCoreServerException +{ + public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + { + parent::__construct( + $request, + "[incorrectResponse] Response received didn’t match the challenge's requirements: ".$detail, + $previous + ); + } +} diff --git a/src/Core/Exception/Server/InvalidContactServerException.php b/src/Core/Exception/Server/InvalidContactServerException.php new file mode 100644 index 00000000..ffd457cb --- /dev/null +++ b/src/Core/Exception/Server/InvalidContactServerException.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Core\Exception\Server; + +use AcmePhp\Core\Exception\AcmeCoreServerException; +use Psr\Http\Message\RequestInterface; + +/** + * @author Alex Plekhanov + */ +class InvalidContactServerException extends AcmeCoreServerException +{ + public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + { + parent::__construct( + $request, + "[invalidContact] A contact URL for an account was invalid: ".$detail, + $previous + ); + } +} diff --git a/src/Core/Exception/Server/RejectedIdentifierServerException.php b/src/Core/Exception/Server/RejectedIdentifierServerException.php index 00a4d489..43e1f988 100644 --- a/src/Core/Exception/Server/RejectedIdentifierServerException.php +++ b/src/Core/Exception/Server/RejectedIdentifierServerException.php @@ -23,7 +23,7 @@ public function __construct(RequestInterface $request, $detail, \Exception $prev { parent::__construct( $request, - '[rejectedIdentifier] Domain is forbidden by policy: '.$detail, + '[rejectedIdentifier] The server will not issue certificates for the identifier: '.$detail, $previous ); } diff --git a/src/Core/Exception/Server/UnsupportedContactServerException.php b/src/Core/Exception/Server/UnsupportedContactServerException.php new file mode 100644 index 00000000..1b43ce18 --- /dev/null +++ b/src/Core/Exception/Server/UnsupportedContactServerException.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Core\Exception\Server; + +use AcmePhp\Core\Exception\AcmeCoreServerException; +use Psr\Http\Message\RequestInterface; + +/** + * @author Alex Plekhanov + */ +class UnsupportedContactServerException extends AcmeCoreServerException +{ + public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + { + parent::__construct( + $request, + "[unsupportedContact] A contact URL for an account used an unsupported protocol scheme: ".$detail, + $previous + ); + } +} diff --git a/src/Core/Exception/Server/UnsupportedIdentifierServerException.php b/src/Core/Exception/Server/UnsupportedIdentifierServerException.php new file mode 100644 index 00000000..b312d925 --- /dev/null +++ b/src/Core/Exception/Server/UnsupportedIdentifierServerException.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Core\Exception\Server; + +use AcmePhp\Core\Exception\AcmeCoreServerException; +use Psr\Http\Message\RequestInterface; + +/** + * @author Alex Plekhanov + */ +class UnsupportedIdentifierServerException extends AcmeCoreServerException +{ + public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + { + parent::__construct( + $request, + "[unsupportedIdentifier] An identifier is of an unsupported type: ".$detail, + $previous + ); + } +} diff --git a/src/Core/Exception/Server/UserActionRequiredServerException.php b/src/Core/Exception/Server/UserActionRequiredServerException.php new file mode 100644 index 00000000..a61ad949 --- /dev/null +++ b/src/Core/Exception/Server/UserActionRequiredServerException.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Core\Exception\Server; + +use AcmePhp\Core\Exception\AcmeCoreServerException; +use Psr\Http\Message\RequestInterface; + +/** + * @author Alex Plekhanov + */ +class UserActionRequiredServerException extends AcmeCoreServerException +{ + public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + { + parent::__construct( + $request, + "[userActionRequired] Visit the “instance” URL and take actions specified there: ".$detail, + $previous + ); + } +} diff --git a/src/Core/Http/ServerErrorHandler.php b/src/Core/Http/ServerErrorHandler.php index 3f21dd45..84dc03ff 100644 --- a/src/Core/Http/ServerErrorHandler.php +++ b/src/Core/Http/ServerErrorHandler.php @@ -14,8 +14,12 @@ use AcmePhp\Core\Exception\AcmeCoreServerException; use AcmePhp\Core\Exception\Server\BadCsrServerException; use AcmePhp\Core\Exception\Server\BadNonceServerException; +use AcmePhp\Core\Exception\Server\CaaServerException; use AcmePhp\Core\Exception\Server\ConnectionServerException; +use AcmePhp\Core\Exception\Server\DnsServerException; +use AcmePhp\Core\Exception\Server\IncorrectResponseServerException; use AcmePhp\Core\Exception\Server\InternalServerException; +use AcmePhp\Core\Exception\Server\InvalidContactServerException; use AcmePhp\Core\Exception\Server\InvalidEmailServerException; use AcmePhp\Core\Exception\Server\MalformedServerException; use AcmePhp\Core\Exception\Server\OrderNotReadyServerException; @@ -24,6 +28,9 @@ use AcmePhp\Core\Exception\Server\TlsServerException; use AcmePhp\Core\Exception\Server\UnauthorizedServerException; use AcmePhp\Core\Exception\Server\UnknownHostServerException; +use AcmePhp\Core\Exception\Server\UnsupportedContactServerException; +use AcmePhp\Core\Exception\Server\UnsupportedIdentifierServerException; +use AcmePhp\Core\Exception\Server\UserActionRequiredServerException; use AcmePhp\Core\Util\JsonDecoder; use GuzzleHttp\Exception\RequestException; use Psr\Http\Message\RequestInterface; @@ -39,7 +46,11 @@ class ServerErrorHandler private static $exceptions = [ 'badCSR' => BadCsrServerException::class, 'badNonce' => BadNonceServerException::class, + 'caa' => CaaServerException::class, 'connection' => ConnectionServerException::class, + 'dns' => DnsServerException::class, + 'incorrectResponse' => IncorrectResponseServerException::class, + 'invalidContact' => InvalidContactServerException::class, 'invalidEmail' => InvalidEmailServerException::class, 'malformed' => MalformedServerException::class, 'orderNotReady' => OrderNotReadyServerException::class, @@ -49,6 +60,9 @@ class ServerErrorHandler 'tls' => TlsServerException::class, 'unauthorized' => UnauthorizedServerException::class, 'unknownHost' => UnknownHostServerException::class, + 'unsupportedContact' => UnsupportedContactServerException::class, + 'unsupportedIdentifier' => UnsupportedIdentifierServerException::class, + 'userActionRequired' => UserActionRequiredServerException::class, ]; /** diff --git a/tests/Core/Http/ServerErrorHandlerTest.php b/tests/Core/Http/ServerErrorHandlerTest.php index 74f02af1..49fbc835 100644 --- a/tests/Core/Http/ServerErrorHandlerTest.php +++ b/tests/Core/Http/ServerErrorHandlerTest.php @@ -14,15 +14,23 @@ use AcmePhp\Core\Exception\AcmeCoreServerException; use AcmePhp\Core\Exception\Server\BadCsrServerException; use AcmePhp\Core\Exception\Server\BadNonceServerException; +use AcmePhp\Core\Exception\Server\CaaServerException; use AcmePhp\Core\Exception\Server\ConnectionServerException; +use AcmePhp\Core\Exception\Server\DnsServerException; +use AcmePhp\Core\Exception\Server\IncorrectResponseServerException; use AcmePhp\Core\Exception\Server\InternalServerException; +use AcmePhp\Core\Exception\Server\InvalidContactServerException; use AcmePhp\Core\Exception\Server\InvalidEmailServerException; use AcmePhp\Core\Exception\Server\MalformedServerException; +use AcmePhp\Core\Exception\Server\OrderNotReadyServerException; use AcmePhp\Core\Exception\Server\RateLimitedServerException; use AcmePhp\Core\Exception\Server\RejectedIdentifierServerException; use AcmePhp\Core\Exception\Server\TlsServerException; use AcmePhp\Core\Exception\Server\UnauthorizedServerException; use AcmePhp\Core\Exception\Server\UnknownHostServerException; +use AcmePhp\Core\Exception\Server\UnsupportedContactServerException; +use AcmePhp\Core\Exception\Server\UnsupportedIdentifierServerException; +use AcmePhp\Core\Exception\Server\UserActionRequiredServerException; use AcmePhp\Core\Http\ServerErrorHandler; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; @@ -35,15 +43,23 @@ public function getErrorTypes() return [ ['badCSR', BadCsrServerException::class], ['badNonce', BadNonceServerException::class], + ['caa', CaaServerException::class], ['connection', ConnectionServerException::class], + ['dns', DnsServerException::class], + ['incorrectResponse', IncorrectResponseServerException::class], + ['invalidContact', InvalidContactServerException::class], ['invalidEmail', InvalidEmailServerException::class], ['malformed', MalformedServerException::class], + ['orderNotReady', OrderNotReadyServerException::class], ['rateLimited', RateLimitedServerException::class], ['rejectedIdentifier', RejectedIdentifierServerException::class], ['serverInternal', InternalServerException::class], ['tls', TlsServerException::class], ['unauthorized', UnauthorizedServerException::class], ['unknownHost', UnknownHostServerException::class], + ['unsupportedContact', UnsupportedContactServerException::class], + ['unsupportedIdentifier', UnsupportedIdentifierServerException::class], + ['userActionRequired', UserActionRequiredServerException::class], ]; } From a9ed8ac1ce0080292c3ceaf450462eb295cb0ae0 Mon Sep 17 00:00:00 2001 From: Alex Plekhanov Date: Sun, 28 Jun 2020 20:50:06 +0200 Subject: [PATCH 006/121] Fixed quotes --- src/Core/Exception/Server/InvalidContactServerException.php | 2 +- src/Core/Exception/Server/UnsupportedContactServerException.php | 2 +- .../Exception/Server/UnsupportedIdentifierServerException.php | 2 +- src/Core/Exception/Server/UserActionRequiredServerException.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Core/Exception/Server/InvalidContactServerException.php b/src/Core/Exception/Server/InvalidContactServerException.php index ffd457cb..c0095ae9 100644 --- a/src/Core/Exception/Server/InvalidContactServerException.php +++ b/src/Core/Exception/Server/InvalidContactServerException.php @@ -23,7 +23,7 @@ public function __construct(RequestInterface $request, $detail, \Exception $prev { parent::__construct( $request, - "[invalidContact] A contact URL for an account was invalid: ".$detail, + '[invalidContact] A contact URL for an account was invalid: '.$detail, $previous ); } diff --git a/src/Core/Exception/Server/UnsupportedContactServerException.php b/src/Core/Exception/Server/UnsupportedContactServerException.php index 1b43ce18..81d58a5a 100644 --- a/src/Core/Exception/Server/UnsupportedContactServerException.php +++ b/src/Core/Exception/Server/UnsupportedContactServerException.php @@ -23,7 +23,7 @@ public function __construct(RequestInterface $request, $detail, \Exception $prev { parent::__construct( $request, - "[unsupportedContact] A contact URL for an account used an unsupported protocol scheme: ".$detail, + '[unsupportedContact] A contact URL for an account used an unsupported protocol scheme: '.$detail, $previous ); } diff --git a/src/Core/Exception/Server/UnsupportedIdentifierServerException.php b/src/Core/Exception/Server/UnsupportedIdentifierServerException.php index b312d925..d1ef9af7 100644 --- a/src/Core/Exception/Server/UnsupportedIdentifierServerException.php +++ b/src/Core/Exception/Server/UnsupportedIdentifierServerException.php @@ -23,7 +23,7 @@ public function __construct(RequestInterface $request, $detail, \Exception $prev { parent::__construct( $request, - "[unsupportedIdentifier] An identifier is of an unsupported type: ".$detail, + '[unsupportedIdentifier] An identifier is of an unsupported type: '.$detail, $previous ); } diff --git a/src/Core/Exception/Server/UserActionRequiredServerException.php b/src/Core/Exception/Server/UserActionRequiredServerException.php index a61ad949..7493f32a 100644 --- a/src/Core/Exception/Server/UserActionRequiredServerException.php +++ b/src/Core/Exception/Server/UserActionRequiredServerException.php @@ -23,7 +23,7 @@ public function __construct(RequestInterface $request, $detail, \Exception $prev { parent::__construct( $request, - "[userActionRequired] Visit the “instance” URL and take actions specified there: ".$detail, + '[userActionRequired] Visit the “instance” URL and take actions specified there: '.$detail, $previous ); } From 6ec9c4774106bf89b6b63199fbb95860e26979bd Mon Sep 17 00:00:00 2001 From: Hans Adema Date: Mon, 14 Sep 2020 09:18:12 +0200 Subject: [PATCH 007/121] Support both Guzzle 6.x and 7.x --- composer.json | 2 +- src/Core/composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 0f79c2d4..dc5f369b 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "ext-openssl": "*", "lib-openssl": ">=0.9.8", "aws/aws-sdk-php": "^3.38", - "guzzlehttp/guzzle": "^6.0", + "guzzlehttp/guzzle": "^6.0|^7.0", "guzzlehttp/psr7": "^1.0", "league/flysystem": "^1.0.19", "league/flysystem-memory": "^1.0", diff --git a/src/Core/composer.json b/src/Core/composer.json index e512ecc6..b455719f 100644 --- a/src/Core/composer.json +++ b/src/Core/composer.json @@ -32,7 +32,7 @@ "ext-json": "*", "ext-openssl": "*", "acmephp/ssl": "^1.0", - "guzzlehttp/guzzle": "^6.0", + "guzzlehttp/guzzle": "^6.0|^7.0", "guzzlehttp/psr7": "^1.0", "psr/http-message": "^1.0", "psr/log": "^1.0", From 965c6b6da49541d8d616ec1ffb9f2fb03827aaf2 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 17 Oct 2020 14:49:27 +0200 Subject: [PATCH 008/121] Fix Box config for latest Box version --- box.json.dist | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/box.json.dist b/box.json.dist index c3d40d75..e26b939a 100644 --- a/box.json.dist +++ b/box.json.dist @@ -16,12 +16,12 @@ "in": "vendor" } ], - "compactors": [ "Herrera\\Box\\Compactor\\Php" ], + "compactors": [ "KevinGH\\Box\\Compactor\\Php" ], + "check-requirements": false, "compression": "GZ", "git-version": "package_version", "main": "bin/acme", "output": "build/acmephp.phar", - "chmod": "0755", - "stub": true, + "algorithm": "OPENSSL", "key": "../private.key" } From 5514e9152e4f49a9a8cc4c00bab4702d778bc2fa Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sun, 13 Dec 2020 22:20:21 +0100 Subject: [PATCH 009/121] Fix 1.0 CI --- .github/workflows/test-build.yaml | 32 +++++++++++++++++++ .gitignore | 1 + src/Core/Http/SecureHttpClient.php | 2 +- tests/Cli/AbstractApplicationTest.php | 4 +-- .../Cli/Repository/AbstractRepositoryTest.php | 2 +- tests/Core/Challenge/WaitingValidatorTest.php | 4 +-- tests/Ssl/Generator/KeyPairGeneratorTest.php | 2 +- tests/Ssl/Parser/CertificateParserTest.php | 2 +- tests/Ssl/Parser/KeyParserTest.php | 2 +- .../Signer/CertificateRequestSignerTest.php | 2 +- tests/Ssl/Signer/DataSignerTest.php | 2 +- 11 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/test-build.yaml diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml new file mode 100644 index 00000000..4164a9ee --- /dev/null +++ b/.github/workflows/test-build.yaml @@ -0,0 +1,32 @@ +name: Test and build + +on: push + +jobs: + php-cs: + runs-on: ubuntu-latest + steps: + - uses: shivammathur/setup-php@v2 + with: + php-version: '7.2' + - uses: actions/checkout@master + - name: php-cs-fixer + run: | + wget https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v2.16.4/php-cs-fixer.phar -q + php php-cs-fixer.phar fix --dry-run --diff + + tests: + runs-on: ubuntu-latest + steps: + - uses: shivammathur/setup-php@v2 + with: + php-version: '7.2' + - uses: actions/checkout@master + - name: Install dependencies + run: | + composer require --dev "sebastian/comparator:^2.0" + composer update --no-interaction --no-progress --ansi --prefer-lowest --prefer-stable + - name: Preparing tests + run: ./tests/setup.sh + - name: Running tests + run: ./tests/run.sh diff --git a/.gitignore b/.gitignore index 3cda68c4..afc37e8d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ vendor/ .php_cs.cache doc/.couscous .subsplit +.phpunit.result.cache diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index 862153d6..627ab05b 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -245,7 +245,7 @@ private function signPayload(array $protected, array $payload = null) $protected = $this->base64Encoder->encode(json_encode($protected, JSON_UNESCAPED_SLASHES)); if (null === $payload) { $payload = ''; - } elseif ($payload === []) { + } elseif ([] === $payload) { $payload = $this->base64Encoder->encode('{}'); } else { $payload = $this->base64Encoder->encode(json_encode($payload, JSON_UNESCAPED_SLASHES)); diff --git a/tests/Cli/AbstractApplicationTest.php b/tests/Cli/AbstractApplicationTest.php index b8f164d4..72a60072 100644 --- a/tests/Cli/AbstractApplicationTest.php +++ b/tests/Cli/AbstractApplicationTest.php @@ -36,14 +36,14 @@ abstract protected function getFixturesDirectories(); */ abstract protected function createApplication(); - public function setUp() + public function setUp(): void { $this->cleanContext(); $this->application = $this->createApplication(); } - public function tearDown() + public function tearDown(): void { $this->cleanContext(); } diff --git a/tests/Cli/Repository/AbstractRepositoryTest.php b/tests/Cli/Repository/AbstractRepositoryTest.php index 10680647..07094b3f 100644 --- a/tests/Cli/Repository/AbstractRepositoryTest.php +++ b/tests/Cli/Repository/AbstractRepositoryTest.php @@ -49,7 +49,7 @@ abstract class AbstractRepositoryTest extends TestCase */ protected $repository; - public function setUp() + public function setUp(): void { $this->serializer = new Serializer( [new PemNormalizer(), new GetSetMethodNormalizer()], diff --git a/tests/Core/Challenge/WaitingValidatorTest.php b/tests/Core/Challenge/WaitingValidatorTest.php index a2f19d67..555aaf32 100644 --- a/tests/Core/Challenge/WaitingValidatorTest.php +++ b/tests/Core/Challenge/WaitingValidatorTest.php @@ -19,7 +19,7 @@ class WaitingValidatorTest extends TestCase { - public function setUp() + public function setUp(): void { parent::setUp(); @@ -28,7 +28,7 @@ public function setUp() ClockMock::withClockMock(true); } - public function tearDown() + public function tearDown(): void { parent::tearDown(); diff --git a/tests/Ssl/Generator/KeyPairGeneratorTest.php b/tests/Ssl/Generator/KeyPairGeneratorTest.php index 23a8b722..e9d3612b 100644 --- a/tests/Ssl/Generator/KeyPairGeneratorTest.php +++ b/tests/Ssl/Generator/KeyPairGeneratorTest.php @@ -24,7 +24,7 @@ class KeyPairGeneratorTest extends TestCase /** @var KeyPairGenerator */ private $service; - public function setUp() + public function setUp(): void { parent::setUp(); diff --git a/tests/Ssl/Parser/CertificateParserTest.php b/tests/Ssl/Parser/CertificateParserTest.php index 309d7ef5..692f76a0 100644 --- a/tests/Ssl/Parser/CertificateParserTest.php +++ b/tests/Ssl/Parser/CertificateParserTest.php @@ -21,7 +21,7 @@ class CertificateParserTest extends TestCase /** @var CertificateParser */ private $service; - public function setUp() + public function setUp(): void { parent::setUp(); diff --git a/tests/Ssl/Parser/KeyParserTest.php b/tests/Ssl/Parser/KeyParserTest.php index 6b0ad0d9..0a2ea7fb 100644 --- a/tests/Ssl/Parser/KeyParserTest.php +++ b/tests/Ssl/Parser/KeyParserTest.php @@ -23,7 +23,7 @@ class KeyParserTest extends TestCase /** @var KeyParser */ private $service; - public function setUp() + public function setUp(): void { parent::setUp(); diff --git a/tests/Ssl/Signer/CertificateRequestSignerTest.php b/tests/Ssl/Signer/CertificateRequestSignerTest.php index 779c3a0d..20a2ba8e 100644 --- a/tests/Ssl/Signer/CertificateRequestSignerTest.php +++ b/tests/Ssl/Signer/CertificateRequestSignerTest.php @@ -23,7 +23,7 @@ class CertificateRequestSignerTest extends TestCase /** @var CertificateRequestSigner */ private $service; - public function setUp() + public function setUp(): void { parent::setUp(); diff --git a/tests/Ssl/Signer/DataSignerTest.php b/tests/Ssl/Signer/DataSignerTest.php index 36ded63c..91847b10 100644 --- a/tests/Ssl/Signer/DataSignerTest.php +++ b/tests/Ssl/Signer/DataSignerTest.php @@ -23,7 +23,7 @@ class DataSignerTest extends TestCase /** @var DataSigner */ private $service; - public function setUp() + public function setUp(): void { parent::setUp(); From 38a9a5fcccb0f2bcdbb99ed4eb237ca511491c24 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sun, 13 Dec 2020 22:05:00 +0100 Subject: [PATCH 010/121] Fix openssl_free_key deprecation notice in PHP 8 --- src/Ssl/Generator/OpensslPrivateKeyGeneratorTrait.php | 5 ++++- src/Ssl/Parser/KeyParser.php | 6 +++++- src/Ssl/PrivateKey.php | 5 ++++- src/Ssl/Signer/CertificateRequestSigner.php | 5 ++++- src/Ssl/Signer/DataSigner.php | 5 ++++- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/Ssl/Generator/OpensslPrivateKeyGeneratorTrait.php b/src/Ssl/Generator/OpensslPrivateKeyGeneratorTrait.php index 31677586..d2b8e005 100644 --- a/src/Ssl/Generator/OpensslPrivateKeyGeneratorTrait.php +++ b/src/Ssl/Generator/OpensslPrivateKeyGeneratorTrait.php @@ -28,7 +28,10 @@ private function generatePrivateKeyFromOpensslOptions(array $opensslOptions) throw new KeyPairGenerationException(sprintf('OpenSSL key export failed during generation with error: %s', openssl_error_string())); } - openssl_free_key($resource); + // PHP 8 automatically frees the key instance and deprecates the function + if (\PHP_VERSION_ID < 80000) { + openssl_free_key($resource); + } return new PrivateKey($privateKey); } diff --git a/src/Ssl/Parser/KeyParser.php b/src/Ssl/Parser/KeyParser.php index 423074d8..e4f205fb 100644 --- a/src/Ssl/Parser/KeyParser.php +++ b/src/Ssl/Parser/KeyParser.php @@ -37,7 +37,11 @@ public function parse(Key $key) } $rawData = openssl_pkey_get_details($resource); - openssl_free_key($resource); + + // PHP 8 automatically frees the key instance and deprecates the function + if (\PHP_VERSION_ID < 80000) { + openssl_free_key($resource); + } if (!\is_array($rawData)) { throw new KeyParsingException(sprintf('Fail to parse key with error: %s', openssl_error_string())); diff --git a/src/Ssl/PrivateKey.php b/src/Ssl/PrivateKey.php index 6bfe96d2..3f464bcf 100644 --- a/src/Ssl/PrivateKey.php +++ b/src/Ssl/PrivateKey.php @@ -43,7 +43,10 @@ public function getPublicKey() throw new KeyFormatException(sprintf('Failed to extract public key: %s', openssl_error_string())); } - openssl_free_key($resource); + // PHP 8 automatically frees the key instance and deprecates the function + if (\PHP_VERSION_ID < 80000) { + openssl_free_key($resource); + } return new PublicKey($details['key']); } diff --git a/src/Ssl/Signer/CertificateRequestSigner.php b/src/Ssl/Signer/CertificateRequestSigner.php index 3c6f4a90..acb6f85e 100644 --- a/src/Ssl/Signer/CertificateRequestSigner.php +++ b/src/Ssl/Signer/CertificateRequestSigner.php @@ -86,7 +86,10 @@ protected function createCsrWithSANsObject(CertificateRequest $certificateReques ] ); - openssl_free_key($resource); + // PHP 8 automatically frees the key instance and deprecates the function + if (\PHP_VERSION_ID < 80000) { + openssl_free_key($resource); + } if (!$csr) { throw new CSRSigningException(sprintf('OpenSSL CSR signing failed with error: %s', openssl_error_string())); diff --git a/src/Ssl/Signer/DataSigner.php b/src/Ssl/Signer/DataSigner.php index 13c0c590..5657ccd2 100644 --- a/src/Ssl/Signer/DataSigner.php +++ b/src/Ssl/Signer/DataSigner.php @@ -44,7 +44,10 @@ public function signData($data, PrivateKey $privateKey, $algorithm = OPENSSL_ALG throw new DataSigningException(sprintf('OpenSSL data signing failed with error: %s', openssl_error_string())); } - openssl_free_key($resource); + // PHP 8 automatically frees the key instance and deprecates the function + if (\PHP_VERSION_ID < 80000) { + openssl_free_key($resource); + } switch ($format) { case self::FORMAT_DER: From e849f3047bfec712dd831e531b878aad618bb24b Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sun, 13 Dec 2020 22:07:47 +0100 Subject: [PATCH 011/121] Do not check HTTPS certificate validity in HttpValidator --- src/Core/Challenge/Http/HttpValidator.php | 2 +- tests/Core/Challenge/Http/HttpValidatorTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Core/Challenge/Http/HttpValidator.php b/src/Core/Challenge/Http/HttpValidator.php index 03d7b686..7940941d 100644 --- a/src/Core/Challenge/Http/HttpValidator.php +++ b/src/Core/Challenge/Http/HttpValidator.php @@ -56,7 +56,7 @@ public function isValid(AuthorizationChallenge $authorizationChallenge) $checkContent = $this->extractor->getCheckContent($authorizationChallenge); try { - return $checkContent === trim($this->client->get($checkUrl)->getBody()->getContents()); + return $checkContent === trim($this->client->get($checkUrl, ['verify' => false])->getBody()->getContents()); } catch (ClientException $e) { return false; } diff --git a/tests/Core/Challenge/Http/HttpValidatorTest.php b/tests/Core/Challenge/Http/HttpValidatorTest.php index 6cc231f4..513dca2f 100644 --- a/tests/Core/Challenge/Http/HttpValidatorTest.php +++ b/tests/Core/Challenge/Http/HttpValidatorTest.php @@ -57,7 +57,7 @@ public function testIsValid() $mockExtractor->getCheckUrl($stubChallenge->reveal())->willReturn($checkUrl); $mockExtractor->getCheckContent($stubChallenge->reveal())->willReturn($checkContent); - $mockHttpClient->get($checkUrl)->willReturn($stubResponse->reveal()); + $mockHttpClient->get($checkUrl, ['verify' => false])->willReturn($stubResponse->reveal()); $stubResponse->getBody()->willReturn($stubStream->reveal()); $stubStream->getContents()->willReturn($checkContent); @@ -78,7 +78,7 @@ public function testIsValidCatchExceptions() $mockExtractor->getCheckUrl($stubChallenge->reveal())->willReturn($checkUrl); $mockExtractor->getCheckContent($stubChallenge->reveal())->willReturn($checkContent); - $mockHttpClient->get($checkUrl)->willThrow(new ClientException( + $mockHttpClient->get($checkUrl, ['verify' => false])->willThrow(new ClientException( 'boom', $this->prophesize(RequestInterface::class)->reveal(), $this->prophesize(ResponseInterface::class)->reveal() From 38d59299f5f7e8263e7bba936d186f8d33da1e63 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sun, 13 Dec 2020 23:03:32 +0100 Subject: [PATCH 012/121] Release of new version 1.3.0 --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81f8bdf4..8a317c5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,28 @@ +13/12/2020 23:03 1.3.0 v1.3.0 +5d37fb1 Merge pull request #218 from acmephp/do-not-verify-https-http-vaidator +fe50cf6 Merge pull request #217 from acmephp/openssl-php8 +e849f30 Do not check HTTPS certificate validity in HttpValidator +38a9a5f Fix openssl_free_key deprecation notice in PHP 8 +cb1eae4 Merge pull request #219 from acmephp/fix-tests +5514e91 Fix 1.0 CI +db4d497 Merge pull request #192 from acmephp/handle-processing +965c6b6 Fix Box config for latest Box version +6968927 Merge pull request #208 from InfinityFreeHosting/guzzle-7 +6ec9c47 Support both Guzzle 6.x and 7.x +9565469 Merge pull request #204 from p-seven-v/add-rejected-identifier-exception +a9ed8ac Fixed quotes +c0d3f0d Added more exceptions +3749f96 Reordered alphabetically +47c2139 Added class comment +765dd86 Added RejectedIdentifierServerException +852d90c Handle processing status case +5b07014 Merge pull request #193 from acmephp/update-ci +6133be4 Fix coding-style +252306a Update CI configuration +312ef14 Merge pull request #190 from philipsharp/response-body-summary +187ce72 Merge pull request #188 from miranovy/master +edcb011 Rewind response body before generating summary for server errors +a156f98 Distinguished name assert update 15/01/2020 22:42 1.2.0 Release version 1.2.0 d031223 Merge pull request #185 from miranovy/master 24b8575 Fix getIssuerCertificate return type From 2330ef62a2efdb8ffb0e498b5fad7728c89fe58b Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sun, 13 Dec 2020 23:14:07 +0100 Subject: [PATCH 013/121] Bump version --- src/Cli/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/Application.php b/src/Cli/Application.php index 8de32692..fc6aa772 100644 --- a/src/Cli/Application.php +++ b/src/Cli/Application.php @@ -30,7 +30,7 @@ */ class Application extends BaseApplication { - const VERSION = '1.2.0'; + const VERSION = '1.3.0'; /** * {@inheritdoc} From e2be9c3feee97b187abe837e9e3992395c7bca38 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 17 Oct 2020 15:24:41 +0200 Subject: [PATCH 014/121] Upgrade dependencies and drop support for PHP <7.2 --- .sensiolabs.yml | 3 --- .travis.yml | 13 ------------- composer.json | 23 ++++++++++++++--------- 3 files changed, 14 insertions(+), 25 deletions(-) delete mode 100644 .sensiolabs.yml diff --git a/.sensiolabs.yml b/.sensiolabs.yml deleted file mode 100644 index 23e1fe78..00000000 --- a/.sensiolabs.yml +++ /dev/null @@ -1,3 +0,0 @@ -commit_failure_conditions: - - "project.severity.critical > 0" - - "project.severity.major > 0" diff --git a/.travis.yml b/.travis.yml index 6a786133..92927460 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,6 @@ notifications: - ./tests/setup.sh install: composer install --no-interaction --no-progress --ansi script: ./tests/run.sh - env: - SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT: 1 cache: directories: - $HOME/.composer/cache/files @@ -32,14 +30,12 @@ notifications: - composer update --no-interaction --no-progress --ansi --prefer-lowest --prefer-stable env: COMMENT: "low deps" - SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT: 1 .template_phpunit_high: &phpunit_high <<: *phpunit install: composer update --no-interaction --no-progress --ansi env: COMMENT: "high deps" - SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT: 1 jobs: include: @@ -50,15 +46,6 @@ jobs: script: - php php-cs-fixer.phar fix --dry-run --diff - - <<: *phpunit - dist: trusty - php: 5.5 - - <<: *phpunit - php: 5.6 - - <<: *phpunit_low - php: 7.0 - - <<: *phpunit - php: 7.1 - <<: *phpunit php: 7.2 - <<: *phpunit diff --git a/composer.json b/composer.json index dc5f369b..2c8c32d4 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": ">=7.2.5", "ext-hash": "*", "ext-json": "*", "ext-filter": "*", @@ -50,12 +50,12 @@ "psr/http-message": "^1.0", "psr/log": "^1.0", "swiftmailer/swiftmailer": "^5.4|^6.0", - "symfony/config": "^3.0|^4.0", - "symfony/console": "^3.0|^4.0", - "symfony/dependency-injection": "^3.3|^4.0", - "symfony/filesystem": "^3.0|^4.0", - "symfony/serializer": "^3.0|^4.0", - "symfony/yaml": "^3.0|^4.0", + "symfony/config": "^4.4|^5.0", + "symfony/console": "^4.4|^5.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/filesystem": "^4.4|^5.0", + "symfony/serializer": "^4.4|^5.0", + "symfony/yaml": "^4.4|^5.0", "webmozart/assert": "^1.0", "webmozart/path-util": "^2.3", "alibabacloud/cdn": "^1.7", @@ -66,9 +66,9 @@ }, "require-dev": { "phpspec/prophecy": "^1.9", - "symfony/finder": "^3.4|^4.0", + "symfony/finder": "^4.4|^5.0", "symfony/phpunit-bridge": "^5.0", - "symfony/var-dumper": "^3.4|^4.0" + "symfony/var-dumper": "^4.4|^5.0" }, "autoload": { "psr-4": { @@ -80,6 +80,11 @@ "Tests\\AcmePhp\\": "tests/" } }, + "config": { + "platform": { + "php": "7.2.5" + } + }, "extra": { "branch-alias": { "dev-master": "1.0.x-dev" From e51784a08e1bafdaa1558da6668a022481684a04 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 17 Oct 2020 16:51:13 +0200 Subject: [PATCH 015/121] Remove deprecated commands, improve tests on Run command and merge v2 interfaces --- .travis.yml | 2 +- src/Cli/Application.php | 22 +- src/Cli/Command/AbstractCommand.php | 19 +- src/Cli/Command/AuthorizeCommand.php | 138 ------ src/Cli/Command/CheckCommand.php | 155 ------- src/Cli/Command/MonitoringTestCommand.php | 73 ---- src/Cli/Command/RegisterCommand.php | 96 ----- src/Cli/Command/RequestCommand.php | 404 ------------------ src/Cli/Command/RunCommand.php | 3 +- src/Cli/Command/StatusCommand.php | 2 +- src/Cli/Monitoring/EmailHandlerBuilder.php | 67 --- .../Monitoring/HandlerBuilderInterface.php | 29 -- .../Monitoring/MonitoringLoggerFactory.php | 62 --- src/Cli/Monitoring/SlackHandlerBuilder.php | 55 --- src/Cli/Repository/Repository.php | 95 +--- src/Cli/Repository/RepositoryInterface.php | 28 ++ src/Cli/Repository/RepositoryV2Interface.php | 48 --- src/Cli/Resources/services.xml | 45 +- src/Core/AcmeClient.php | 2 +- src/Core/AcmeClientInterface.php | 60 ++- src/Core/AcmeClientV2Interface.php | 81 ---- src/Core/Challenge/ChainValidator.php | 10 +- src/Core/Challenge/Dns/DnsValidator.php | 5 +- src/Core/Challenge/Http/HttpValidator.php | 7 +- src/Core/Challenge/Http/MockHttpValidator.php | 40 ++ .../Challenge/Http/MockServerHttpSolver.php | 58 +++ src/Core/Challenge/ValidatorInterface.php | 4 +- src/Core/Challenge/WaitingValidator.php | 8 +- tests/Cli/AbstractApplicationTest.php | 159 +------ .../Cli/Fixtures/config_sfpt_nginxproxy.yaml | 22 + tests/Cli/Fixtures/config_simple.yaml | 13 + tests/Cli/Fixtures/monitoring.conf | 20 - tests/Cli/Fixtures/sftp_nginxproxy.conf | 13 - tests/Cli/Fixtures/simple.conf | 5 - tests/Cli/Mock/AbstractTestApplication.php | 33 -- tests/Cli/Mock/MonitoredApplication.php | 25 -- tests/Cli/Mock/SftpNginxProxyApplication.php | 25 -- ...pleApplication.php => TestApplication.php} | 6 +- tests/Cli/MonitoredApplicationTest.php | 177 -------- ...tRepositoryTest.php => RepositoryTest.php} | 37 +- .../Repository/RepositoryWithBackupTest.php | 55 --- .../RepositoryWithoutBackupTest.php | 55 --- tests/Cli/SftpNginxProxyApplicationTest.php | 26 +- tests/Cli/SimpleApplicationTest.php | 15 +- tests/Core/Challenge/ChainValidatorTest.php | 25 +- tests/Core/Challenge/Dns/DnsValidatorTest.php | 9 +- .../Core/Challenge/Http/HttpValidatorTest.php | 9 +- tests/Core/Challenge/WaitingValidatorTest.php | 25 +- tests/setup.sh | 2 +- 49 files changed, 353 insertions(+), 2021 deletions(-) delete mode 100644 src/Cli/Command/AuthorizeCommand.php delete mode 100644 src/Cli/Command/CheckCommand.php delete mode 100644 src/Cli/Command/MonitoringTestCommand.php delete mode 100644 src/Cli/Command/RegisterCommand.php delete mode 100644 src/Cli/Command/RequestCommand.php delete mode 100644 src/Cli/Monitoring/EmailHandlerBuilder.php delete mode 100644 src/Cli/Monitoring/HandlerBuilderInterface.php delete mode 100644 src/Cli/Monitoring/MonitoringLoggerFactory.php delete mode 100644 src/Cli/Monitoring/SlackHandlerBuilder.php delete mode 100644 src/Cli/Repository/RepositoryV2Interface.php delete mode 100644 src/Core/AcmeClientV2Interface.php create mode 100644 src/Core/Challenge/Http/MockHttpValidator.php create mode 100644 src/Core/Challenge/Http/MockServerHttpSolver.php create mode 100644 tests/Cli/Fixtures/config_sfpt_nginxproxy.yaml create mode 100644 tests/Cli/Fixtures/config_simple.yaml delete mode 100644 tests/Cli/Fixtures/monitoring.conf delete mode 100644 tests/Cli/Fixtures/sftp_nginxproxy.conf delete mode 100644 tests/Cli/Fixtures/simple.conf delete mode 100644 tests/Cli/Mock/AbstractTestApplication.php delete mode 100644 tests/Cli/Mock/MonitoredApplication.php delete mode 100644 tests/Cli/Mock/SftpNginxProxyApplication.php rename tests/Cli/Mock/{SimpleApplication.php => TestApplication.php} (68%) delete mode 100644 tests/Cli/MonitoredApplicationTest.php rename tests/Cli/Repository/{AbstractRepositoryTest.php => RepositoryTest.php} (90%) delete mode 100644 tests/Cli/Repository/RepositoryWithBackupTest.php delete mode 100644 tests/Cli/Repository/RepositoryWithoutBackupTest.php diff --git a/.travis.yml b/.travis.yml index 92927460..3054ab55 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,7 +46,7 @@ jobs: script: - php php-cs-fixer.phar fix --dry-run --diff - - <<: *phpunit + - <<: *phpunit_low php: 7.2 - <<: *phpunit php: 7.3 diff --git a/src/Cli/Application.php b/src/Cli/Application.php index fc6aa772..f3e1dd9d 100644 --- a/src/Cli/Application.php +++ b/src/Cli/Application.php @@ -11,12 +11,7 @@ namespace AcmePhp\Cli; -use AcmePhp\Cli\Command\AuthorizeCommand; -use AcmePhp\Cli\Command\CheckCommand; use AcmePhp\Cli\Command\Helper\DistinguishedNameHelper; -use AcmePhp\Cli\Command\MonitoringTestCommand; -use AcmePhp\Cli\Command\RegisterCommand; -use AcmePhp\Cli\Command\RequestCommand; use AcmePhp\Cli\Command\RevokeCommand; use AcmePhp\Cli\Command\RunCommand; use AcmePhp\Cli\Command\SelfUpdateCommand; @@ -30,14 +25,12 @@ */ class Application extends BaseApplication { - const VERSION = '1.3.0'; - /** * {@inheritdoc} */ public function __construct() { - parent::__construct('Acme PHP - Let\'s Encrypt client', self::VERSION); + parent::__construct('Acme PHP - Let\'s Encrypt/ZeroSSL client', ''); } /** @@ -47,14 +40,9 @@ protected function getDefaultCommands() { return array_merge(parent::getDefaultCommands(), [ new RunCommand(), - new RegisterCommand(), - new AuthorizeCommand(), - new CheckCommand(), - new RequestCommand(), new RevokeCommand(), new StatusCommand(), new SelfUpdateCommand(), - new MonitoringTestCommand(), ]); } @@ -110,12 +98,4 @@ public function getStorageDirectory() { return Path::canonicalize('~/.acmephp/master'); } - - /** - * @return string - */ - public function getBackupDirectory() - { - return Path::canonicalize('~/.acmephp/backup'); - } } diff --git a/src/Cli/Command/AbstractCommand.php b/src/Cli/Command/AbstractCommand.php index cf8c0951..296decf2 100644 --- a/src/Cli/Command/AbstractCommand.php +++ b/src/Cli/Command/AbstractCommand.php @@ -12,10 +12,9 @@ namespace AcmePhp\Cli\Command; use AcmePhp\Cli\ActionHandler\ActionHandler; -use AcmePhp\Cli\Application; use AcmePhp\Cli\Configuration\AcmeConfiguration; use AcmePhp\Cli\Exception\CommandFlowException; -use AcmePhp\Cli\Repository\RepositoryV2Interface; +use AcmePhp\Cli\Repository\RepositoryInterface; use AcmePhp\Core\AcmeClient; use AcmePhp\Core\Challenge\Dns\LibDnsResolver; use AcmePhp\Core\Http\SecureHttpClient; @@ -69,7 +68,7 @@ protected function initialize(InputInterface $input, OutputInterface $output) } /** - * @return RepositoryV2Interface + * @return RepositoryInterface */ protected function getRepository() { @@ -78,16 +77,6 @@ protected function getRepository() return $this->getContainer()->get('repository'); } - /** - * @return ActionHandler - */ - protected function getActionHandler() - { - $this->debug('Loading action handler'); - - return $this->getContainer()->get('acmephp.action_handler'); - } - /** * @return AcmeClient */ @@ -141,16 +130,12 @@ private function initializeContainer() // Application services and parameters $this->container->set('app', $this->getApplication()); $this->container->set('container', $this->container); - $this->container->setParameter('app.version', Application::VERSION); $this->container->setParameter('app.storage_directory', $this->getApplication()->getStorageDirectory()); - $this->container->setParameter('app.backup_directory', $this->getApplication()->getBackupDirectory()); // Load configuration $processor = new Processor(); $config = $processor->processConfiguration(new AcmeConfiguration(), $this->configuration); - $this->container->setParameter('storage.enable_backup', $config['storage']['enable_backup']); $this->container->setParameter('storage.post_generate', $config['storage']['post_generate']); - $this->container->setParameter('monitoring.handlers', $config['monitoring']); // Load services $loader = new XmlFileLoader($this->container, new FileLocator(__DIR__.'/../Resources')); diff --git a/src/Cli/Command/AuthorizeCommand.php b/src/Cli/Command/AuthorizeCommand.php deleted file mode 100644 index c767b0d1..00000000 --- a/src/Cli/Command/AuthorizeCommand.php +++ /dev/null @@ -1,138 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\Command; - -use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface; -use AcmePhp\Core\Challenge\SolverInterface; -use AcmePhp\Core\Exception\Protocol\ChallengeNotSupportedException; -use AcmePhp\Core\Protocol\AuthorizationChallenge; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * @author Titouan Galopin - */ -class AuthorizeCommand extends AbstractCommand -{ - /** - * {@inheritdoc} - */ - protected function configure() - { - $this->setName('authorize') - ->setDefinition([ - new InputOption('solver', 's', InputOption::VALUE_REQUIRED, 'The type of challenge solver to use (available: http, dns, route53, gandi)', 'http'), - new InputArgument('domains', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'List of domains to ask an authorization for'), - ]) - ->setDescription('Ask the ACME server for an authorization token to check you are the owner of a domain') - ->setHelp(<<<'EOF' -The %command.name% command asks the ACME server for an authorization token. -You will then have to expose that token on a specific URL under that domain and ask for -the server to check you are the own of the domain by checking this URL. - -Ask the server for an authorization token: - - php %command.full_name% example.com www.exemple.org *.example.io - -Follow the instructions to expose your token on the specific URL, and then run the check -command to tell the server to check your token. -EOF - ); - } - - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->error('This command is deprecated. Use command "run" instead'); - - $client = $this->getClient(); - $domains = $input->getArgument('domains'); - - $solverName = strtolower($input->getOption('solver')); - - $this->debug('Locating solver', ['name' => $solverName]); - - $solverLocator = $this->getContainer()->get('acmephp.challenge_solver.locator'); - /** @var SolverInterface $solver */ - $solver = $solverLocator->get($solverName); - $this->debug('Solver found', ['name' => $solverName]); - - $this->notice(sprintf('Requesting an authorization token for domains %s ...', implode(', ', $domains))); - $order = $client->requestOrder($domains); - $this->notice('The authorization tokens was successfully fetched!'); - $authorizationChallengesToSolve = []; - foreach ($order->getAuthorizationsChallenges() as $domainKey => $authorizationChallenges) { - $authorizationChallenge = null; - foreach ($authorizationChallenges as $candidate) { - if ($solver->supports($candidate)) { - $authorizationChallenge = $candidate; - - $this->debug('Authorization challenge supported by solver', [ - 'solver' => $solverName, - 'challenge' => $candidate->getType(), - ]); - - break; - } - - $this->debug('Authorization challenge not supported by solver', [ - 'solver' => $solverName, - 'challenge' => $candidate->getType(), - ]); - } - if (null === $authorizationChallenge) { - throw new ChallengeNotSupportedException(); - } - $this->debug('Storing authorization challenge', [ - 'domain' => $domainKey, - 'challenge' => $authorizationChallenge->toArray(), - ]); - - $this->getRepository()->storeDomainAuthorizationChallenge($domainKey, $authorizationChallenge); - $authorizationChallengesToSolve[] = $authorizationChallenge; - } - if ($solver instanceof MultipleChallengesSolverInterface) { - $solver->solveAll($authorizationChallengesToSolve); - } else { - /** @var AuthorizationChallenge $authorizationChallenge */ - foreach ($authorizationChallengesToSolve as $authorizationChallenge) { - $this->info('Solving authorization challenge for domain', [ - 'domain' => $authorizationChallenge->getDomain(), - 'challenge' => $authorizationChallenge->toArray(), - ]); - $solver->solve($authorizationChallenge); - } - } - - $this->getRepository()->storeCertificateOrder($domains, $order); - - $this->info(sprintf( -<<<'EOF' -Then, you can ask to the CA to check the challenge! - Call the check command to ask the server to check your URL: - - php %s check -s %s %s - -EOF - , - $_SERVER['PHP_SELF'], - $solverName, - implode(' ', array_keys($order->getAuthorizationsChallenges())) - )); - - return 0; - } -} diff --git a/src/Cli/Command/CheckCommand.php b/src/Cli/Command/CheckCommand.php deleted file mode 100644 index 200d5ae1..00000000 --- a/src/Cli/Command/CheckCommand.php +++ /dev/null @@ -1,155 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\Command; - -use AcmePhp\Cli\Exception\CommandFlowException; -use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface; -use AcmePhp\Core\Challenge\SolverInterface; -use AcmePhp\Core\Challenge\ValidatorInterface; -use AcmePhp\Core\Exception\Protocol\ChallengeNotSupportedException; -use AcmePhp\Core\Protocol\AuthorizationChallenge; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * @author Titouan Galopin - */ -class CheckCommand extends AbstractCommand -{ - /** - * {@inheritdoc} - */ - protected function configure() - { - $this->setName('check') - ->setDefinition([ - new InputOption('solver', 's', InputOption::VALUE_REQUIRED, 'The type of challenge solver to use (available: http, dns, route53, gandi)', 'http'), - new InputOption('no-test', 't', InputOption::VALUE_NONE, 'Whether or not internal tests should be disabled'), - new InputArgument('domains', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'The list of domains to check the authorization for'), - ]) - ->setDescription('Ask the ACME server to check an authorization token you expose to prove you are the owner of a list of domains') - ->setHelp(<<<'EOF' -The %command.name% command asks the ACME server to check an authorization token -you exposed to prove you own a given list of domains. - -Once you are the proved owner of the domains, you can request SSL certificates for those domains. - -Use the authorize command before this one. -EOF - ); - } - - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->error('This command is deprecated. Use command "run" instead'); - - $repository = $this->getRepository(); - $client = $this->getClient(); - $domains = $input->getArgument('domains'); - - $solverName = strtolower($input->getOption('solver')); - - $this->debug('Locating solver', ['name' => $solverName]); - - $solverLocator = $this->getContainer()->get('acmephp.challenge_solver.locator'); - /** @var SolverInterface $solver */ - $solver = $solverLocator->get($solverName); - - $this->debug('Solver found', ['name' => $solverName]); - - /** @var ValidatorInterface $validator */ - $validator = $this->getContainer()->get('challenge_validator'); - - $this->notice(sprintf('Loading the order related to the domains %s ...', implode(', ', $domains))); - $order = null; - if ($this->getRepository()->hasCertificateOrder($domains)) { - $order = $this->getRepository()->loadCertificateOrder($domains); - } - - $this->notice(sprintf('Loading the authorization token for domains %s ...', implode(', ', $domains))); - $authorizationChallengeToCleanup = []; - foreach ($domains as $domain) { - if ($order) { - $authorizationChallenge = null; - $authorizationChallenges = $order->getAuthorizationChallenges($domain); - foreach ($authorizationChallenges as $challenge) { - if ($solver->supports($challenge)) { - $authorizationChallenge = $challenge; - break; - } - } - if (null === $authorizationChallenge) { - throw new ChallengeNotSupportedException(); - } - } else { - if (!$repository->hasDomainAuthorizationChallenge($domain)) { - throw new CommandFlowException('ask a challenge', 'authorize', [$domains]); - } - $authorizationChallenge = $repository->loadDomainAuthorizationChallenge($domain); - if (!$solver->supports($authorizationChallenge)) { - throw new ChallengeNotSupportedException(); - } - } - $this->debug('Challenge loaded', ['challenge' => $authorizationChallenge->toArray()]); - - $authorizationChallenge = $client->reloadAuthorization($authorizationChallenge); - if ($authorizationChallenge->isValid()) { - $this->notice(sprintf('The challenge is alread validated for domain %s ...', $domain)); - } else { - if (!$input->getOption('no-test')) { - $this->notice(sprintf('Testing the challenge for domain %s...', $domain)); - if (!$validator->isValid($authorizationChallenge)) { - $this->output->writeln(sprintf('Can not valid challenge for domain %s ...', $domain)); - } - } - - $this->notice(sprintf('Requesting authorization check for domain %s ...', $domain)); - $client->challengeAuthorization($authorizationChallenge); - $authorizationChallengeToCleanup[] = $authorizationChallenge; - } - } - - $this->info(sprintf(<<<'EOF' - -The authorization check was successful! - -You are now the proved owner of those domains %s. -Please note that you won't need to prove it anymore as long as you keep the same account key pair. - -You can now request a certificate for your domains: - - php %s request %s - -EOF - , - implode(', ', $domains), - $_SERVER['PHP_SELF'], - implode(' -a ', $domains) - )); - - if ($solver instanceof MultipleChallengesSolverInterface) { - $solver->cleanupAll($authorizationChallengeToCleanup); - } else { - /** @var AuthorizationChallenge $authorizationChallenge */ - foreach ($authorizationChallengeToCleanup as $authorizationChallenge) { - $solver->cleanup($authorizationChallenge); - } - } - - return 0; - } -} diff --git a/src/Cli/Command/MonitoringTestCommand.php b/src/Cli/Command/MonitoringTestCommand.php deleted file mode 100644 index 80e04bb1..00000000 --- a/src/Cli/Command/MonitoringTestCommand.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\Command; - -use AcmePhp\Cli\Exception\AcmeCliException; -use AcmePhp\Cli\Monitoring\HandlerBuilderInterface; -use Psr\Log\LoggerInterface; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * @author Titouan Galopin - */ -class MonitoringTestCommand extends AbstractCommand -{ - /** - * {@inheritdoc} - */ - protected function configure() - { - $this->setName('monitoring-test') - ->setDefinition([ - new InputArgument('level', InputArgument::OPTIONAL, 'The level to use for the test (info/error, by default error)', 'error'), - ]) - ->setDescription('Throw an error in a monitored context to test your configuration') - ->setHelp(<<<'EOF' -The %command.name% command list will set up the same monitored context as in your CRON -jobs and will voluntarily throw an error inside it so you can check you are successfully alerted if -there is a problem. -EOF - ); - } - - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->info('Loading monitoring configuration...'); - - /** @var LoggerInterface $monitoringLogger */ - $monitoringLogger = $this->getContainer()->get('acmephp.monitoring_factory')->createLogger(); - - $level = $input->getArgument('level'); - - if (!\in_array($level, [HandlerBuilderInterface::LEVEL_ERROR, HandlerBuilderInterface::LEVEL_INFO], true)) { - throw new AcmeCliException('Level '.$level.' is not valid (available levels: info, error)'); - } - - $this->info('Triggering monitoring on "'.$level.'" level...'); - - if (HandlerBuilderInterface::LEVEL_INFO === $level) { - $monitoringLogger->info('This is a testing message from Acme PHP monitoring (info level)'); - } else { - $monitoringLogger->alert('This is a testing message from Acme PHP monitoring (error level)'); - } - - $this->notice('Triggered successfully'); - $this->info('You should have been alerted'); - - return 0; - } -} diff --git a/src/Cli/Command/RegisterCommand.php b/src/Cli/Command/RegisterCommand.php deleted file mode 100644 index de703367..00000000 --- a/src/Cli/Command/RegisterCommand.php +++ /dev/null @@ -1,96 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\Command; - -use AcmePhp\Cli\Command\Helper\KeyOptionCommandTrait; -use AcmePhp\Ssl\KeyPair; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * @author Titouan Galopin - */ -class RegisterCommand extends AbstractCommand -{ - use KeyOptionCommandTrait; - - /** - * {@inheritdoc} - */ - protected function configure() - { - $this->setName('register') - ->setDefinition([ - new InputArgument('email', InputArgument::OPTIONAL, 'An e-mail to use when certificates will expire soon'), - new InputOption('agreement', null, InputOption::VALUE_REQUIRED, '[DEPRECATED] The server usage conditions you agree with (automatically agreed with all licenses)'), - new InputOption('key-type', 'k', InputOption::VALUE_REQUIRED, 'The type of private key used to sign certificates (one of RSA, EC)', 'RSA'), - ]) - ->setDescription('Register your account private key in the ACME server') - ->setHelp(<<<'EOF' -The %command.name% command register your account key in the ACME server -provided by the option --server (by default it will use Let's Encrypt servers). -This command will generate an account key if no account key exists in the storage. - -You can add an e-mail that will be added to your registration (required for Let's Encrypt): - - php %command.full_name% acmephp@example.com -EOF - ); - } - - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->error('This command is deprecated. Use command "run" instead'); - - $repository = $this->getRepository(); - - /* - * Generate account key pair if needed - */ - if (!$repository->hasAccountKeyPair()) { - $this->notice('No account key pair was found, generating one...'); - $this->debug('Generating a key pair'); - - /* @var KeyPair $accountKeyPair */ - $accountKeyPair = $this->getContainer()->get('ssl.key_pair_generator')->generateKeyPair( - $this->createKeyOption($input->getOption('key-type')) - ); - - $this->debug('Key pair generated, storing', ['public_key' => $accountKeyPair->getPublicKey()->getPEM()]); - $repository->storeAccountKeyPair($accountKeyPair); - } - - /* - * Register on server - */ - $client = $this->getClient(); - - $email = $input->getArgument('email') ?: null; - if ($input->getOption('agreement')) { - @trigger_error('The "agreement" option is deprecated since version 1.0 and will be removed in 2.0.', E_USER_DEPRECATED); - } - - $this->notice('Registering on the ACME server...'); - $this->debug('Registering your account on Acme server', ['email' => $email]); - - $client->registerAccount(null, $email); - - $this->notice('Account registered successfully!'); - - return 0; - } -} diff --git a/src/Cli/Command/RequestCommand.php b/src/Cli/Command/RequestCommand.php deleted file mode 100644 index d2845bbb..00000000 --- a/src/Cli/Command/RequestCommand.php +++ /dev/null @@ -1,404 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\Command; - -use AcmePhp\Cli\ActionHandler\ActionHandler; -use AcmePhp\Cli\Command\Helper\DistinguishedNameHelper; -use AcmePhp\Cli\Command\Helper\KeyOptionCommandTrait; -use AcmePhp\Cli\Exception\CommandFlowException; -use AcmePhp\Cli\Repository\Repository; -use AcmePhp\Cli\Repository\RepositoryInterface; -use AcmePhp\Core\AcmeClientV2Interface; -use AcmePhp\Ssl\CertificateRequest; -use AcmePhp\Ssl\CertificateResponse; -use AcmePhp\Ssl\DistinguishedName; -use AcmePhp\Ssl\KeyPair; -use AcmePhp\Ssl\ParsedCertificate; -use Psr\Log\LoggerInterface; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * @author Titouan Galopin - */ -class RequestCommand extends AbstractCommand -{ - use KeyOptionCommandTrait; - - /** - * @var RepositoryInterface - */ - private $repository; - - /** - * @var AcmeClientV2Interface - */ - private $client; - - /** - * @var ActionHandler - */ - private $actionHandler; - - /** - * {@inheritdoc} - */ - protected function configure() - { - $this->setName('request') - ->setDefinition([ - new InputArgument('domain', InputArgument::REQUIRED, 'The domain to get a certificate for'), - new InputOption('force', 'f', InputOption::VALUE_NONE, 'Whether to force renewal or not (by default, renewal will be done only if the certificate expire in less than a week)'), - new InputOption('country', null, InputOption::VALUE_REQUIRED, 'Your country two-letters code (field "C" of the distinguished name, for instance: "US")'), - new InputOption('province', null, InputOption::VALUE_REQUIRED, 'Your country province (field "ST" of the distinguished name, for instance: "California")'), - new InputOption('locality', null, InputOption::VALUE_REQUIRED, 'Your locality (field "L" of the distinguished name, for instance: "Mountain View")'), - new InputOption('organization', null, InputOption::VALUE_REQUIRED, 'Your organization/company (field "O" of the distinguished name, for instance: "Acme PHP")'), - new InputOption('unit', null, InputOption::VALUE_REQUIRED, 'Your unit/department in your organization (field "OU" of the distinguished name, for instance: "Sales")'), - new InputOption('email', null, InputOption::VALUE_REQUIRED, 'Your e-mail address (field "E" of the distinguished name)'), - new InputOption('alternative-name', 'a', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Alternative domains for this certificate'), - new InputOption('key-type', 'k', InputOption::VALUE_REQUIRED, 'The type of private key used to sign certificates (one of RSA, EC)', 'RSA'), - ]) - ->setDescription('Request a SSL certificate for a domain') - ->setHelp(<<<'EOF' -The %command.name% command requests to the ACME server a SSL certificate for a -given domain. - -This certificate will be stored in the Acme PHP storage directory. - -You need to be the proved owner of the domain you ask a certificate for. To prove your ownership -of the domain, please use commands authorize and check before this one. -EOF - ); - } - - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->error('This command is deprecated. Use command "run" instead'); - - $this->repository = $this->getRepository(); - $this->client = $this->getClient(); - $this->actionHandler = $this->getActionHandler(); - - $domain = $input->getArgument('domain'); - $alternativeNames = array_unique($input->getOption('alternative-name')); - sort($alternativeNames); - - // Certificate renewal - if ($this->hasValidCertificate($domain, $alternativeNames)) { - $this->debug('Certificate found, executing renewal', [ - 'domain' => $domain, - 'alternative_names' => $alternativeNames, - ]); - - return $this->executeRenewal($domain, $alternativeNames); - } - - $this->debug('No certificate found, executing first request', [ - 'domain' => $domain, - 'alternative_names' => $alternativeNames, - ]); - - // Certificate first request - return $this->executeFirstRequest($domain, $alternativeNames, $input->getOption('key-type')); - } - - private function hasValidCertificate($domain, array $alternativeNames) - { - if (!$this->repository->hasDomainCertificate($domain)) { - return false; - } - - if (!$this->repository->hasDomainKeyPair($domain)) { - return false; - } - - if (!$this->repository->hasDomainDistinguishedName($domain)) { - return false; - } - - if ($this->repository->loadDomainDistinguishedName($domain)->getSubjectAlternativeNames() !== $alternativeNames) { - return false; - } - - return true; - } - - /** - * Request a first certificate for the given domain. - * - * @param string $domain - * @param string $keyType - */ - private function executeFirstRequest($domain, array $alternativeNames, $keyType) - { - $introduction = <<<'EOF' - -There is currently no certificate for domain %s in the Acme PHP storage. As it is the -first time you request a certificate for this domain, some configuration is required. - -Generating domain key pair... -EOF; - - $this->info(sprintf($introduction, $domain)); - - /* @var KeyPair $domainKeyPair */ - $domainKeyPair = $this->getContainer()->get('ssl.key_pair_generator')->generateKeyPair( - $this->createKeyOption($keyType) - ); - $this->repository->storeDomainKeyPair($domain, $domainKeyPair); - - $this->debug('Domain key pair generated and stored', [ - 'domain' => $domain, - 'public_key' => $domainKeyPair->getPublicKey()->getPEM(), - ]); - - $distinguishedName = $this->getOrCreateDistinguishedName($domain, $alternativeNames); - $this->notice('Distinguished name informations have been stored locally for this domain (they won\'t be asked on renewal).'); - - // Order - $domains = array_merge([$domain], $alternativeNames); - $this->notice(sprintf('Loading the order related to the domains %s ...', implode(', ', $domains))); - if (!$this->getRepository()->hasCertificateOrder($domains)) { - throw new CommandFlowException('ask a challenge', 'authorize', $domains); - } - $order = $this->getRepository()->loadCertificateOrder($domains); - - // Request - $this->notice(sprintf('Requesting first certificate for domain %s ...', $domain)); - $csr = new CertificateRequest($distinguishedName, $domainKeyPair); - $response = $this->client->finalizeOrder($order, $csr); - $this->debug('Certificate received', ['certificate' => $response->getCertificate()->getPEM()]); - - // Store - $this->repository->storeDomainCertificate($domain, $response->getCertificate()); - $this->debug('Certificate stored'); - - // Post-generate actions - $this->notice('Running post-generate actions...'); - $this->actionHandler->handle($response); - - // Success message - /** @var ParsedCertificate $parsedCertificate */ - $parsedCertificate = $this->getContainer()->get('ssl.certificate_parser')->parse($response->getCertificate()); - - $success = <<<'EOF' - -The SSL certificate was fetched successfully! - -This certificate is valid from now to %expiration%. - -5 files were created in the Acme PHP storage directory: - - * %private% contains your domain private key (required in many cases). - - * %cert% contains only your certificate, without the issuer certificate. - It may be useful in certains cases but you will probably not need it (use fullchain.pem instead). - - * %chain% contains the issuer certificate chain (its certificate, the - certificate of its issuer, the certificate of the issuer of its issuer, etc.). Your certificate is - not present in this file. - - * %fullchain% contains your certificate AND the issuer certificate chain. - You most likely will use this file in your webserver. - - * %combined% contains the fullchain AND your domain private key (some - webservers expect this format such as haproxy). - -Read the documentation at https://acmephp.github.io/documentation/ to learn more about how to -configure your web server and set up automatic renewal. - -To renew your certificate manually, simply re-run this command. - -EOF; - - $masterPath = $this->getContainer()->getParameter('app.storage_directory'); - - $replacements = [ - '%expiration%' => $parsedCertificate->getValidTo()->format(\DateTime::ISO8601), - '%private%' => $masterPath.'/'.Repository::PATH_DOMAIN_KEY_PRIVATE, - '%combined%' => $masterPath.'/'.Repository::PATH_DOMAIN_CERT_COMBINED, - '%cert%' => $masterPath.'/'.Repository::PATH_DOMAIN_CERT_CERT, - '%chain%' => $masterPath.'/'.Repository::PATH_DOMAIN_CERT_CHAIN, - '%fullchain%' => $masterPath.'/'.Repository::PATH_DOMAIN_CERT_FULLCHAIN, - ]; - - $this->info(strtr(strtr($success, $replacements), ['{domain}' => $domain])); - - return 0; - } - - /** - * Renew a given domain certificate. - * - * @param string $domain - */ - private function executeRenewal($domain, array $alternativeNames) - { - /** @var LoggerInterface $monitoringLogger */ - $monitoringLogger = $this->getContainer()->get('acmephp.monitoring_factory')->createLogger(); - - try { - // Check expiration date to avoid too much renewal - $this->debug('Loading current certificate', [ - 'domain' => $domain, - ]); - - $certificate = $this->repository->loadDomainCertificate($domain); - - if (!$this->input->getOption('force')) { - /** @var ParsedCertificate $parsedCertificate */ - $parsedCertificate = $this->getContainer()->get('ssl.certificate_parser')->parse($certificate); - - if ($parsedCertificate->getValidTo()->format('U') - time() >= 604800) { - $monitoringLogger->debug('Certificate does not need renewal', [ - 'domain' => $domain, - 'valid_until' => $parsedCertificate->getValidTo()->format('Y-m-d H:i:s'), - ]); - - $this->notice(sprintf( - 'Current certificate is valid until %s, renewal is not necessary. Use --force to force renewal.', - $parsedCertificate->getValidTo()->format('Y-m-d H:i:s')) - ); - - // Post-generate actions - $this->info('Running post-generate actions...'); - $response = new CertificateResponse( - new CertificateRequest( - $this->repository->loadDomainDistinguishedName($domain), - $this->repository->loadDomainKeyPair($domain) - ), - $certificate - ); - - $this->actionHandler->handle($response); - - return; - } - - $monitoringLogger->debug('Certificate needs renewal', [ - 'domain' => $domain, - 'valid_until' => $parsedCertificate->getValidTo()->format('Y-m-d H:i:s'), - ]); - - $this->notice(sprintf( - 'Current certificate will expire in less than a week (%s), renewal is required.', - $parsedCertificate->getValidTo()->format('Y-m-d H:i:s')) - ); - } else { - $this->notice('Forced renewal.'); - } - - // Key pair - $this->info('Loading domain key pair...'); - $domainKeyPair = $this->repository->loadDomainKeyPair($domain); - - // Distinguished name - $this->info('Loading domain distinguished name...'); - $distinguishedName = $this->getOrCreateDistinguishedName($domain, $alternativeNames); - - // Order - $domains = array_merge([$domain], $alternativeNames); - $this->notice(sprintf('Loading the order related to the domains %s ...', implode(', ', $domains))); - if (!$this->getRepository()->hasCertificateOrder($domains)) { - throw new CommandFlowException('ask a challenge', 'authorize', $domains); - } - $order = $this->getRepository()->loadCertificateOrder($domains); - - // Renewal - $this->info(sprintf('Renewing certificate for domain %s ...', $domain)); - $csr = new CertificateRequest($distinguishedName, $domainKeyPair); - $response = $this->client->finalizeOrder($order, $csr); - $this->debug('Certificate received', ['certificate' => $response->getCertificate()->getPEM()]); - - $this->repository->storeDomainCertificate($domain, $response->getCertificate()); - $this->debug('Certificate stored'); - - // Post-generate actions - $this->info('Running post-generate actions...'); - $this->actionHandler->handle($response); - - $this->notice('Certificate renewed successfully!'); - - $monitoringLogger->info('Certificate renewed successfully', ['domain' => $domain]); - } catch (\Exception $e) { - $monitoringLogger->alert('A critical error occured during certificate renewal', ['exception' => $e]); - - throw $e; - } catch (\Throwable $e) { - $monitoringLogger->alert('A critical error occured during certificate renewal', ['exception' => $e]); - - throw $e; - } - - return 0; - } - - /** - * Retrieve the stored distinguishedName or create a new one if needed. - * - * @param string $domain - * - * @return DistinguishedName - */ - private function getOrCreateDistinguishedName($domain, array $alternativeNames) - { - if ($this->repository->hasDomainDistinguishedName($domain)) { - $original = $this->repository->loadDomainDistinguishedName($domain); - - $distinguishedName = new DistinguishedName( - $domain, - $this->input->getOption('country') ?: $original->getCountryName(), - $this->input->getOption('province') ?: $original->getStateOrProvinceName(), - $this->input->getOption('locality') ?: $original->getLocalityName(), - $this->input->getOption('organization') ?: $original->getOrganizationName(), - $this->input->getOption('unit') ?: $original->getOrganizationalUnitName(), - $this->input->getOption('email') ?: $original->getEmailAddress(), - $alternativeNames - ); - } else { - // Ask DistinguishedName - $distinguishedName = new DistinguishedName( - $domain, - $this->input->getOption('country'), - $this->input->getOption('province'), - $this->input->getOption('locality'), - $this->input->getOption('organization'), - $this->input->getOption('unit'), - $this->input->getOption('email'), - $alternativeNames - ); - - /** @var DistinguishedNameHelper $helper */ - $helper = $this->getHelper('distinguished_name'); - - if (!$helper->isReadyForRequest($distinguishedName)) { - $this->info("\n\nSome informations about you or your company are required for the certificate:\n"); - - $distinguishedName = $helper->ask( - $this->getHelper('question'), - $this->input, - $this->output, - $distinguishedName - ); - } - } - - $this->repository->storeDomainDistinguishedName($domain, $distinguishedName); - - return $distinguishedName; - } -} diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index 8204ee42..b3cd6493 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -78,6 +78,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $keyOption = $this->createKeyOption($config['key_type']); $this->register($config['contact_email'], $keyOption); + foreach ($config['certificates'] as $domainConfig) { $domain = $domainConfig['domain']; @@ -289,7 +290,7 @@ private function challengeDomains(array $domainConfig) } $this->output->writeln(sprintf('Testing the challenge for domain %s...', $domain)); - if (time() - $startTestTime > 180 || !$validator->isValid($authorizationChallenge)) { + if (time() - $startTestTime > 180 || !$validator->isValid($authorizationChallenge, $solver)) { $this->output->writeln(sprintf('Can not self validate challenge for domain %s. Maybe letsencrypt will be able to do it...', $domain)); } diff --git a/src/Cli/Command/StatusCommand.php b/src/Cli/Command/StatusCommand.php index daec4282..524b9b56 100644 --- a/src/Cli/Command/StatusCommand.php +++ b/src/Cli/Command/StatusCommand.php @@ -46,7 +46,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $repository = $this->getRepository(); /** @var FilesystemInterface $master */ - $master = $this->getContainer()->get('repository.master_storage'); + $master = $this->getContainer()->get('repository.storage'); /** @var CertificateParser $certificateParser */ $certificateParser = $this->getContainer()->get('ssl.certificate_parser'); diff --git a/src/Cli/Monitoring/EmailHandlerBuilder.php b/src/Cli/Monitoring/EmailHandlerBuilder.php deleted file mode 100644 index dfd00d1d..00000000 --- a/src/Cli/Monitoring/EmailHandlerBuilder.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\Monitoring; - -use AcmePhp\Cli\Exception\AcmeCliException; -use Monolog\Handler\FingersCrossedHandler; -use Monolog\Handler\SwiftMailerHandler; -use Monolog\Logger; - -/** - * @author Titouan Galopin - */ -class EmailHandlerBuilder implements HandlerBuilderInterface -{ - private static $defaults = [ - 'from' => 'monitoring@acmephp.github.io', - 'subject' => 'An error occured during Acme PHP CRON renewal', - 'port' => 25, - 'username' => null, - 'password' => null, - 'encryption' => null, - 'level' => Logger::ERROR, - ]; - - /** - * {@inheritdoc} - */ - public function createHandler($config) - { - if (!isset($config['host'])) { - throw new AcmeCliException('The SMTP host (key "host") is required in the email monitoring alert handler.'); - } - - if (!isset($config['to'])) { - throw new AcmeCliException('The mail recipient (key "to") is required in the email monitoring alert handler.'); - } - - $config = array_merge(self::$defaults, $config); - - $transport = new \Swift_SmtpTransport($config['host'], $config['port'], $config['encryption']); - - if ($config['username']) { - $transport->setUsername($config['username']); - } - - if ($config['password']) { - $transport->setPassword($config['password']); - } - - $message = new \Swift_Message($config['subject']); - $message->setFrom($config['from']); - $message->setTo($config['to']); - - $handler = new SwiftMailerHandler(new \Swift_Mailer($transport), $message); - - return new FingersCrossedHandler($handler, $config['level']); - } -} diff --git a/src/Cli/Monitoring/HandlerBuilderInterface.php b/src/Cli/Monitoring/HandlerBuilderInterface.php deleted file mode 100644 index a47aa163..00000000 --- a/src/Cli/Monitoring/HandlerBuilderInterface.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\Monitoring; - -use Monolog\Handler\HandlerInterface; - -interface HandlerBuilderInterface -{ - const LEVEL_ERROR = 'error'; - const LEVEL_INFO = 'info'; - - /** - * Create a handler usable with Monolog given a configuration. - * - * @param array $config - * - * @return HandlerInterface - */ - public function createHandler($config); -} diff --git a/src/Cli/Monitoring/MonitoringLoggerFactory.php b/src/Cli/Monitoring/MonitoringLoggerFactory.php deleted file mode 100644 index a65c84b8..00000000 --- a/src/Cli/Monitoring/MonitoringLoggerFactory.php +++ /dev/null @@ -1,62 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\Monitoring; - -use AcmePhp\Cli\Exception\AcmeCliException; -use Monolog\Logger; -use Psr\Container\ContainerInterface; -use Psr\Log\NullLogger; - -/** - * @author Titouan Galopin - */ -class MonitoringLoggerFactory -{ - private $monitoringLocator; - private $monitoringConfig; - - private static $levels = [ - 'info' => Logger::INFO, - 'error' => Logger::ERROR, - ]; - - public function __construct(ContainerInterface $monitoringLocator, array $monitoringConfig) - { - $this->monitoringLocator = $monitoringLocator; - $this->monitoringConfig = $monitoringConfig; - } - - public function createLogger() - { - if (!$this->monitoringConfig) { - return new NullLogger(); - } - - $logger = new Logger('acmephp'); - - foreach ($this->monitoringConfig as $name => $config) { - if (isset($config['level'])) { - if (!isset(self::$levels[$config['level']])) { - throw new AcmeCliException(sprintf('Monitoring handler level "%s" is not valid.', $config['level'])); - } - - $config['level'] = self::$levels[$config['level']]; - } else { - $config['level'] = null; - } - - $logger->pushHandler($this->monitoringLocator->get($name)->createHandler($config)); - } - - return $logger; - } -} diff --git a/src/Cli/Monitoring/SlackHandlerBuilder.php b/src/Cli/Monitoring/SlackHandlerBuilder.php deleted file mode 100644 index d02e1c11..00000000 --- a/src/Cli/Monitoring/SlackHandlerBuilder.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\Monitoring; - -use AcmePhp\Cli\Exception\AcmeCliException; -use Monolog\Handler\FingersCrossedHandler; -use Monolog\Handler\SlackHandler; -use Monolog\Logger; - -/** - * @author Titouan Galopin - */ -class SlackHandlerBuilder implements HandlerBuilderInterface -{ - private static $defaults = [ - 'username' => 'Acme PHP', - 'level' => Logger::INFO, - ]; - - /** - * {@inheritdoc} - */ - public function createHandler($config) - { - if (!isset($config['token'])) { - throw new AcmeCliException('The Slack token (key "token") is required in the slack monitoring alert handler.'); - } - - if (!isset($config['channel'])) { - throw new AcmeCliException('The Slack channel (key "channel") is required in the slack monitoring alert handler.'); - } - - $config = array_merge(self::$defaults, $config); - - $handler = new SlackHandler( - $config['token'], - '#'.ltrim($config['channel'], '#'), - $config['username'], - true, - null, - Logger::DEBUG - ); - - return new FingersCrossedHandler($handler, $config['level']); - } -} diff --git a/src/Cli/Repository/Repository.php b/src/Cli/Repository/Repository.php index bcbf3dbe..68a21dc9 100644 --- a/src/Cli/Repository/Repository.php +++ b/src/Cli/Repository/Repository.php @@ -28,7 +28,7 @@ /** * @author Titouan Galopin */ -class Repository implements RepositoryV2Interface +class Repository implements RepositoryInterface { const PATH_ACCOUNT_KEY_PRIVATE = 'account/key.private.pem'; const PATH_ACCOUNT_KEY_PUBLIC = 'account/key.public.pem'; @@ -52,27 +52,12 @@ class Repository implements RepositoryV2Interface /** * @var FilesystemInterface */ - private $master; + private $storage; - /** - * @var FilesystemInterface - */ - private $backup; - - /** - * @var bool - */ - private $enableBackup; - - /** - * @param bool $enableBackup - */ - public function __construct(SerializerInterface $serializer, FilesystemInterface $master, FilesystemInterface $backup, $enableBackup) + public function __construct(SerializerInterface $serializer, FilesystemInterface $storage) { $this->serializer = $serializer; - $this->master = $master; - $this->backup = $backup; - $this->enableBackup = $enableBackup; + $this->storage = $storage; } /** @@ -123,7 +108,7 @@ private function getPathForDomainList($path, array $domains) */ public function hasAccountKeyPair() { - return $this->master->has(self::PATH_ACCOUNT_KEY_PRIVATE); + return $this->storage->has(self::PATH_ACCOUNT_KEY_PRIVATE); } /** @@ -132,8 +117,8 @@ public function hasAccountKeyPair() public function loadAccountKeyPair() { try { - $publicKeyPem = $this->master->read(self::PATH_ACCOUNT_KEY_PUBLIC); - $privateKeyPem = $this->master->read(self::PATH_ACCOUNT_KEY_PRIVATE); + $publicKeyPem = $this->storage->read(self::PATH_ACCOUNT_KEY_PUBLIC); + $privateKeyPem = $this->storage->read(self::PATH_ACCOUNT_KEY_PRIVATE); return new KeyPair( $this->serializer->deserialize($publicKeyPem, PublicKey::class, PemEncoder::FORMAT), @@ -169,7 +154,7 @@ public function storeDomainKeyPair($domain, KeyPair $keyPair) */ public function hasDomainKeyPair($domain) { - return $this->master->has($this->getPathForDomain(self::PATH_DOMAIN_KEY_PRIVATE, $domain)); + return $this->storage->has($this->getPathForDomain(self::PATH_DOMAIN_KEY_PRIVATE, $domain)); } /** @@ -178,8 +163,8 @@ public function hasDomainKeyPair($domain) public function loadDomainKeyPair($domain) { try { - $publicKeyPem = $this->master->read($this->getPathForDomain(self::PATH_DOMAIN_KEY_PUBLIC, $domain)); - $privateKeyPem = $this->master->read($this->getPathForDomain(self::PATH_DOMAIN_KEY_PRIVATE, $domain)); + $publicKeyPem = $this->storage->read($this->getPathForDomain(self::PATH_DOMAIN_KEY_PUBLIC, $domain)); + $privateKeyPem = $this->storage->read($this->getPathForDomain(self::PATH_DOMAIN_KEY_PRIVATE, $domain)); return new KeyPair( $this->serializer->deserialize($publicKeyPem, PublicKey::class, PemEncoder::FORMAT), @@ -210,7 +195,7 @@ public function storeDomainAuthorizationChallenge($domain, AuthorizationChalleng */ public function hasDomainAuthorizationChallenge($domain) { - return $this->master->has($this->getPathForDomain(self::PATH_CACHE_AUTHORIZATION_CHALLENGE, $domain)); + return $this->storage->has($this->getPathForDomain(self::PATH_CACHE_AUTHORIZATION_CHALLENGE, $domain)); } /** @@ -219,7 +204,7 @@ public function hasDomainAuthorizationChallenge($domain) public function loadDomainAuthorizationChallenge($domain) { try { - $json = $this->master->read($this->getPathForDomain(self::PATH_CACHE_AUTHORIZATION_CHALLENGE, $domain)); + $json = $this->storage->read($this->getPathForDomain(self::PATH_CACHE_AUTHORIZATION_CHALLENGE, $domain)); return $this->serializer->deserialize($json, AuthorizationChallenge::class, JsonEncoder::FORMAT); } catch (\Exception $e) { @@ -247,7 +232,7 @@ public function storeDomainDistinguishedName($domain, DistinguishedName $disting */ public function hasDomainDistinguishedName($domain) { - return $this->master->has($this->getPathForDomain(self::PATH_CACHE_DISTINGUISHED_NAME, $domain)); + return $this->storage->has($this->getPathForDomain(self::PATH_CACHE_DISTINGUISHED_NAME, $domain)); } /** @@ -256,7 +241,7 @@ public function hasDomainDistinguishedName($domain) public function loadDomainDistinguishedName($domain) { try { - $json = $this->master->read($this->getPathForDomain(self::PATH_CACHE_DISTINGUISHED_NAME, $domain)); + $json = $this->storage->read($this->getPathForDomain(self::PATH_CACHE_DISTINGUISHED_NAME, $domain)); return $this->serializer->deserialize($json, DistinguishedName::class, JsonEncoder::FORMAT); } catch (\Exception $e) { @@ -302,7 +287,7 @@ public function storeDomainCertificate($domain, Certificate $certificate) */ public function hasDomainCertificate($domain) { - return $this->master->has($this->getPathForDomain(self::PATH_DOMAIN_CERT_FULLCHAIN, $domain)); + return $this->storage->has($this->getPathForDomain(self::PATH_DOMAIN_CERT_FULLCHAIN, $domain)); } /** @@ -311,7 +296,7 @@ public function hasDomainCertificate($domain) public function loadDomainCertificate($domain) { try { - $pems = explode('-----BEGIN CERTIFICATE-----', $this->master->read($this->getPathForDomain(self::PATH_DOMAIN_CERT_FULLCHAIN, $domain))); + $pems = explode('-----BEGIN CERTIFICATE-----', $this->storage->read($this->getPathForDomain(self::PATH_DOMAIN_CERT_FULLCHAIN, $domain))); } catch (\Exception $e) { throw new AcmeCliException(sprintf('Loading of domain %s certificate failed', $domain), $e); } @@ -354,7 +339,7 @@ public function storeCertificateOrder(array $domains, CertificateOrder $order) */ public function hasCertificateOrder(array $domains) { - return $this->master->has($this->getPathForDomainList(self::PATH_CACHE_CERTIFICATE_ORDER, $domains)); + return $this->storage->has($this->getPathForDomainList(self::PATH_CACHE_CERTIFICATE_ORDER, $domains)); } /** @@ -363,7 +348,7 @@ public function hasCertificateOrder(array $domains) public function loadCertificateOrder(array $domains) { try { - $json = $this->master->read($this->getPathForDomainList(self::PATH_CACHE_CERTIFICATE_ORDER, $domains)); + $json = $this->storage->read($this->getPathForDomainList(self::PATH_CACHE_CERTIFICATE_ORDER, $domains)); return $this->serializer->deserialize($json, CertificateOrder::class, JsonEncoder::FORMAT); } catch (\Exception $e) { @@ -376,49 +361,13 @@ public function loadCertificateOrder(array $domains) */ public function save($path, $content, $visibility = self::VISIBILITY_PRIVATE) { - if (!$this->master->has($path)) { - // File creation: remove from backup if it existed and warm-up both master and backup - $this->createAndBackup($path, $content); + if (!$this->storage->has($path)) { + $this->storage->write($path, $content); } else { - // File update: backup before writing - $this->backupAndUpdate($path, $content); - } - - if ($this->enableBackup) { - $this->backup->setVisibility($path, $visibility); - } - - $this->master->setVisibility($path, $visibility); - } - - private function createAndBackup($path, $content) - { - if ($this->enableBackup) { - if ($this->backup->has($path)) { - $this->backup->delete($path); - } - - $this->backup->write($path, $content); - } - - $this->master->write($path, $content); - } - - private function backupAndUpdate($path, $content) - { - if ($this->enableBackup) { - $oldContent = $this->master->read($path); - - if (false !== $oldContent) { - if ($this->backup->has($path)) { - $this->backup->update($path, $oldContent); - } else { - $this->backup->write($path, $oldContent); - } - } + $this->storage->update($path, $content); } - $this->master->update($path, $content); + $this->storage->setVisibility($path, $visibility); } private function normalizeDomain($domain) diff --git a/src/Cli/Repository/RepositoryInterface.php b/src/Cli/Repository/RepositoryInterface.php index c36a285d..c04f1fe1 100644 --- a/src/Cli/Repository/RepositoryInterface.php +++ b/src/Cli/Repository/RepositoryInterface.php @@ -13,6 +13,7 @@ use AcmePhp\Cli\Exception\AcmeCliException; use AcmePhp\Core\Protocol\AuthorizationChallenge; +use AcmePhp\Core\Protocol\CertificateOrder; use AcmePhp\Ssl\Certificate; use AcmePhp\Ssl\CertificateResponse; use AcmePhp\Ssl\DistinguishedName; @@ -26,6 +27,33 @@ interface RepositoryInterface const VISIBILITY_PUBLIC = 'public'; const VISIBILITY_PRIVATE = 'private'; + /** + * Store a given certificate as associated to a given domain. + * + * @throws AcmeCliException + */ + public function storeCertificateOrder(array $domains, CertificateOrder $order); + + /** + * Check if there is a certificate associated to the given domain in the repository. + * + * @param string $domain + * + * @return bool + */ + public function hasCertificateOrder(array $domains); + + /** + * Load the certificate associated to a given domain. + * + * @param string $domain + * + * @throws AcmeCliException + * + * @return CertificateOrder + */ + public function loadCertificateOrder(array $domains); + /** * Extract important elements from the given certificate response and store them * in the repository. diff --git a/src/Cli/Repository/RepositoryV2Interface.php b/src/Cli/Repository/RepositoryV2Interface.php deleted file mode 100644 index 26d0d9e9..00000000 --- a/src/Cli/Repository/RepositoryV2Interface.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\Repository; - -use AcmePhp\Cli\Exception\AcmeCliException; -use AcmePhp\Core\Protocol\CertificateOrder; - -/** - * @author Titouan Galopin - */ -interface RepositoryV2Interface extends RepositoryInterface -{ - /** - * Store a given certificate as associated to a given domain. - * - * @throws AcmeCliException - */ - public function storeCertificateOrder(array $domains, CertificateOrder $order); - - /** - * Check if there is a certificate associated to the given domain in the repository. - * - * @param string $domain - * - * @return bool - */ - public function hasCertificateOrder(array $domains); - - /** - * Load the certificate associated to a given domain. - * - * @param string $domain - * - * @throws AcmeCliException - * - * @return CertificateOrder - */ - public function loadCertificateOrder(array $domains); -} diff --git a/src/Cli/Resources/services.xml b/src/Cli/Resources/services.xml index 0a511ea9..0cff0233 100644 --- a/src/Cli/Resources/services.xml +++ b/src/Cli/Resources/services.xml @@ -69,7 +69,7 @@ - + %app.storage_directory% @@ -77,19 +77,9 @@ - - - - %app.backup_directory% - - - - - - - %storage.enable_backup% + @@ -106,7 +96,7 @@ - + @@ -134,30 +124,6 @@ - - - - %storage.post_generate% - - - - - - - - - - - - - - - - - - %monitoring.handlers% - - @@ -194,6 +160,9 @@ + + + @@ -240,9 +209,11 @@ + + diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 7d62b47e..75e84fda 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -34,7 +34,7 @@ * * @author Titouan Galopin */ -class AcmeClient implements AcmeClientV2Interface +class AcmeClient implements AcmeClientInterface { /** * @var SecureHttpClient diff --git a/src/Core/AcmeClientInterface.php b/src/Core/AcmeClientInterface.php index 148714d3..554c8484 100644 --- a/src/Core/AcmeClientInterface.php +++ b/src/Core/AcmeClientInterface.php @@ -19,8 +19,8 @@ use AcmePhp\Core\Exception\Protocol\ChallengeFailedException; use AcmePhp\Core\Exception\Protocol\ChallengeNotSupportedException; use AcmePhp\Core\Exception\Protocol\ChallengeTimedOutException; -use AcmePhp\Core\Http\SecureHttpClient; use AcmePhp\Core\Protocol\AuthorizationChallenge; +use AcmePhp\Core\Protocol\CertificateOrder; use AcmePhp\Core\Protocol\RevocationReason; use AcmePhp\Ssl\Certificate; use AcmePhp\Ssl\CertificateRequest; @@ -47,6 +47,48 @@ interface AcmeClientInterface */ public function registerAccount($agreement = null, $email = null); + /** + * Request authorization challenge data for a list of domains. + * + * An AuthorizationChallenge is an association between a URI, a token and a payload. + * The Certificate Authority will create this challenge data and you will then have + * to expose the payload for the verification (see challengeAuthorization). + * + * @param string[] $domains the domains to challenge + * + * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code + * (the exception will be more specific if detail is provided) + * @throws AcmeCoreClientException when an error occured during response parsing + * @throws ChallengeNotSupportedException when the HTTP challenge is not supported by the server + * + * @return CertificateOrder the Order returned by the Certificate Authority + */ + public function requestOrder(array $domains); + + /** + * Request a certificate for the given domain. + * + * This method should be called only if a previous authorization challenge has + * been successful for the asked domain. + * + * WARNING : This method SHOULD NOT BE USED in a web action. It will + * wait for the Certificate Authority to validate the certificate and + * this operation could be long. + * + * @param CertificateOrder $order the Order returned by the Certificate Authority + * @param CertificateRequest $csr the Certificate Signing Request (informations for the certificate) + * @param int $timeout the timeout period + * + * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code + * (the exception will be more specific if detail is provided) + * @throws AcmeCoreClientException when an error occured during response parsing + * @throws CertificateRequestFailedException when the certificate request failed + * @throws CertificateRequestTimedOutException when the certificate request timed out + * + * @return CertificateResponse the certificate data to save it somewhere you want + */ + public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, $timeout = 180); + /** * Request authorization challenge data for a given domain. * @@ -65,6 +107,15 @@ public function registerAccount($agreement = null, $email = null); */ public function requestAuthorization($domain); + /** + * Request the current status of an authorization challenge. + * + * @param AuthorizationChallenge $challenge The challenge to request + * + * @return AuthorizationChallenge A new instance of the challenge + */ + public function reloadAuthorization(AuthorizationChallenge $challenge); + /** * Ask the Certificate Authority to challenge a given authorization. * @@ -117,11 +168,4 @@ public function requestCertificate($domain, CertificateRequest $csr, $timeout = * @throws CertificateRevocationException */ public function revokeCertificate(Certificate $certificate, RevocationReason $revocationReason = null); - - /** - * Get the HTTP client. - * - * @return SecureHttpClient - */ - public function getHttpClient(); } diff --git a/src/Core/AcmeClientV2Interface.php b/src/Core/AcmeClientV2Interface.php deleted file mode 100644 index c1f4f75d..00000000 --- a/src/Core/AcmeClientV2Interface.php +++ /dev/null @@ -1,81 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Core; - -use AcmePhp\Core\Exception\AcmeCoreClientException; -use AcmePhp\Core\Exception\AcmeCoreServerException; -use AcmePhp\Core\Exception\Protocol\CertificateRequestFailedException; -use AcmePhp\Core\Exception\Protocol\CertificateRequestTimedOutException; -use AcmePhp\Core\Exception\Protocol\ChallengeNotSupportedException; -use AcmePhp\Core\Protocol\AuthorizationChallenge; -use AcmePhp\Core\Protocol\CertificateOrder; -use AcmePhp\Ssl\CertificateRequest; -use AcmePhp\Ssl\CertificateResponse; - -/** - * ACME protocol client interface. - * - * @author Titouan Galopin - */ -interface AcmeClientV2Interface extends AcmeClientInterface -{ - /** - * Request authorization challenge data for a list of domains. - * - * An AuthorizationChallenge is an association between a URI, a token and a payload. - * The Certificate Authority will create this challenge data and you will then have - * to expose the payload for the verification (see challengeAuthorization). - * - * @param string[] $domains the domains to challenge - * - * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code - * (the exception will be more specific if detail is provided) - * @throws AcmeCoreClientException when an error occured during response parsing - * @throws ChallengeNotSupportedException when the HTTP challenge is not supported by the server - * - * @return CertificateOrder the Order returned by the Certificate Authority - */ - public function requestOrder(array $domains); - - /** - * Request a certificate for the given domain. - * - * This method should be called only if a previous authorization challenge has - * been successful for the asked domain. - * - * WARNING : This method SHOULD NOT BE USED in a web action. It will - * wait for the Certificate Authority to validate the certificate and - * this operation could be long. - * - * @param CertificateOrder $order the Order returned by the Certificate Authority - * @param CertificateRequest $csr the Certificate Signing Request (informations for the certificate) - * @param int $timeout the timeout period - * - * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code - * (the exception will be more specific if detail is provided) - * @throws AcmeCoreClientException when an error occured during response parsing - * @throws CertificateRequestFailedException when the certificate request failed - * @throws CertificateRequestTimedOutException when the certificate request timed out - * - * @return CertificateResponse the certificate data to save it somewhere you want - */ - public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, $timeout = 180); - - /** - * Request the current status of an authorization challenge. - * - * @param AuthorizationChallenge $challenge The challenge to request - * - * @return AuthorizationChallenge A new instance of the challenge - */ - public function reloadAuthorization(AuthorizationChallenge $challenge); -} diff --git a/src/Core/Challenge/ChainValidator.php b/src/Core/Challenge/ChainValidator.php index a5a2d3a1..cd05ba1d 100644 --- a/src/Core/Challenge/ChainValidator.php +++ b/src/Core/Challenge/ChainValidator.php @@ -37,10 +37,10 @@ public function __construct(array $validators) /** * {@inheritdoc} */ - public function supports(AuthorizationChallenge $authorizationChallenge) + public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) { foreach ($this->validators as $validator) { - if ($validator->supports($authorizationChallenge)) { + if ($validator->supports($authorizationChallenge, $solver)) { return true; } } @@ -51,11 +51,11 @@ public function supports(AuthorizationChallenge $authorizationChallenge) /** * {@inheritdoc} */ - public function isValid(AuthorizationChallenge $authorizationChallenge) + public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) { foreach ($this->validators as $validator) { - if ($validator->supports($authorizationChallenge)) { - return $validator->isValid($authorizationChallenge); + if ($validator->supports($authorizationChallenge, $solver)) { + return $validator->isValid($authorizationChallenge, $solver); } } diff --git a/src/Core/Challenge/Dns/DnsValidator.php b/src/Core/Challenge/Dns/DnsValidator.php index 56198bcd..edf086fc 100644 --- a/src/Core/Challenge/Dns/DnsValidator.php +++ b/src/Core/Challenge/Dns/DnsValidator.php @@ -11,6 +11,7 @@ namespace AcmePhp\Core\Challenge\Dns; +use AcmePhp\Core\Challenge\SolverInterface; use AcmePhp\Core\Challenge\ValidatorInterface; use AcmePhp\Core\Exception\AcmeDnsResolutionException; use AcmePhp\Core\Protocol\AuthorizationChallenge; @@ -45,7 +46,7 @@ public function __construct(DnsDataExtractor $extractor = null, DnsResolverInter /** * {@inheritdoc} */ - public function supports(AuthorizationChallenge $authorizationChallenge) + public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) { return 'dns-01' === $authorizationChallenge->getType(); } @@ -53,7 +54,7 @@ public function supports(AuthorizationChallenge $authorizationChallenge) /** * {@inheritdoc} */ - public function isValid(AuthorizationChallenge $authorizationChallenge) + public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) { $recordName = $this->extractor->getRecordName($authorizationChallenge); $recordValue = $this->extractor->getRecordValue($authorizationChallenge); diff --git a/src/Core/Challenge/Http/HttpValidator.php b/src/Core/Challenge/Http/HttpValidator.php index 7940941d..a0be5ee6 100644 --- a/src/Core/Challenge/Http/HttpValidator.php +++ b/src/Core/Challenge/Http/HttpValidator.php @@ -11,6 +11,7 @@ namespace AcmePhp\Core\Challenge\Http; +use AcmePhp\Core\Challenge\SolverInterface; use AcmePhp\Core\Challenge\ValidatorInterface; use AcmePhp\Core\Protocol\AuthorizationChallenge; use GuzzleHttp\Client; @@ -42,15 +43,15 @@ public function __construct(HttpDataExtractor $extractor = null, Client $client /** * {@inheritdoc} */ - public function supports(AuthorizationChallenge $authorizationChallenge) + public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) { - return 'http-01' === $authorizationChallenge->getType(); + return 'http-01' === $authorizationChallenge->getType() && !$solver instanceof MockServerHttpSolver; } /** * {@inheritdoc} */ - public function isValid(AuthorizationChallenge $authorizationChallenge) + public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) { $checkUrl = $this->extractor->getCheckUrl($authorizationChallenge); $checkContent = $this->extractor->getCheckContent($authorizationChallenge); diff --git a/src/Core/Challenge/Http/MockHttpValidator.php b/src/Core/Challenge/Http/MockHttpValidator.php new file mode 100644 index 00000000..68c1fb2d --- /dev/null +++ b/src/Core/Challenge/Http/MockHttpValidator.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Core\Challenge\Http; + +use AcmePhp\Core\Challenge\SolverInterface; +use AcmePhp\Core\Challenge\ValidatorInterface; +use AcmePhp\Core\Protocol\AuthorizationChallenge; + +/** + * Validator for pebble-challtestsrv. + * + * @author Titouan Galopin + */ +class MockHttpValidator implements ValidatorInterface +{ + /** + * {@inheritdoc} + */ + public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) + { + return 'http-01' === $authorizationChallenge->getType() && $solver instanceof MockServerHttpSolver; + } + + /** + * {@inheritdoc} + */ + public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) + { + return true; + } +} diff --git a/src/Core/Challenge/Http/MockServerHttpSolver.php b/src/Core/Challenge/Http/MockServerHttpSolver.php new file mode 100644 index 00000000..0dcb1e8d --- /dev/null +++ b/src/Core/Challenge/Http/MockServerHttpSolver.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Core\Challenge\Http; + +use AcmePhp\Core\Challenge\SolverInterface; +use AcmePhp\Core\Protocol\AuthorizationChallenge; +use GuzzleHttp\Client; +use GuzzleHttp\RequestOptions; + +/** + * ACME HTTP solver talking to pebble-challtestsrv. + * + * @author Titouan Galopin + */ +class MockServerHttpSolver implements SolverInterface +{ + /** + * {@inheritdoc} + */ + public function supports(AuthorizationChallenge $authorizationChallenge) + { + return 'http-01' === $authorizationChallenge->getType(); + } + + /** + * {@inheritdoc} + */ + public function solve(AuthorizationChallenge $authorizationChallenge) + { + (new Client())->post('http://localhost:8055/add-http01', [ + RequestOptions::JSON => [ + 'token' => $authorizationChallenge->getToken(), + 'content' => $authorizationChallenge->getPayload(), + ], + ]); + } + + /** + * {@inheritdoc} + */ + public function cleanup(AuthorizationChallenge $authorizationChallenge) + { + (new Client())->post('http://localhost:8055/del-http01', [ + RequestOptions::JSON => [ + 'token' => $authorizationChallenge->getToken(), + ], + ]); + } +} diff --git a/src/Core/Challenge/ValidatorInterface.php b/src/Core/Challenge/ValidatorInterface.php index 3aca9ad7..894fe7ba 100644 --- a/src/Core/Challenge/ValidatorInterface.php +++ b/src/Core/Challenge/ValidatorInterface.php @@ -25,12 +25,12 @@ interface ValidatorInterface * * @return bool The validator supports the given challenge's type */ - public function supports(AuthorizationChallenge $authorizationChallenge); + public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver); /** * Internally validate the challenge by performing the same kind of test than the CA. * * @return bool The challenge is valid */ - public function isValid(AuthorizationChallenge $authorizationChallenge); + public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver); } diff --git a/src/Core/Challenge/WaitingValidator.php b/src/Core/Challenge/WaitingValidator.php index 07a95647..6c2fe25d 100644 --- a/src/Core/Challenge/WaitingValidator.php +++ b/src/Core/Challenge/WaitingValidator.php @@ -43,20 +43,20 @@ public function __construct(ValidatorInterface $validator, $timeout = 180) /** * {@inheritdoc} */ - public function supports(AuthorizationChallenge $authorizationChallenge) + public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) { - return $this->validator->supports($authorizationChallenge); + return $this->validator->supports($authorizationChallenge, $solver); } /** * {@inheritdoc} */ - public function isValid(AuthorizationChallenge $authorizationChallenge) + public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) { $limitEndTime = time() + $this->timeout; do { - if ($this->validator->isValid($authorizationChallenge)) { + if ($this->validator->isValid($authorizationChallenge, $solver)) { return true; } sleep(3); diff --git a/tests/Cli/AbstractApplicationTest.php b/tests/Cli/AbstractApplicationTest.php index 72a60072..67780619 100644 --- a/tests/Cli/AbstractApplicationTest.php +++ b/tests/Cli/AbstractApplicationTest.php @@ -14,33 +14,24 @@ use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Finder\Finder; -use Tests\AcmePhp\Cli\Mock\AbstractTestApplication; -use Tests\AcmePhp\Cli\Mock\SimpleApplication; +use Tests\AcmePhp\Cli\Mock\TestApplication; use Tests\AcmePhp\Core\AbstractFunctionnalTest; -use Webmozart\PathUtil\Path; abstract class AbstractApplicationTest extends AbstractFunctionnalTest { /** - * @var AbstractTestApplication + * @var TestApplication */ protected $application; - /** - * @return array - */ - abstract protected function getFixturesDirectories(); - - /** - * @return AbstractTestApplication - */ - abstract protected function createApplication(); + abstract protected function getFixturesDirectories(): array; + abstract protected function getConfigFile(): string; public function setUp(): void { $this->cleanContext(); - $this->application = $this->createApplication(); + $this->application = new TestApplication(); } public function tearDown(): void @@ -50,90 +41,30 @@ public function tearDown(): void public function testFullProcess() { - /* - * Register - */ - $register = $this->application->find('register'); - $registerTester = new CommandTester($register); - $registerTester->execute([ - 'command' => $register->getName(), - 'email' => 'foo@example.com', + $runTester = new CommandTester($this->application->find('run')); + $runTester->execute([ + 'command' => 'run', + 'config' => $this->getConfigFile(), '--server' => 'https://localhost:14000/dir', ]); - $registerDisplay = $registerTester->getDisplay(); + $output = $runTester->getDisplay(); - $this->assertStringContainsString('No account key pair was found, generating one', $registerDisplay); - $this->assertStringContainsString('Account registered successfully', $registerDisplay); + // Register + $this->assertStringContainsString('No account key pair was found, generating one', $output); + $this->assertStringContainsString('Account registered successfully', $output); $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/account/key.private.pem'); $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/account/key.public.pem'); - /* - * Authorize - */ - $authorize = $this->application->find('authorize'); - $authorizeTest = new CommandTester($authorize); - $authorizeTest->execute([ - 'command' => $authorize->getName(), - 'domains' => ['acmephp.com'], - '--server' => 'https://localhost:14000/dir', - ]); - - $authorizeDisplay = $authorizeTest->getDisplay(); - - $this->assertStringContainsString('The authorization tokens was successfully fetched', $authorizeDisplay); - $this->assertStringContainsString('http://acmephp.com/.well-known/acme-challenge/', $authorizeDisplay); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/var/acmephp.com/authorization_challenge.json'); - - /* - * Check - */ - - // Find challenge and expose token - $challenge = json_decode( - file_get_contents(__DIR__.'/../Cli/Fixtures/local/master/var/acmephp.com/authorization_challenge.json'), - true - ); - - $this->handleChallenge($challenge['token'], $challenge['payload']); - try { - $check = $this->application->find('check'); - $checkTest = new CommandTester($check); - $checkTest->execute([ - 'command' => $check->getName(), - 'domains' => ['acmephp.com'], - '--server' => 'https://localhost:14000/dir', - '--no-test' => null, - ]); - - $checkDisplay = $checkTest->getDisplay(); - - $this->assertStringContainsString('The authorization check was successful', $checkDisplay); - } finally { - $this->cleanChallenge($challenge['token']); - } - - /* - * Request - */ - $request = $this->application->find('request'); - $requestTest = new CommandTester($request); - $requestTest->execute([ - 'command' => $request->getName(), - 'domain' => 'acmephp.com', - '--server' => 'https://localhost:14000/dir', - '--country' => 'FR', - '--province' => 'Ile de France', - '--locality' => 'Paris', - '--organization' => 'Acme PHP', - '--unit' => 'Sales', - '--email' => 'example@acmephp.github.io', - ]); - - $requestDisplay = $requestTest->getDisplay(); + // Challenge + $this->assertStringContainsString('Requesting certificate order', $output); + $this->assertStringContainsString('Solving challenge for domain acmephp.com', $output); + $this->assertStringContainsString('Requesting authorization check for domain acmephp.com', $output); + $this->assertStringContainsString('Cleaning up challenge for domain acmephp.com', $output); - $this->assertStringContainsString('The SSL certificate was fetched successfully', $requestDisplay); - $this->assertStringContainsString(Path::canonicalize(__DIR__.'/Fixtures/local/master'), $requestDisplay); + // Certificate + $this->assertStringContainsString('Requesting certificate for domain acmephp.com', $output); + $this->assertStringContainsString('Certificate requested successfully!', $output); $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/private/key.private.pem'); $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/private/key.public.pem'); $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/public/cert.pem'); @@ -142,54 +73,6 @@ public function testFullProcess() $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/public/fullchain.pem'); } - public function testCheckWithoutKeyFail() - { - $this->expectException('AcmePhp\Cli\Exception\CommandFlowException'); - $this->application = new SimpleApplication(); - - $command = $this->application->find('check'); - $commandTester = new CommandTester($command); - $commandTester->execute([ - 'command' => $command->getName(), - 'domains' => ['example.com'], - '--server' => 'https://localhost:14000/dir', - ]); - } - - public function testAuthorizeWithoutKeyFail() - { - $this->expectException('AcmePhp\Cli\Exception\CommandFlowException'); - $this->application = new SimpleApplication(); - - $command = $this->application->find('authorize'); - $commandTester = new CommandTester($command); - $commandTester->execute([ - 'command' => $command->getName(), - 'domains' => 'example.com', - '--server' => 'https://localhost:14000/dir', - ]); - } - - public function testRequestWithoutKeyFail() - { - $this->expectException('AcmePhp\Cli\Exception\CommandFlowException'); - $this->application = new SimpleApplication(); - - $command = $this->application->find('request'); - $commandTester = new CommandTester($command); - $commandTester->execute([ - 'command' => $command->getName(), - 'domain' => 'acmephp.com', - '--server' => 'https://localhost:14000/dir', - '--country' => 'FR', - '--province' => 'Ile de France', - '--locality' => 'Paris', - '--organization' => 'Acme PHP', - '--unit' => 'Sales', - '--email' => 'example@acmephp.github.io', - ]); - } - /** * Remove fixtures files and directories to have a clean context. */ diff --git a/tests/Cli/Fixtures/config_sfpt_nginxproxy.yaml b/tests/Cli/Fixtures/config_sfpt_nginxproxy.yaml new file mode 100644 index 00000000..e8226e3f --- /dev/null +++ b/tests/Cli/Fixtures/config_sfpt_nginxproxy.yaml @@ -0,0 +1,22 @@ +contact_email: foo@example.com +key_type: RSA + +defaults: + distinguished_name: + country: FR + locality: Paris + organization_name: Acme PHP + +certificates: + - domain: acmephp.com + solver: + name: mock-server + install: + - action: build_nginxproxy + - action: mirror_file + adapter: sftp + root: /share + host: localhost + username: acmephp + password: acmephp + port: 8022 diff --git a/tests/Cli/Fixtures/config_simple.yaml b/tests/Cli/Fixtures/config_simple.yaml new file mode 100644 index 00000000..8ba9ae3f --- /dev/null +++ b/tests/Cli/Fixtures/config_simple.yaml @@ -0,0 +1,13 @@ +contact_email: foo@example.com +key_type: RSA + +defaults: + distinguished_name: + country: FR + locality: Paris + organization_name: Acme PHP + +certificates: + - domain: acmephp.com + solver: + name: mock-server diff --git a/tests/Cli/Fixtures/monitoring.conf b/tests/Cli/Fixtures/monitoring.conf deleted file mode 100644 index fd24caa0..00000000 --- a/tests/Cli/Fixtures/monitoring.conf +++ /dev/null @@ -1,20 +0,0 @@ -storage: - enable_backup: false - post_generate: ~ - -monitoring: - email: - to: galopintitouan@gmail.com - host: smtp.example.com - port: 25 - username: foo - password: bar - encryption: tls - subject: foo_subject - level: info - - slack: - token: foo_token - channel: foochannel - username: Acme PHP - level: error diff --git a/tests/Cli/Fixtures/sftp_nginxproxy.conf b/tests/Cli/Fixtures/sftp_nginxproxy.conf deleted file mode 100644 index 66bab04b..00000000 --- a/tests/Cli/Fixtures/sftp_nginxproxy.conf +++ /dev/null @@ -1,13 +0,0 @@ -storage: - enable_backup: true - post_generate: - - action: build_nginxproxy - - action: mirror_file - adapter: sftp - root: /share - host: localhost - username: acmephp - password: acmephp - port: 8022 - -monitoring: ~ diff --git a/tests/Cli/Fixtures/simple.conf b/tests/Cli/Fixtures/simple.conf deleted file mode 100644 index 319c6875..00000000 --- a/tests/Cli/Fixtures/simple.conf +++ /dev/null @@ -1,5 +0,0 @@ -storage: - enable_backup: false - post_generate: ~ - -monitoring: ~ diff --git a/tests/Cli/Mock/AbstractTestApplication.php b/tests/Cli/Mock/AbstractTestApplication.php deleted file mode 100644 index 0be580b4..00000000 --- a/tests/Cli/Mock/AbstractTestApplication.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Tests\AcmePhp\Cli\Mock; - -use Webmozart\PathUtil\Path; - -abstract class AbstractTestApplication extends \AcmePhp\Cli\Application -{ - /** - * @return string - */ - public function getStorageDirectory() - { - return Path::canonicalize(__DIR__.'/../Fixtures/local/master'); - } - - /** - * @return string - */ - public function getBackupDirectory() - { - return Path::canonicalize(__DIR__.'/../Fixtures/local/backup'); - } -} diff --git a/tests/Cli/Mock/MonitoredApplication.php b/tests/Cli/Mock/MonitoredApplication.php deleted file mode 100644 index c7828e8d..00000000 --- a/tests/Cli/Mock/MonitoredApplication.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Tests\AcmePhp\Cli\Mock; - -use Webmozart\PathUtil\Path; - -class MonitoredApplication extends AbstractTestApplication -{ - /** - * @return string - */ - public function getConfigFile() - { - return Path::canonicalize(__DIR__.'/../Fixtures/monitoring.conf'); - } -} diff --git a/tests/Cli/Mock/SftpNginxProxyApplication.php b/tests/Cli/Mock/SftpNginxProxyApplication.php deleted file mode 100644 index 5f600105..00000000 --- a/tests/Cli/Mock/SftpNginxProxyApplication.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Tests\AcmePhp\Cli\Mock; - -use Webmozart\PathUtil\Path; - -class SftpNginxProxyApplication extends AbstractTestApplication -{ - /** - * @return string - */ - public function getConfigFile() - { - return Path::canonicalize(__DIR__.'/../Fixtures/sftp_nginxproxy.conf'); - } -} diff --git a/tests/Cli/Mock/SimpleApplication.php b/tests/Cli/Mock/TestApplication.php similarity index 68% rename from tests/Cli/Mock/SimpleApplication.php rename to tests/Cli/Mock/TestApplication.php index 26a26de7..dbc4f4c3 100644 --- a/tests/Cli/Mock/SimpleApplication.php +++ b/tests/Cli/Mock/TestApplication.php @@ -13,13 +13,13 @@ use Webmozart\PathUtil\Path; -class SimpleApplication extends AbstractTestApplication +class TestApplication extends \AcmePhp\Cli\Application { /** * @return string */ - public function getConfigFile() + public function getStorageDirectory() { - return Path::canonicalize(__DIR__.'/../Fixtures/simple.conf'); + return Path::canonicalize(__DIR__.'/../Fixtures/local/master'); } } diff --git a/tests/Cli/MonitoredApplicationTest.php b/tests/Cli/MonitoredApplicationTest.php deleted file mode 100644 index 4ed65a55..00000000 --- a/tests/Cli/MonitoredApplicationTest.php +++ /dev/null @@ -1,177 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Tests\AcmePhp\Cli; - -use AcmePhp\Cli\Command\AbstractCommand; -use AcmePhp\Cli\Monitoring\HandlerBuilderInterface; -use AcmePhp\Core\Exception\AcmeCoreClientException; -use GuzzleHttp\Client; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Handler\MockHandler; -use GuzzleHttp\Psr7\Request; -use Monolog\Handler\TestHandler; -use Monolog\Logger; -use Symfony\Component\Console\Tester\CommandTester; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Tests\AcmePhp\Cli\Mock\AbstractTestApplication; -use Tests\AcmePhp\Cli\Mock\MonitoredApplication; - -class MonitoredApplicationTest extends AbstractApplicationTest -{ - /** - * @return array - */ - protected function getFixturesDirectories() - { - return [ - __DIR__.'/../Cli/Fixtures/local/backup', - __DIR__.'/../Cli/Fixtures/local/master', - ]; - } - - /** - * @return AbstractTestApplication - */ - protected function createApplication() - { - return new MonitoredApplication(); - } - - public function testFullProcess() - { - parent::testFullProcess(); - - /* - * Renewal without issue - */ - $command = $this->application->find('request'); - $commandTester = new CommandTester($command); - $commandTester->execute([ - 'command' => $command->getName(), - 'domain' => 'acmephp.com', - '--server' => 'https://localhost:14000/dir', - '--country' => 'FR', - '--province' => 'Ile de France', - '--locality' => 'Paris', - '--organization' => 'Acme PHP', - '--unit' => 'Sales', - '--email' => 'galopintitouan@gmail.com', - '--force' => true, - ]); - - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/private/key.private.pem'); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/private/key.public.pem'); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/public/cert.pem'); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/private/combined.pem'); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/public/chain.pem'); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/public/fullchain.pem'); - } - - public function testRenewalWithIssue() - { - parent::testFullProcess(); - - $command = $this->application->find('request'); - $commandTester = new CommandTester($command); - $commandTester->execute([ - 'command' => $command->getName(), - 'domain' => 'acmephp.com', - '--server' => 'https://localhost:14000/dir', - '--country' => 'FR', - '--province' => 'Ile de France', - '--locality' => 'Paris', - '--organization' => 'Acme PHP', - '--unit' => 'Sales', - '--email' => 'galopintitouan@gmail.com', - '--force' => true, - ]); - - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/private/key.private.pem'); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/private/key.public.pem'); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/public/cert.pem'); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/private/combined.pem'); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/public/chain.pem'); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/public/fullchain.pem'); - - /* - * Mock monitoring handlers - */ - - // Initialize container - $parentReflection = new \ReflectionClass(AbstractCommand::class); - $containerReflection = $parentReflection->getProperty('container'); - $containerReflection->setAccessible(true); - $containerReflection->setValue($command, null); - - $commandReflection = new \ReflectionObject($command); - $initializer = $commandReflection->getMethod('initializeContainer'); - $initializer->setAccessible(true); - $initializer->invoke($command); - - // Replace handlers builders by mocks - $handler = new TestHandler(); - - $handlerBuilder = $this->getMockBuilder(HandlerBuilderInterface::class)->getMock(); - $handlerBuilder - ->expects($this->exactly(2)) - ->method('createHandler') - ->willReturn($handler); - - /** @var ContainerInterface $container */ - $container = $containerReflection->getValue($command); - $container->set('acmephp.monitoring.email', $handlerBuilder); - $container->set('acmephp.monitoring.slack', $handlerBuilder); - - // Introduce HTTP issue - $container->set('http.raw_client', new Client(['handler' => new MockHandler([ - new RequestException('Error Communicating with Server', new Request('GET', 'test')), - ])])); - - // Set new container - $containerReflection->setValue($command, $container); - - /* - * Renewal with issue - */ - $commandTester = new CommandTester($command); - $thrownException = null; - - try { - $commandTester->execute([ - 'command' => $command->getName(), - 'domain' => 'acmephp.com', - '--server' => 'https://localhost:14000/dir', - '--country' => 'FR', - '--province' => 'Ile de France', - '--locality' => 'Paris', - '--organization' => 'Acme PHP', - '--unit' => 'Sales', - '--email' => 'galopintitouan@gmail.com', - '--force' => true, - ]); - } catch (AcmeCoreClientException $e) { - $thrownException = $e; - } - - $this->assertNotNull($thrownException); - $this->assertNotNull($thrownException->getPrevious()); - $this->assertInstanceOf(RequestException::class, $thrownException->getPrevious()); - - $records = $handler->getRecords(); - - $this->assertCount(2, $records); - $this->assertSame(Logger::ALERT, $records[0]['level']); - $this->assertSame('A critical error occured during certificate renewal', $records[0]['message']); - $this->assertSame(Logger::ALERT, $records[1]['level']); - $this->assertSame('A critical error occured during certificate renewal', $records[1]['message']); - } -} diff --git a/tests/Cli/Repository/AbstractRepositoryTest.php b/tests/Cli/Repository/RepositoryTest.php similarity index 90% rename from tests/Cli/Repository/AbstractRepositoryTest.php rename to tests/Cli/Repository/RepositoryTest.php index 07094b3f..4a1d87a6 100644 --- a/tests/Cli/Repository/AbstractRepositoryTest.php +++ b/tests/Cli/Repository/RepositoryTest.php @@ -27,7 +27,7 @@ use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; use Symfony\Component\Serializer\Serializer; -abstract class AbstractRepositoryTest extends TestCase +class RepositoryTest extends TestCase { /** * @var Serializer @@ -37,12 +37,7 @@ abstract class AbstractRepositoryTest extends TestCase /** * @var Filesystem */ - protected $master; - - /** - * @var Filesystem - */ - protected $backup; + protected $storage; /** * @var Repository @@ -56,20 +51,16 @@ public function setUp(): void [new PemEncoder(), new JsonEncoder()] ); - $this->master = new Filesystem(new MemoryAdapter()); - $this->backup = new Filesystem(new MemoryAdapter()); - - $this->repository = $this->createRepository(); + $this->storage = new Filesystem(new MemoryAdapter()); + $this->repository = new Repository($this->serializer, $this->storage); } - abstract protected function createRepository(); - public function testStoreAccountKeyPair() { $this->repository->storeAccountKeyPair(new KeyPair(new PublicKey('public'), new PrivateKey('private'))); - $this->assertEquals("public\n", $this->master->read('account/key.public.pem')); - $this->assertEquals("private\n", $this->master->read('account/key.private.pem')); + $this->assertEquals("public\n", $this->storage->read('account/key.public.pem')); + $this->assertEquals("private\n", $this->storage->read('account/key.private.pem')); } public function testLoadAccountKeyPair() @@ -92,8 +83,8 @@ public function testStoreDomainKeyPair() { $this->repository->storeDomainKeyPair('example.com', new KeyPair(new PublicKey('public'), new PrivateKey('private'))); - $this->assertEquals("public\n", $this->master->read('certs/example.com/private/key.public.pem')); - $this->assertEquals("private\n", $this->master->read('certs/example.com/private/key.private.pem')); + $this->assertEquals("public\n", $this->storage->read('certs/example.com/private/key.public.pem')); + $this->assertEquals("private\n", $this->storage->read('certs/example.com/private/key.private.pem')); } public function testLoadDomainKeyPair() @@ -125,7 +116,7 @@ public function testStoreDomainAuthorizationChallenge() $this->repository->storeDomainAuthorizationChallenge('example.com', $challenge); - $json = $this->master->read('var/example.com/authorization_challenge.json'); + $json = $this->storage->read('var/example.com/authorization_challenge.json'); $this->assertJson($json); $data = json_decode($json, true); @@ -175,7 +166,7 @@ public function testStoreDomainDistinguishedName() $this->repository->storeDomainDistinguishedName('example.com', $dn); - $json = $this->master->read('var/example.com/distinguished_name.json'); + $json = $this->storage->read('var/example.com/distinguished_name.json'); $this->assertJson($json); $data = json_decode($json, true); @@ -222,10 +213,10 @@ public function testStoreDomainCertificate() $this->repository->storeDomainKeyPair('example.com', new KeyPair(new PublicKey('public'), new PrivateKey('private'))); $this->repository->storeDomainCertificate('example.com', $cert); - $this->assertEquals(self::$certPem."\n".self::$issuerCertPem."\nprivate\n", $this->master->read('certs/example.com/private/combined.pem')); - $this->assertEquals(self::$certPem."\n", $this->master->read('certs/example.com/public/cert.pem')); - $this->assertEquals(self::$issuerCertPem."\n", $this->master->read('certs/example.com/public/chain.pem')); - $this->assertEquals(self::$certPem."\n".self::$issuerCertPem."\n", $this->master->read('certs/example.com/public/fullchain.pem')); + $this->assertEquals(self::$certPem."\n".self::$issuerCertPem."\nprivate\n", $this->storage->read('certs/example.com/private/combined.pem')); + $this->assertEquals(self::$certPem."\n", $this->storage->read('certs/example.com/public/cert.pem')); + $this->assertEquals(self::$issuerCertPem."\n", $this->storage->read('certs/example.com/public/chain.pem')); + $this->assertEquals(self::$certPem."\n".self::$issuerCertPem."\n", $this->storage->read('certs/example.com/public/fullchain.pem')); } public function testLoadDomainCertificate() diff --git a/tests/Cli/Repository/RepositoryWithBackupTest.php b/tests/Cli/Repository/RepositoryWithBackupTest.php deleted file mode 100644 index ef652987..00000000 --- a/tests/Cli/Repository/RepositoryWithBackupTest.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Tests\AcmePhp\Cli\Repository; - -use AcmePhp\Cli\Repository\Repository; - -class RepositoryWithBackupTest extends AbstractRepositoryTest -{ - protected function createRepository() - { - return new Repository($this->serializer, $this->master, $this->backup, true); - } - - public function testStoreAccountKeyPair() - { - parent::testStoreAccountKeyPair(); - - $this->assertEquals("public\n", $this->backup->read('account/key.public.pem')); - $this->assertEquals("private\n", $this->backup->read('account/key.private.pem')); - } - - public function testStoreDomainKeyPair() - { - parent::testStoreDomainKeyPair(); - - $this->assertEquals("public\n", $this->backup->read('certs/example.com/private/key.public.pem')); - $this->assertEquals("private\n", $this->backup->read('certs/example.com/private/key.private.pem')); - } - - public function testStoreDomainDistinguishedName() - { - parent::testStoreDomainDistinguishedName(); - - $this->assertJson($this->backup->read('var/example.com/distinguished_name.json')); - } - - public function testStoreDomainCertificate() - { - parent::testStoreDomainCertificate(); - - $this->assertEquals(self::$certPem."\n".self::$issuerCertPem."\nprivate\n", $this->backup->read('certs/example.com/private/combined.pem')); - $this->assertEquals(self::$certPem."\n", $this->backup->read('certs/example.com/public/cert.pem')); - $this->assertEquals(self::$issuerCertPem."\n", $this->backup->read('certs/example.com/public/chain.pem')); - $this->assertEquals(self::$certPem."\n".self::$issuerCertPem."\n", $this->backup->read('certs/example.com/public/fullchain.pem')); - } -} diff --git a/tests/Cli/Repository/RepositoryWithoutBackupTest.php b/tests/Cli/Repository/RepositoryWithoutBackupTest.php deleted file mode 100644 index cce36ca3..00000000 --- a/tests/Cli/Repository/RepositoryWithoutBackupTest.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Tests\AcmePhp\Cli\Repository; - -use AcmePhp\Cli\Repository\Repository; - -class RepositoryWithoutBackupTest extends AbstractRepositoryTest -{ - protected function createRepository() - { - return new Repository($this->serializer, $this->master, $this->backup, false); - } - - public function testStoreAccountKeyPair() - { - parent::testStoreAccountKeyPair(); - - $this->assertFalse($this->backup->has('account/key.public.pem')); - $this->assertFalse($this->backup->has('account/key.private.pem')); - } - - public function testStoreDomainKeyPair() - { - parent::testStoreDomainKeyPair(); - - $this->assertFalse($this->backup->has('certs/acmephp.com/private/key.public.pem')); - $this->assertFalse($this->backup->has('certs/acmephp.com/private/key.private.pem')); - } - - public function testStoreDomainDistinguishedName() - { - parent::testStoreDomainDistinguishedName(); - - $this->assertFalse($this->backup->has('var/example.com/distinguished_name.json')); - } - - public function testStoreDomainCertificate() - { - parent::testStoreDomainCertificate(); - - $this->assertFalse($this->backup->has('certs/example.com/private/combined.pem')); - $this->assertFalse($this->backup->has('certs/example.com/public/cert.pem')); - $this->assertFalse($this->backup->has('certs/example.com/public/chain.pem')); - $this->assertFalse($this->backup->has('certs/example.com/public/fullchain.pem')); - } -} diff --git a/tests/Cli/SftpNginxProxyApplicationTest.php b/tests/Cli/SftpNginxProxyApplicationTest.php index 513c250a..1128d41b 100644 --- a/tests/Cli/SftpNginxProxyApplicationTest.php +++ b/tests/Cli/SftpNginxProxyApplicationTest.php @@ -13,15 +13,10 @@ use League\Flysystem\Filesystem; use League\Flysystem\Sftp\SftpAdapter; -use Tests\AcmePhp\Cli\Mock\AbstractTestApplication; -use Tests\AcmePhp\Cli\Mock\SftpNginxProxyApplication; class SftpNginxProxyApplicationTest extends AbstractApplicationTest { - /** - * @return array - */ - protected function getFixturesDirectories() + protected function getFixturesDirectories(): array { return [ __DIR__.'/../Cli/Fixtures/local/backup', @@ -30,12 +25,9 @@ protected function getFixturesDirectories() ]; } - /** - * @return AbstractTestApplication - */ - protected function createApplication() + protected function getConfigFile(): string { - return new SftpNginxProxyApplication(); + return __DIR__.'/Fixtures/config_sfpt_nginxproxy.yaml'; } public function testFullProcess() @@ -60,18 +52,6 @@ public function testFullProcess() $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/nginxproxy/acmephp.com.crt'); $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/nginxproxy/acmephp.com.key'); - // Backup - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/backup/certs/acmephp.com/private/key.private.pem'); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/backup/certs/acmephp.com/private/key.public.pem'); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/backup/certs/acmephp.com/private/combined.pem'); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/backup/certs/acmephp.com/public/cert.pem'); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/backup/certs/acmephp.com/public/chain.pem'); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/backup/certs/acmephp.com/public/fullchain.pem'); - - // Backup nginxproxy - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/backup/nginxproxy/acmephp.com.crt'); - $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/backup/nginxproxy/acmephp.com.key'); - // SFTP $this->assertTrue($sftpFilesystem->has('account/key.private.pem')); $this->assertTrue($sftpFilesystem->has('account/key.public.pem')); diff --git a/tests/Cli/SimpleApplicationTest.php b/tests/Cli/SimpleApplicationTest.php index ca01697e..7bb5c3c6 100644 --- a/tests/Cli/SimpleApplicationTest.php +++ b/tests/Cli/SimpleApplicationTest.php @@ -11,26 +11,17 @@ namespace Tests\AcmePhp\Cli; -use Tests\AcmePhp\Cli\Mock\AbstractTestApplication; -use Tests\AcmePhp\Cli\Mock\SimpleApplication; - class SimpleApplicationTest extends AbstractApplicationTest { - /** - * @return array - */ - protected function getFixturesDirectories() + protected function getFixturesDirectories(): array { return [ __DIR__.'/../Cli/Fixtures/local/master', ]; } - /** - * @return AbstractTestApplication - */ - protected function createApplication() + protected function getConfigFile(): string { - return new SimpleApplication(); + return __DIR__.'/Fixtures/config_simple.yaml'; } } diff --git a/tests/Core/Challenge/ChainValidatorTest.php b/tests/Core/Challenge/ChainValidatorTest.php index bf0d8e17..900aaaf6 100644 --- a/tests/Core/Challenge/ChainValidatorTest.php +++ b/tests/Core/Challenge/ChainValidatorTest.php @@ -12,6 +12,7 @@ namespace Tests\AcmePhp\Core\Challenge; use AcmePhp\Core\Challenge\ChainValidator; +use AcmePhp\Core\Challenge\SolverInterface; use AcmePhp\Core\Challenge\ValidatorInterface; use AcmePhp\Core\Protocol\AuthorizationChallenge; use PHPUnit\Framework\TestCase; @@ -23,16 +24,17 @@ public function testSupports() $mockValidator1 = $this->prophesize(ValidatorInterface::class); $mockValidator2 = $this->prophesize(ValidatorInterface::class); $dummyChallenge = $this->prophesize(AuthorizationChallenge::class)->reveal(); + $solver = $this->prophesize(SolverInterface::class)->reveal(); $validator = new ChainValidator([$mockValidator1->reveal(), $mockValidator2->reveal()]); - $mockValidator1->supports($dummyChallenge)->willReturn(false); - $mockValidator2->supports($dummyChallenge)->willReturn(true); - $this->assertTrue($validator->supports($dummyChallenge)); + $mockValidator1->supports($dummyChallenge, $solver)->willReturn(false); + $mockValidator2->supports($dummyChallenge, $solver)->willReturn(true); + $this->assertTrue($validator->supports($dummyChallenge, $solver)); - $mockValidator1->supports($dummyChallenge)->willReturn(false); - $mockValidator2->supports($dummyChallenge)->willReturn(false); - $this->assertFalse($validator->supports($dummyChallenge)); + $mockValidator1->supports($dummyChallenge, $solver)->willReturn(false); + $mockValidator2->supports($dummyChallenge, $solver)->willReturn(false); + $this->assertFalse($validator->supports($dummyChallenge, $solver)); } public function testIsValid() @@ -40,14 +42,15 @@ public function testIsValid() $mockValidator1 = $this->prophesize(ValidatorInterface::class); $mockValidator2 = $this->prophesize(ValidatorInterface::class); $dummyChallenge = $this->prophesize(AuthorizationChallenge::class)->reveal(); + $solver = $this->prophesize(SolverInterface::class)->reveal(); $validator = new ChainValidator([$mockValidator1->reveal(), $mockValidator2->reveal()]); - $mockValidator1->supports($dummyChallenge)->willReturn(false); - $mockValidator1->isValid($dummyChallenge)->shouldNotBeCalled(); - $mockValidator2->supports($dummyChallenge)->willReturn(true); - $mockValidator2->isValid($dummyChallenge)->willReturn(true); + $mockValidator1->supports($dummyChallenge, $solver)->willReturn(false); + $mockValidator1->isValid($dummyChallenge, $solver)->shouldNotBeCalled(); + $mockValidator2->supports($dummyChallenge, $solver)->willReturn(true); + $mockValidator2->isValid($dummyChallenge, $solver)->willReturn(true); - $this->assertTrue($validator->isValid($dummyChallenge)); + $this->assertTrue($validator->isValid($dummyChallenge, $solver)); } } diff --git a/tests/Core/Challenge/Dns/DnsValidatorTest.php b/tests/Core/Challenge/Dns/DnsValidatorTest.php index cc3fe403..9b964111 100644 --- a/tests/Core/Challenge/Dns/DnsValidatorTest.php +++ b/tests/Core/Challenge/Dns/DnsValidatorTest.php @@ -14,6 +14,7 @@ use AcmePhp\Core\Challenge\Dns\DnsDataExtractor; use AcmePhp\Core\Challenge\Dns\DnsResolverInterface; use AcmePhp\Core\Challenge\Dns\DnsValidator; +use AcmePhp\Core\Challenge\SolverInterface; use AcmePhp\Core\Protocol\AuthorizationChallenge; use PHPUnit\Framework\TestCase; @@ -30,10 +31,10 @@ public function testSupports() $validator = new DnsValidator($mockExtractor->reveal()); $stubChallenge->getType()->willReturn($typeDns); - $this->assertTrue($validator->supports($stubChallenge->reveal())); + $this->assertTrue($validator->supports($stubChallenge->reveal(), $this->prophesize(SolverInterface::class)->reveal())); $stubChallenge->getType()->willReturn($typeHttp); - $this->assertFalse($validator->supports($stubChallenge->reveal())); + $this->assertFalse($validator->supports($stubChallenge->reveal(), $this->prophesize(SolverInterface::class)->reveal())); } public function testIsValid() @@ -51,7 +52,7 @@ public function testIsValid() $mockExtractor->getRecordName($stubChallenge->reveal())->willReturn($recordName); $mockExtractor->getRecordValue($stubChallenge->reveal())->willReturn($recordValue); - $this->assertTrue($validator->isValid($stubChallenge->reveal())); + $this->assertTrue($validator->isValid($stubChallenge->reveal(), $this->prophesize(SolverInterface::class)->reveal())); } public function testIsValidCheckRecordValue() @@ -69,6 +70,6 @@ public function testIsValidCheckRecordValue() $mockExtractor->getRecordName($stubChallenge->reveal())->willReturn($recordName); $mockExtractor->getRecordValue($stubChallenge->reveal())->willReturn($recordValue); - $this->assertFalse($validator->isValid($stubChallenge->reveal())); + $this->assertFalse($validator->isValid($stubChallenge->reveal(), $this->prophesize(SolverInterface::class)->reveal())); } } diff --git a/tests/Core/Challenge/Http/HttpValidatorTest.php b/tests/Core/Challenge/Http/HttpValidatorTest.php index 513dca2f..98cf691f 100644 --- a/tests/Core/Challenge/Http/HttpValidatorTest.php +++ b/tests/Core/Challenge/Http/HttpValidatorTest.php @@ -13,6 +13,7 @@ use AcmePhp\Core\Challenge\Http\HttpDataExtractor; use AcmePhp\Core\Challenge\Http\HttpValidator; +use AcmePhp\Core\Challenge\SolverInterface; use AcmePhp\Core\Protocol\AuthorizationChallenge; use GuzzleHttp\Client; use GuzzleHttp\Exception\ClientException; @@ -35,10 +36,10 @@ public function testSupports() $validator = new HttpValidator($mockExtractor->reveal(), $mockHttpClient->reveal()); $stubChallenge->getType()->willReturn($typeDns); - $this->assertFalse($validator->supports($stubChallenge->reveal())); + $this->assertFalse($validator->supports($stubChallenge->reveal(), $this->prophesize(SolverInterface::class)->reveal())); $stubChallenge->getType()->willReturn($typeHttp); - $this->assertTrue($validator->supports($stubChallenge->reveal())); + $this->assertTrue($validator->supports($stubChallenge->reveal(), $this->prophesize(SolverInterface::class)->reveal())); } public function testIsValid() @@ -61,7 +62,7 @@ public function testIsValid() $stubResponse->getBody()->willReturn($stubStream->reveal()); $stubStream->getContents()->willReturn($checkContent); - $this->assertTrue($validator->isValid($stubChallenge->reveal())); + $this->assertTrue($validator->isValid($stubChallenge->reveal(), $this->prophesize(SolverInterface::class)->reveal())); } public function testIsValidCatchExceptions() @@ -84,6 +85,6 @@ public function testIsValidCatchExceptions() $this->prophesize(ResponseInterface::class)->reveal() )); - $this->assertFalse($validator->isValid($stubChallenge->reveal())); + $this->assertFalse($validator->isValid($stubChallenge->reveal(), $this->prophesize(SolverInterface::class)->reveal())); } } diff --git a/tests/Core/Challenge/WaitingValidatorTest.php b/tests/Core/Challenge/WaitingValidatorTest.php index 555aaf32..db230f03 100644 --- a/tests/Core/Challenge/WaitingValidatorTest.php +++ b/tests/Core/Challenge/WaitingValidatorTest.php @@ -11,6 +11,7 @@ namespace Tests\AcmePhp\Core\Challenge; +use AcmePhp\Core\Challenge\SolverInterface; use AcmePhp\Core\Challenge\ValidatorInterface; use AcmePhp\Core\Challenge\WaitingValidator; use AcmePhp\Core\Protocol\AuthorizationChallenge; @@ -39,26 +40,28 @@ public function testSupports() { $mockDecorated = $this->prophesize(ValidatorInterface::class); $dummyChallenge = $this->prophesize(AuthorizationChallenge::class)->reveal(); + $solver = $this->prophesize(SolverInterface::class)->reveal(); $validator = new WaitingValidator($mockDecorated->reveal()); - $mockDecorated->supports($dummyChallenge)->willReturn(true); - $this->assertTrue($validator->supports($dummyChallenge)); + $mockDecorated->supports($dummyChallenge, $solver)->willReturn(true); + $this->assertTrue($validator->supports($dummyChallenge, $solver)); - $mockDecorated->supports($dummyChallenge)->willReturn(false); - $this->assertFalse($validator->supports($dummyChallenge)); + $mockDecorated->supports($dummyChallenge, $solver)->willReturn(false); + $this->assertFalse($validator->supports($dummyChallenge, $solver)); } public function testIsValid() { $mockDecorated = $this->prophesize(ValidatorInterface::class); $dummyChallenge = $this->prophesize(AuthorizationChallenge::class)->reveal(); + $solver = $this->prophesize(SolverInterface::class)->reveal(); $validator = new WaitingValidator($mockDecorated->reveal()); $start = time(); - $mockDecorated->isValid($dummyChallenge)->willReturn(true); - $this->assertTrue($validator->isValid($dummyChallenge)); + $mockDecorated->isValid($dummyChallenge, $solver)->willReturn(true); + $this->assertTrue($validator->isValid($dummyChallenge, $solver)); $this->assertLessThan(1, time() - $start); } @@ -66,12 +69,13 @@ public function testIsValidWaitBetweenTests() { $mockDecorated = $this->prophesize(ValidatorInterface::class); $dummyChallenge = $this->prophesize(AuthorizationChallenge::class)->reveal(); + $solver = $this->prophesize(SolverInterface::class)->reveal(); $validator = new WaitingValidator($mockDecorated->reveal()); $start = time(); - $mockDecorated->isValid($dummyChallenge)->willReturn(false); - $this->assertFalse($validator->isValid($dummyChallenge)); + $mockDecorated->isValid($dummyChallenge, $solver)->willReturn(false); + $this->assertFalse($validator->isValid($dummyChallenge, $solver)); $this->assertGreaterThanOrEqual(180, time() - $start); } @@ -79,12 +83,13 @@ public function testIsValidRetryTillOk() { $mockDecorated = $this->prophesize(ValidatorInterface::class); $dummyChallenge = $this->prophesize(AuthorizationChallenge::class)->reveal(); + $solver = $this->prophesize(SolverInterface::class)->reveal(); $validator = new WaitingValidator($mockDecorated->reveal()); $start = time(); - $mockDecorated->isValid($dummyChallenge)->willReturn(false, false, true); - $this->assertTrue($validator->isValid($dummyChallenge)); + $mockDecorated->isValid($dummyChallenge, $solver)->willReturn(false, false, true); + $this->assertTrue($validator->isValid($dummyChallenge, $solver)); $this->assertGreaterThanOrEqual(6, time() - $start); $this->assertLessThan(9, time() - $start); } diff --git a/tests/setup.sh b/tests/setup.sh index 5ed674aa..0d829e34 100755 --- a/tests/setup.sh +++ b/tests/setup.sh @@ -12,4 +12,4 @@ docker run -d --name acme_server --net host letsencrypt/pebble-challtestsrv pebb docker run -d --name acme_pebble --net host -e PEBBLE_VA_NOSLEEP=1 -e PEBBLE_WFE_NONCEREJECT=0 letsencrypt/pebble pebble -dnsserver 127.0.0.1:8053 # Wait for boot to be completed -docker run --rm --net host martin/wait -c localhost:14000,localhost:8022,localhost:8053,localhost:5002 -t 120 +docker run --rm --net host martin/wait -c localhost:14000,localhost:80,localhost:8022,localhost:8053,localhost:5002 -t 120 From be0a5b1991ecea9f93dcdc13e9633f706c7bcec0 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 17 Oct 2020 16:55:28 +0200 Subject: [PATCH 016/121] Fix subpackages composer.json --- composer.json | 13 ++-- src/Cli/Command/AbstractCommand.php | 8 --- src/Cli/Configuration/AcmeConfiguration.php | 77 --------------------- src/Core/composer.json | 11 +-- src/Ssl/composer.json | 10 +-- tests/Cli/AbstractApplicationTest.php | 1 + 6 files changed, 7 insertions(+), 113 deletions(-) delete mode 100644 src/Cli/Configuration/AcmeConfiguration.php diff --git a/composer.json b/composer.json index 2c8c32d4..2e601379 100644 --- a/composer.json +++ b/composer.json @@ -4,6 +4,9 @@ "type": "project", "license": "MIT", "homepage": "https://github.com/acmephp/acmephp", + "bin": [ + "bin/acme" + ], "keywords": [ "acme", "acmephp", @@ -84,13 +87,5 @@ "platform": { "php": "7.2.5" } - }, - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "bin": [ - "bin/acme" - ] + } } diff --git a/src/Cli/Command/AbstractCommand.php b/src/Cli/Command/AbstractCommand.php index 296decf2..d4566f2f 100644 --- a/src/Cli/Command/AbstractCommand.php +++ b/src/Cli/Command/AbstractCommand.php @@ -11,8 +11,6 @@ namespace AcmePhp\Cli\Command; -use AcmePhp\Cli\ActionHandler\ActionHandler; -use AcmePhp\Cli\Configuration\AcmeConfiguration; use AcmePhp\Cli\Exception\CommandFlowException; use AcmePhp\Cli\Repository\RepositoryInterface; use AcmePhp\Core\AcmeClient; @@ -20,7 +18,6 @@ use AcmePhp\Core\Http\SecureHttpClient; use AcmePhp\Ssl\Signer\CertificateRequestSigner; use Psr\Log\LoggerInterface; -use Symfony\Component\Config\Definition\Processor; use Symfony\Component\Config\FileLocator; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; @@ -132,11 +129,6 @@ private function initializeContainer() $this->container->set('container', $this->container); $this->container->setParameter('app.storage_directory', $this->getApplication()->getStorageDirectory()); - // Load configuration - $processor = new Processor(); - $config = $processor->processConfiguration(new AcmeConfiguration(), $this->configuration); - $this->container->setParameter('storage.post_generate', $config['storage']['post_generate']); - // Load services $loader = new XmlFileLoader($this->container, new FileLocator(__DIR__.'/../Resources')); $loader->load('services.xml'); diff --git a/src/Cli/Configuration/AcmeConfiguration.php b/src/Cli/Configuration/AcmeConfiguration.php deleted file mode 100644 index ea15ff2b..00000000 --- a/src/Cli/Configuration/AcmeConfiguration.php +++ /dev/null @@ -1,77 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\Configuration; - -use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; -use Symfony\Component\Config\Definition\Builder\TreeBuilder; -use Symfony\Component\Config\Definition\ConfigurationInterface; - -/** - * @author Titouan Galopin - */ -class AcmeConfiguration implements ConfigurationInterface -{ - /** - * {@inheritdoc} - */ - public function getConfigTreeBuilder() - { - $treeBuilder = new TreeBuilder('acmephp'); - if (method_exists(TreeBuilder::class, 'getRootNode')) { - $rootNode = $treeBuilder->getRootNode(); - } else { - $rootNode = $treeBuilder->root('acmephp'); - } - - $this->createRootNode($rootNode); - - return $treeBuilder; - } - - protected function createRootNode(ArrayNodeDefinition $rootNode) - { - $rootNode - ->addDefaultsIfNotSet() - ->children() - ->arrayNode('storage') - ->info('Configure here where and how you want to save your certificates and SSL keys.') - ->children() - ->booleanNode('enable_backup') - ->info('By default, Acme PHP will create a backup of every file before any modification. You can disable this mechanism here.') - ->isRequired() - ->defaultTrue() - ->end() - ->arrayNode('post_generate') - ->info('Actions to execute right after the generation of a file (key, CSR or certificate). Actions are executed in the order provided in configuration.') - ->normalizeKeys(false) - ->prototype('variable') - ->cannotBeEmpty() - ->validate() - ->ifTrue(function ($action) { - return !\array_key_exists('action', $action); - }) - ->thenInvalid('The "action" configuration key is required.') - ->end() - ->end() - ->end() - ->end() - ->end() - ->arrayNode('monitoring') - ->info('Configure here a simple monitoring mechanism that will warn you if an error occurs during a CRON job.') - ->normalizeKeys(false) - ->prototype('variable') - ->cannotBeEmpty() - ->end() - ->end() - ->end(); - } -} diff --git a/src/Core/composer.json b/src/Core/composer.json index b455719f..c3204a06 100644 --- a/src/Core/composer.json +++ b/src/Core/composer.json @@ -27,7 +27,7 @@ } ], "require": { - "php": ">=5.5.0", + "php": ">=7.2.5", "ext-hash": "*", "ext-json": "*", "ext-openssl": "*", @@ -38,18 +38,9 @@ "psr/log": "^1.0", "webmozart/assert": "^1.0" }, - "require-dev": { - "phpunit/phpunit": "^4.8.22", - "symfony/process": "^3.0" - }, "autoload": { "psr-4": { "AcmePhp\\Core\\": "." } - }, - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } } } diff --git a/src/Ssl/composer.json b/src/Ssl/composer.json index 017da12f..e25c462c 100644 --- a/src/Ssl/composer.json +++ b/src/Ssl/composer.json @@ -32,18 +32,10 @@ } }, "require": { - "php": ">=5.5.0", + "php": ">=7.2.5", "ext-hash": "*", "ext-openssl": "*", "lib-openssl": ">=0.9.8", "webmozart/assert": "^1.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.22" - }, - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } } } diff --git a/tests/Cli/AbstractApplicationTest.php b/tests/Cli/AbstractApplicationTest.php index 67780619..622817f6 100644 --- a/tests/Cli/AbstractApplicationTest.php +++ b/tests/Cli/AbstractApplicationTest.php @@ -25,6 +25,7 @@ abstract class AbstractApplicationTest extends AbstractFunctionnalTest protected $application; abstract protected function getFixturesDirectories(): array; + abstract protected function getConfigFile(): string; public function setUp(): void From b342a8636f5c69e7740d252208467d82bb1d3126 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 17 Oct 2020 17:12:51 +0200 Subject: [PATCH 017/121] Migrate acmephp/ssl to use typehints --- src/Ssl/Certificate.php | 23 ++----- src/Ssl/CertificateRequest.php | 10 +-- src/Ssl/CertificateResponse.php | 16 ++--- src/Ssl/DistinguishedName.php | 69 ++++--------------- .../Generator/ChainPrivateKeyGenerator.php | 8 ++- src/Ssl/Generator/DhKey/DhKeyGenerator.php | 24 +++---- src/Ssl/Generator/DhKey/DhKeyOption.php | 13 ++-- src/Ssl/Generator/DsaKey/DsaKeyGenerator.php | 18 ++--- src/Ssl/Generator/DsaKey/DsaKeyOption.php | 10 +-- src/Ssl/Generator/EcKey/EcKeyGenerator.php | 22 ++---- src/Ssl/Generator/EcKey/EcKeyOption.php | 8 +-- src/Ssl/Generator/KeyPairGenerator.php | 14 +--- .../OpensslPrivateKeyGeneratorTrait.php | 2 +- .../PrivateKeyGeneratorInterface.php | 8 +-- src/Ssl/Generator/RsaKey/RsaKeyGenerator.php | 18 ++--- src/Ssl/Generator/RsaKey/RsaKeyOption.php | 10 +-- src/Ssl/Key.php | 15 +--- src/Ssl/KeyPair.php | 10 +-- src/Ssl/ParsedCertificate.php | 56 ++++----------- src/Ssl/ParsedKey.php | 47 +++---------- src/Ssl/Parser/CertificateParser.php | 7 +- src/Ssl/Parser/KeyParser.php | 7 +- src/Ssl/PrivateKey.php | 14 +--- src/Ssl/PublicKey.php | 12 +--- src/Ssl/Signer/CertificateRequestSigner.php | 10 +-- src/Ssl/Signer/DataSigner.php | 27 ++------ tests/Ssl/CertificateTest.php | 4 +- tests/Ssl/Generator/KeyPairGeneratorTest.php | 17 ++--- tests/Ssl/Parser/CertificateParserTest.php | 6 +- tests/Ssl/Parser/KeyParserTest.php | 12 ++-- tests/Ssl/PrivateKeyTest.php | 6 +- tests/Ssl/PublicKeyTest.php | 6 +- .../Signer/CertificateRequestSignerTest.php | 6 +- tests/Ssl/Signer/DataSignerTest.php | 4 +- 34 files changed, 142 insertions(+), 397 deletions(-) diff --git a/src/Ssl/Certificate.php b/src/Ssl/Certificate.php index c7bbab55..5b279cdf 100644 --- a/src/Ssl/Certificate.php +++ b/src/Ssl/Certificate.php @@ -27,11 +27,7 @@ class Certificate /** @var Certificate */ private $issuerCertificate; - /** - * @param string $certificatePEM - * @param Certificate|null $issuerCertificate - */ - public function __construct($certificatePEM, self $issuerCertificate = null) + public function __construct(string $certificatePEM, self $issuerCertificate = null) { Assert::stringNotEmpty($certificatePEM, __CLASS__.'::$certificatePEM should not be an empty string. Got %s'); @@ -42,7 +38,7 @@ public function __construct($certificatePEM, self $issuerCertificate = null) /** * @return Certificate[] */ - public function getIssuerChain() + public function getIssuerChain(): array { $chain = []; $issuerCertificate = $this->getIssuerCertificate(); @@ -55,18 +51,12 @@ public function getIssuerChain() return $chain; } - /** - * @return string - */ - public function getPEM() + public function getPEM(): string { return $this->certificatePEM; } - /** - * @return Certificate|null - */ - public function getIssuerCertificate() + public function getIssuerCertificate(): ?Certificate { return $this->issuerCertificate; } @@ -83,10 +73,7 @@ public function getPublicKeyResource() return $resource; } - /** - * @return PublicKey - */ - public function getPublicKey() + public function getPublicKey(): PublicKey { return new PublicKey(openssl_pkey_get_details($this->getPublicKeyResource())['key']); } diff --git a/src/Ssl/CertificateRequest.php b/src/Ssl/CertificateRequest.php index 68a7ab5b..93bea225 100644 --- a/src/Ssl/CertificateRequest.php +++ b/src/Ssl/CertificateRequest.php @@ -30,18 +30,12 @@ public function __construct(DistinguishedName $distinguishedName, KeyPair $keyPa $this->keyPair = $keyPair; } - /** - * @return DistinguishedName - */ - public function getDistinguishedName() + public function getDistinguishedName(): DistinguishedName { return $this->distinguishedName; } - /** - * @return KeyPair - */ - public function getKeyPair() + public function getKeyPair(): KeyPair { return $this->keyPair; } diff --git a/src/Ssl/CertificateResponse.php b/src/Ssl/CertificateResponse.php index 61e2eafd..5767b767 100644 --- a/src/Ssl/CertificateResponse.php +++ b/src/Ssl/CertificateResponse.php @@ -24,26 +24,18 @@ class CertificateResponse /** @var Certificate */ private $certificate; - public function __construct( - CertificateRequest $certificateRequest, - Certificate $certificate - ) { + public function __construct(CertificateRequest $certificateRequest, Certificate $certificate) + { $this->certificateRequest = $certificateRequest; $this->certificate = $certificate; } - /** - * @return CertificateRequest - */ - public function getCertificateRequest() + public function getCertificateRequest(): CertificateRequest { return $this->certificateRequest; } - /** - * @return Certificate - */ - public function getCertificate() + public function getCertificate(): Certificate { return $this->certificate; } diff --git a/src/Ssl/DistinguishedName.php b/src/Ssl/DistinguishedName.php index 824e2fbe..1136bb61 100644 --- a/src/Ssl/DistinguishedName.php +++ b/src/Ssl/DistinguishedName.php @@ -44,32 +44,17 @@ class DistinguishedName /** @var array */ private $subjectAlternativeNames; - /** - * @param string $commonName - * @param string $countryName - * @param string $stateOrProvinceName - * @param string $localityName - * @param string $organizationName - * @param string $organizationalUnitName - * @param string $emailAddress - */ public function __construct( - $commonName, - $countryName = null, - $stateOrProvinceName = null, - $localityName = null, - $organizationName = null, - $organizationalUnitName = null, - $emailAddress = null, + string $commonName, + string $countryName = null, + string $stateOrProvinceName = null, + string $localityName = null, + string $organizationName = null, + string $organizationalUnitName = null, + string $emailAddress = null, array $subjectAlternativeNames = [] ) { Assert::stringNotEmpty($commonName, __CLASS__.'::$commonName expected a non empty string. Got: %s'); - Assert::nullOrStringNotEmpty($countryName, __CLASS__.'::$countryName expected a string. Got: %s'); - Assert::nullOrStringNotEmpty($stateOrProvinceName, __CLASS__.'::$stateOrProvinceName expected a string. Got: %s'); - Assert::nullOrStringNotEmpty($localityName, __CLASS__.'::$localityName expected a string. Got: %s'); - Assert::nullOrStringNotEmpty($organizationName, __CLASS__.'::$organizationName expected a string. Got: %s'); - Assert::nullOrStringNotEmpty($organizationalUnitName, __CLASS__.'::$organizationalUnitName expected a string. Got: %s'); - Assert::nullOrStringNotEmpty($emailAddress, __CLASS__.'::$emailAddress expected a string. Got: %s'); Assert::allStringNotEmpty( $subjectAlternativeNames, __CLASS__.'::$subjectAlternativeNames expected an array of non empty string. Got: %s' @@ -85,66 +70,42 @@ public function __construct( $this->subjectAlternativeNames = array_diff(array_unique($subjectAlternativeNames), [$commonName]); } - /** - * @return string - */ - public function getCommonName() + public function getCommonName(): string { return $this->commonName; } - /** - * @return string - */ - public function getCountryName() + public function getCountryName(): ?string { return $this->countryName; } - /** - * @return string - */ - public function getStateOrProvinceName() + public function getStateOrProvinceName(): ?string { return $this->stateOrProvinceName; } - /** - * @return string - */ - public function getLocalityName() + public function getLocalityName(): ?string { return $this->localityName; } - /** - * @return string - */ - public function getOrganizationName() + public function getOrganizationName(): ?string { return $this->organizationName; } - /** - * @return string - */ - public function getOrganizationalUnitName() + public function getOrganizationalUnitName(): ?string { return $this->organizationalUnitName; } - /** - * @return string - */ - public function getEmailAddress() + public function getEmailAddress(): ?string { return $this->emailAddress; } - /** - * @return array - */ - public function getSubjectAlternativeNames() + public function getSubjectAlternativeNames(): array { return $this->subjectAlternativeNames; } diff --git a/src/Ssl/Generator/ChainPrivateKeyGenerator.php b/src/Ssl/Generator/ChainPrivateKeyGenerator.php index c5d492ea..ffa4df09 100644 --- a/src/Ssl/Generator/ChainPrivateKeyGenerator.php +++ b/src/Ssl/Generator/ChainPrivateKeyGenerator.php @@ -11,6 +11,8 @@ namespace AcmePhp\Ssl\Generator; +use AcmePhp\Ssl\PrivateKey; + /** * Generate random RSA private key using OpenSSL. * @@ -24,12 +26,12 @@ class ChainPrivateKeyGenerator implements PrivateKeyGeneratorInterface /** * @param PrivateKeyGeneratorInterface[] $generators */ - public function __construct($generators) + public function __construct(iterable $generators) { $this->generators = $generators; } - public function generatePrivateKey(KeyOption $keyOption) + public function generatePrivateKey(KeyOption $keyOption): PrivateKey { foreach ($this->generators as $generator) { if ($generator->supportsKeyOption($keyOption)) { @@ -40,7 +42,7 @@ public function generatePrivateKey(KeyOption $keyOption) throw new \LogicException(sprintf('Unable to find a generator for a key option of type %s', \get_class($keyOption))); } - public function supportsKeyOption(KeyOption $keyOption) + public function supportsKeyOption(KeyOption $keyOption): bool { foreach ($this->generators as $generator) { if ($generator->supportsKeyOption($keyOption)) { diff --git a/src/Ssl/Generator/DhKey/DhKeyGenerator.php b/src/Ssl/Generator/DhKey/DhKeyGenerator.php index 4b719e5e..0b73296f 100644 --- a/src/Ssl/Generator/DhKey/DhKeyGenerator.php +++ b/src/Ssl/Generator/DhKey/DhKeyGenerator.php @@ -14,6 +14,7 @@ use AcmePhp\Ssl\Generator\KeyOption; use AcmePhp\Ssl\Generator\OpensslPrivateKeyGeneratorTrait; use AcmePhp\Ssl\Generator\PrivateKeyGeneratorInterface; +use AcmePhp\Ssl\PrivateKey; use Webmozart\Assert\Assert; /** @@ -25,25 +26,20 @@ class DhKeyGenerator implements PrivateKeyGeneratorInterface { use OpensslPrivateKeyGeneratorTrait; - /** - * @param DhKeyOption|KeyOption $keyOption - */ - public function generatePrivateKey(KeyOption $keyOption) + public function generatePrivateKey(KeyOption $keyOption): PrivateKey { Assert::isInstanceOf($keyOption, DhKeyOption::class); - return $this->generatePrivateKeyFromOpensslOptions( - [ - 'private_key_type' => OPENSSL_KEYTYPE_DH, - 'dh' => [ - 'p' => $keyOption->getPrime(), - 'g' => $keyOption->getGenerator(), - ], - ] - ); + return $this->generatePrivateKeyFromOpensslOptions([ + 'private_key_type' => OPENSSL_KEYTYPE_DH, + 'dh' => [ + 'p' => $keyOption->getPrime(), + 'g' => $keyOption->getGenerator(), + ], + ]); } - public function supportsKeyOption(KeyOption $keyOption) + public function supportsKeyOption(KeyOption $keyOption): bool { return $keyOption instanceof DhKeyOption; } diff --git a/src/Ssl/Generator/DhKey/DhKeyOption.php b/src/Ssl/Generator/DhKey/DhKeyOption.php index d6bdc116..0ecc1a55 100644 --- a/src/Ssl/Generator/DhKey/DhKeyOption.php +++ b/src/Ssl/Generator/DhKey/DhKeyOption.php @@ -17,6 +17,7 @@ class DhKeyOption implements KeyOption { /** @var string */ private $generator; + /** @var string */ private $prime; @@ -26,24 +27,18 @@ class DhKeyOption implements KeyOption * * @see https://tools.ietf.org/html/rfc3526 how to choose a prime and generator numbers */ - public function __construct($prime, $generator = '02') + public function __construct(string $prime, string $generator = '02') { $this->generator = pack('H*', $generator); $this->prime = pack('H*', $prime); } - /** - * @return string - */ - public function getGenerator() + public function getGenerator(): string { return $this->generator; } - /** - * @return string - */ - public function getPrime() + public function getPrime(): string { return $this->prime; } diff --git a/src/Ssl/Generator/DsaKey/DsaKeyGenerator.php b/src/Ssl/Generator/DsaKey/DsaKeyGenerator.php index 5547b568..7bf15382 100644 --- a/src/Ssl/Generator/DsaKey/DsaKeyGenerator.php +++ b/src/Ssl/Generator/DsaKey/DsaKeyGenerator.php @@ -14,6 +14,7 @@ use AcmePhp\Ssl\Generator\KeyOption; use AcmePhp\Ssl\Generator\OpensslPrivateKeyGeneratorTrait; use AcmePhp\Ssl\Generator\PrivateKeyGeneratorInterface; +use AcmePhp\Ssl\PrivateKey; use Webmozart\Assert\Assert; /** @@ -25,22 +26,17 @@ class DsaKeyGenerator implements PrivateKeyGeneratorInterface { use OpensslPrivateKeyGeneratorTrait; - /** - * @param DsaKeyOption|KeyOption $keyOption - */ - public function generatePrivateKey(KeyOption $keyOption) + public function generatePrivateKey(KeyOption $keyOption): PrivateKey { Assert::isInstanceOf($keyOption, DsaKeyOption::class); - return $this->generatePrivateKeyFromOpensslOptions( - [ - 'private_key_type' => OPENSSL_KEYTYPE_DSA, - 'private_key_bits' => $keyOption->getBits(), - ] - ); + return $this->generatePrivateKeyFromOpensslOptions([ + 'private_key_type' => OPENSSL_KEYTYPE_DSA, + 'private_key_bits' => $keyOption->getBits(), + ]); } - public function supportsKeyOption(KeyOption $keyOption) + public function supportsKeyOption(KeyOption $keyOption): bool { return $keyOption instanceof DsaKeyOption; } diff --git a/src/Ssl/Generator/DsaKey/DsaKeyOption.php b/src/Ssl/Generator/DsaKey/DsaKeyOption.php index 344cbabd..615d80e2 100644 --- a/src/Ssl/Generator/DsaKey/DsaKeyOption.php +++ b/src/Ssl/Generator/DsaKey/DsaKeyOption.php @@ -12,24 +12,18 @@ namespace AcmePhp\Ssl\Generator\DsaKey; use AcmePhp\Ssl\Generator\KeyOption; -use Webmozart\Assert\Assert; class DsaKeyOption implements KeyOption { /** @var int */ private $bits; - public function __construct($bits = 2048) + public function __construct(int $bits = 2048) { - Assert::integer($bits); - $this->bits = $bits; } - /** - * @return int - */ - public function getBits() + public function getBits(): int { return $this->bits; } diff --git a/src/Ssl/Generator/EcKey/EcKeyGenerator.php b/src/Ssl/Generator/EcKey/EcKeyGenerator.php index ebcbe6d9..88ebc0c3 100644 --- a/src/Ssl/Generator/EcKey/EcKeyGenerator.php +++ b/src/Ssl/Generator/EcKey/EcKeyGenerator.php @@ -14,6 +14,7 @@ use AcmePhp\Ssl\Generator\KeyOption; use AcmePhp\Ssl\Generator\OpensslPrivateKeyGeneratorTrait; use AcmePhp\Ssl\Generator\PrivateKeyGeneratorInterface; +use AcmePhp\Ssl\PrivateKey; use Webmozart\Assert\Assert; /** @@ -25,26 +26,17 @@ class EcKeyGenerator implements PrivateKeyGeneratorInterface { use OpensslPrivateKeyGeneratorTrait; - /** - * @param EcKeyOption|KeyOption $keyOption - */ - public function generatePrivateKey(KeyOption $keyOption) + public function generatePrivateKey(KeyOption $keyOption): PrivateKey { - if (\PHP_VERSION_ID < 70100) { - throw new \LogicException('The generation of ECDSA requires a version of PHP >= 7.1'); - } - Assert::isInstanceOf($keyOption, EcKeyOption::class); - return $this->generatePrivateKeyFromOpensslOptions( - [ - 'private_key_type' => OPENSSL_KEYTYPE_EC, - 'curve_name' => $keyOption->getCurveName(), - ] - ); + return $this->generatePrivateKeyFromOpensslOptions([ + 'private_key_type' => OPENSSL_KEYTYPE_EC, + 'curve_name' => $keyOption->getCurveName(), + ]); } - public function supportsKeyOption(KeyOption $keyOption) + public function supportsKeyOption(KeyOption $keyOption): bool { return $keyOption instanceof EcKeyOption; } diff --git a/src/Ssl/Generator/EcKey/EcKeyOption.php b/src/Ssl/Generator/EcKey/EcKeyOption.php index 813be898..3f564ca1 100644 --- a/src/Ssl/Generator/EcKey/EcKeyOption.php +++ b/src/Ssl/Generator/EcKey/EcKeyOption.php @@ -19,22 +19,18 @@ class EcKeyOption implements KeyOption /** @var string */ private $curveName; - public function __construct($curveName = 'secp384r1') + public function __construct(string $curveName = 'secp384r1') { if (\PHP_VERSION_ID < 70100) { throw new \LogicException('The generation of ECDSA requires a version of PHP >= 7.1'); } - Assert::stringNotEmpty($curveName); Assert::oneOf($curveName, openssl_get_curve_names(), 'The given curve %s is not supported. Available curves are: %s'); $this->curveName = $curveName; } - /** - * @return string - */ - public function getCurveName() + public function getCurveName(): string { return $this->curveName; } diff --git a/src/Ssl/Generator/KeyPairGenerator.php b/src/Ssl/Generator/KeyPairGenerator.php index 1e248b85..67d4d56b 100644 --- a/src/Ssl/Generator/KeyPairGenerator.php +++ b/src/Ssl/Generator/KeyPairGenerator.php @@ -19,7 +19,6 @@ use AcmePhp\Ssl\Generator\RsaKey\RsaKeyGenerator; use AcmePhp\Ssl\Generator\RsaKey\RsaKeyOption; use AcmePhp\Ssl\KeyPair; -use Webmozart\Assert\Assert; /** * Generate random KeyPair using OpenSSL. @@ -43,24 +42,15 @@ public function __construct(PrivateKeyGeneratorInterface $generator = null) } /** - * Generate KeyPair. - * - * @param KeyOption $keyOption configuration of the key to generate + * @param KeyOption|null $keyOption configuration of the key to generate * * @throws KeyPairGenerationException when OpenSSL failed to generate keys - * - * @return KeyPair */ - public function generateKeyPair($keyOption = null) + public function generateKeyPair(KeyOption $keyOption = null): KeyPair { if (null === $keyOption) { $keyOption = new RsaKeyOption(); } - if (\is_int($keyOption)) { - @trigger_error('Passing a keySize to "generateKeyPair" is deprecated since version 1.1 and will be removed in 2.0. Pass an instance of KeyOption instead', E_USER_DEPRECATED); - $keyOption = new RsaKeyOption($keyOption); - } - Assert::isInstanceOf($keyOption, KeyOption::class); try { $privateKey = $this->generator->generatePrivateKey($keyOption); diff --git a/src/Ssl/Generator/OpensslPrivateKeyGeneratorTrait.php b/src/Ssl/Generator/OpensslPrivateKeyGeneratorTrait.php index d2b8e005..7fc1e3df 100644 --- a/src/Ssl/Generator/OpensslPrivateKeyGeneratorTrait.php +++ b/src/Ssl/Generator/OpensslPrivateKeyGeneratorTrait.php @@ -17,7 +17,7 @@ trait OpensslPrivateKeyGeneratorTrait { - private function generatePrivateKeyFromOpensslOptions(array $opensslOptions) + private function generatePrivateKeyFromOpensslOptions(array $opensslOptions): PrivateKey { $resource = openssl_pkey_new($opensslOptions); diff --git a/src/Ssl/Generator/PrivateKeyGeneratorInterface.php b/src/Ssl/Generator/PrivateKeyGeneratorInterface.php index e0ea2086..98860251 100644 --- a/src/Ssl/Generator/PrivateKeyGeneratorInterface.php +++ b/src/Ssl/Generator/PrivateKeyGeneratorInterface.php @@ -27,17 +27,13 @@ interface PrivateKeyGeneratorInterface * @param KeyOption $keyOption configuration of the key to generate * * @throws KeyGenerationException when OpenSSL failed to generate keys - * - * @return PrivateKey */ - public function generatePrivateKey(KeyOption $keyOption); + public function generatePrivateKey(KeyOption $keyOption): PrivateKey; /** * Returns whether the instance is able to generator a private key from the given option. * * @param KeyOption $keyOption configuration of the key to generate - * - * @return bool */ - public function supportsKeyOption(KeyOption $keyOption); + public function supportsKeyOption(KeyOption $keyOption): bool; } diff --git a/src/Ssl/Generator/RsaKey/RsaKeyGenerator.php b/src/Ssl/Generator/RsaKey/RsaKeyGenerator.php index 78f356fe..6d85a248 100644 --- a/src/Ssl/Generator/RsaKey/RsaKeyGenerator.php +++ b/src/Ssl/Generator/RsaKey/RsaKeyGenerator.php @@ -14,6 +14,7 @@ use AcmePhp\Ssl\Generator\KeyOption; use AcmePhp\Ssl\Generator\OpensslPrivateKeyGeneratorTrait; use AcmePhp\Ssl\Generator\PrivateKeyGeneratorInterface; +use AcmePhp\Ssl\PrivateKey; use Webmozart\Assert\Assert; /** @@ -25,22 +26,17 @@ class RsaKeyGenerator implements PrivateKeyGeneratorInterface { use OpensslPrivateKeyGeneratorTrait; - /** - * @param RsaKeyOption|KeyOption $keyOption - */ - public function generatePrivateKey(KeyOption $keyOption) + public function generatePrivateKey(KeyOption $keyOption): PrivateKey { Assert::isInstanceOf($keyOption, RsaKeyOption::class); - return $this->generatePrivateKeyFromOpensslOptions( - [ - 'private_key_type' => OPENSSL_KEYTYPE_RSA, - 'private_key_bits' => $keyOption->getBits(), - ] - ); + return $this->generatePrivateKeyFromOpensslOptions([ + 'private_key_type' => OPENSSL_KEYTYPE_RSA, + 'private_key_bits' => $keyOption->getBits(), + ]); } - public function supportsKeyOption(KeyOption $keyOption) + public function supportsKeyOption(KeyOption $keyOption): bool { return $keyOption instanceof RsaKeyOption; } diff --git a/src/Ssl/Generator/RsaKey/RsaKeyOption.php b/src/Ssl/Generator/RsaKey/RsaKeyOption.php index 27f9345d..1c8e6f50 100644 --- a/src/Ssl/Generator/RsaKey/RsaKeyOption.php +++ b/src/Ssl/Generator/RsaKey/RsaKeyOption.php @@ -12,24 +12,18 @@ namespace AcmePhp\Ssl\Generator\RsaKey; use AcmePhp\Ssl\Generator\KeyOption; -use Webmozart\Assert\Assert; class RsaKeyOption implements KeyOption { /** @var int */ private $bits; - public function __construct($bits = 4096) + public function __construct(int $bits = 4096) { - Assert::integer($bits); - $this->bits = $bits; } - /** - * @return int - */ - public function getBits() + public function getBits(): int { return $this->bits; } diff --git a/src/Ssl/Key.php b/src/Ssl/Key.php index 1249b546..6614ffb8 100644 --- a/src/Ssl/Key.php +++ b/src/Ssl/Key.php @@ -23,28 +23,19 @@ abstract class Key /** @var string */ protected $keyPEM; - /** - * @param string $keyPEM - */ - public function __construct($keyPEM) + public function __construct(string $keyPEM) { Assert::stringNotEmpty($keyPEM, __CLASS__.'::$keyPEM should not be an empty string. Got %s'); $this->keyPEM = $keyPEM; } - /** - * @return string - */ - public function getPEM() + public function getPEM(): string { return $this->keyPEM; } - /** - * @return string - */ - public function getDER() + public function getDER(): string { $lines = explode("\n", trim($this->keyPEM)); unset($lines[\count($lines) - 1]); diff --git a/src/Ssl/KeyPair.php b/src/Ssl/KeyPair.php index 6d733c6a..903d65ff 100644 --- a/src/Ssl/KeyPair.php +++ b/src/Ssl/KeyPair.php @@ -30,18 +30,12 @@ public function __construct(PublicKey $publicKey, PrivateKey $privateKey) $this->privateKey = $privateKey; } - /** - * @return PublicKey - */ - public function getPublicKey() + public function getPublicKey(): PublicKey { return $this->publicKey; } - /** - * @return PrivateKey - */ - public function getPrivateKey() + public function getPrivateKey(): PrivateKey { return $this->privateKey; } diff --git a/src/Ssl/ParsedCertificate.php b/src/Ssl/ParsedCertificate.php index c2770f21..3facc645 100644 --- a/src/Ssl/ParsedCertificate.php +++ b/src/Ssl/ParsedCertificate.php @@ -54,18 +54,15 @@ class ParsedCertificate */ public function __construct( Certificate $source, - $subject, - $issuer = null, - $selfSigned = true, + string $subject, + string $issuer = null, + bool $selfSigned = true, \DateTime $validFrom = null, \DateTime $validTo = null, - $serialNumber = null, + string $serialNumber = null, array $subjectAlternativeNames = [] ) { Assert::stringNotEmpty($subject, __CLASS__.'::$subject expected a non empty string. Got: %s'); - Assert::nullOrString($issuer, __CLASS__.'::$issuer expected a string or null. Got: %s'); - Assert::nullOrBoolean($selfSigned, __CLASS__.'::$selfSigned expected a boolean or null. Got: %s'); - Assert::nullOrString($serialNumber, __CLASS__.'::$serialNumber expected a string or null. Got: %s'); Assert::allStringNotEmpty( $subjectAlternativeNames, __CLASS__.'::$subjectAlternativeNames expected a array of non empty string. Got: %s' @@ -81,74 +78,47 @@ public function __construct( $this->subjectAlternativeNames = $subjectAlternativeNames; } - /** - * @return Certificate - */ - public function getSource() + public function getSource(): Certificate { return $this->source; } - /** - * @return string - */ - public function getSubject() + public function getSubject(): string { return $this->subject; } - /** - * @return string - */ - public function getIssuer() + public function getIssuer(): ?string { return $this->issuer; } - /** - * @return bool - */ - public function isSelfSigned() + public function isSelfSigned(): bool { return $this->selfSigned; } - /** - * @return \DateTime - */ - public function getValidFrom() + public function getValidFrom(): \DateTimeInterface { return $this->validFrom; } - /** - * @return \DateTime - */ - public function getValidTo() + public function getValidTo(): \DateTimeInterface { return $this->validTo; } - /** - * @return bool - */ - public function isExpired() + public function isExpired(): bool { return $this->validTo < (new \DateTime()); } - /** - * @return string - */ - public function getSerialNumber() + public function getSerialNumber(): ?string { return $this->serialNumber; } - /** - * @return array - */ - public function getSubjectAlternativeNames() + public function getSubjectAlternativeNames(): array { return $this->subjectAlternativeNames; } diff --git a/src/Ssl/ParsedKey.php b/src/Ssl/ParsedKey.php index 083013f9..4a706246 100644 --- a/src/Ssl/ParsedKey.php +++ b/src/Ssl/ParsedKey.php @@ -37,15 +37,9 @@ class ParsedKey /** @var array */ private $details; - /** - * @param string $key - * @param int $bits - * @param int $type - */ - public function __construct(Key $source, $key, $bits, $type, array $details = []) + public function __construct(Key $source, string $key, int $bits, int $type, array $details = []) { Assert::stringNotEmpty($key, __CLASS__.'::$key expected a non empty string. Got: %s'); - Assert::integer($bits, __CLASS__.'::$bits expected an integer. Got: %s'); Assert::oneOf( $type, [OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_DSA, OPENSSL_KEYTYPE_DH, OPENSSL_KEYTYPE_EC], @@ -59,62 +53,37 @@ public function __construct(Key $source, $key, $bits, $type, array $details = [] $this->details = $details; } - /** - * @return Key - */ - public function getSource() + public function getSource(): Key { return $this->source; } - /** - * @return string - */ - public function getKey() + public function getKey(): string { return $this->key; } - /** - * @return int - */ - public function getBits() + public function getBits(): int { return $this->bits; } - /** - * @return int - */ - public function getType() + public function getType(): int { return $this->type; } - /** - * @return array - */ - public function getDetails() + public function getDetails(): array { return $this->details; } - /** - * @param string $name - * - * @return bool - */ - public function hasDetail($name) + public function hasDetail(string $name): bool { return isset($this->details[$name]); } - /** - * @param string $name - * - * @return mixed - */ - public function getDetail($name) + public function getDetail(string $name) { Assert::oneOf($name, array_keys($this->details), 'ParsedKey::getDetail() expected one of: %2$s. Got: %s'); diff --git a/src/Ssl/Parser/CertificateParser.php b/src/Ssl/Parser/CertificateParser.php index c01f779a..d3116de9 100644 --- a/src/Ssl/Parser/CertificateParser.php +++ b/src/Ssl/Parser/CertificateParser.php @@ -22,12 +22,7 @@ */ class CertificateParser { - /** - * Parse the certificate. - * - * @return ParsedCertificate - */ - public function parse(Certificate $certificate) + public function parse(Certificate $certificate): ParsedCertificate { $rawData = openssl_x509_parse($certificate->getPEM()); diff --git a/src/Ssl/Parser/KeyParser.php b/src/Ssl/Parser/KeyParser.php index e4f205fb..215a975f 100644 --- a/src/Ssl/Parser/KeyParser.php +++ b/src/Ssl/Parser/KeyParser.php @@ -23,12 +23,7 @@ */ class KeyParser { - /** - * Parse the key. - * - * @return ParsedKey - */ - public function parse(Key $key) + public function parse(Key $key): ParsedKey { try { $resource = $key->getResource(); diff --git a/src/Ssl/PrivateKey.php b/src/Ssl/PrivateKey.php index 3f464bcf..c7080035 100644 --- a/src/Ssl/PrivateKey.php +++ b/src/Ssl/PrivateKey.php @@ -22,7 +22,7 @@ class PrivateKey extends Key { /** - * {@inheritdoc} + * {@inheritdoc} */ public function getResource() { @@ -33,10 +33,7 @@ public function getResource() return $resource; } - /** - * @return PublicKey - */ - public function getPublicKey() + public function getPublicKey(): PublicKey { $resource = $this->getResource(); if (!$details = openssl_pkey_get_details($resource)) { @@ -51,12 +48,7 @@ public function getPublicKey() return new PublicKey($details['key']); } - /** - * @param $keyDER - * - * @return PrivateKey - */ - public static function fromDER($keyDER) + public static function fromDER(string $keyDER): self { Assert::stringNotEmpty($keyDER, __METHOD__.'::$keyDER should be a non-empty string. Got %s'); diff --git a/src/Ssl/PublicKey.php b/src/Ssl/PublicKey.php index 83040c29..f0a511d2 100644 --- a/src/Ssl/PublicKey.php +++ b/src/Ssl/PublicKey.php @@ -33,12 +33,7 @@ public function getResource() return $resource; } - /** - * @param $keyDER - * - * @return PublicKey - */ - public static function fromDER($keyDER) + public static function fromDER(string $keyDER): self { Assert::stringNotEmpty($keyDER, __METHOD__.'::$keyDER should be a non-empty string. Got %s'); @@ -51,10 +46,7 @@ public static function fromDER($keyDER) return new self(implode("\n", $lines)); } - /** - * @return string - */ - public function getHPKP() + public function getHPKP(): string { return base64_encode(hash('sha256', $this->getDER(), true)); } diff --git a/src/Ssl/Signer/CertificateRequestSigner.php b/src/Ssl/Signer/CertificateRequestSigner.php index acb6f85e..cf7f35cd 100644 --- a/src/Ssl/Signer/CertificateRequestSigner.php +++ b/src/Ssl/Signer/CertificateRequestSigner.php @@ -24,10 +24,8 @@ class CertificateRequestSigner { /** * Generate a CSR from the given distinguishedName and keyPair. - * - * @return string */ - public function signCertificateRequest(CertificateRequest $certificateRequest) + public function signCertificateRequest(CertificateRequest $certificateRequest): string { $csrObject = $this->createCsrWithSANsObject($certificateRequest); @@ -40,8 +38,6 @@ public function signCertificateRequest(CertificateRequest $certificateRequest) /** * Generate a CSR object with SANs from the given distinguishedName and keyPair. - * - * @return mixed */ protected function createCsrWithSANsObject(CertificateRequest $certificateRequest) { @@ -103,10 +99,8 @@ protected function createCsrWithSANsObject(CertificateRequest $certificateReques /** * Retrieves a CSR payload from the given distinguished name. - * - * @return array */ - private function getCSRPayload(DistinguishedName $distinguishedName) + private function getCSRPayload(DistinguishedName $distinguishedName): array { $payload = []; if (null !== $countryName = $distinguishedName->getCountryName()) { diff --git a/src/Ssl/Signer/DataSigner.php b/src/Ssl/Signer/DataSigner.php index 5657ccd2..2db48f9e 100644 --- a/src/Ssl/Signer/DataSigner.php +++ b/src/Ssl/Signer/DataSigner.php @@ -22,8 +22,8 @@ */ class DataSigner { - const FORMAT_DER = 'DER'; - const FORMAT_ECDSA = 'ECDSA'; + public const FORMAT_DER = 'DER'; + public const FORMAT_ECDSA = 'ECDSA'; /** * Generate a signature of the given data using a private key and an algorithm. @@ -32,10 +32,8 @@ class DataSigner * @param PrivateKey $privateKey Key used to sign * @param int $algorithm Signature algorithm defined by constants OPENSSL_ALGO_* * @param string $format Format of the output - * - * @return string */ - public function signData($data, PrivateKey $privateKey, $algorithm = OPENSSL_ALGO_SHA256, $format = self::FORMAT_DER) + public function signData(string $data, PrivateKey $privateKey, int $algorithm = OPENSSL_ALGO_SHA256, string $format = self::FORMAT_DER): string { Assert::oneOf($format, [self::FORMAT_ECDSA, self::FORMAT_DER], 'The format %s to sign request does not exists. Available format: %s'); @@ -74,7 +72,7 @@ public function signData($data, PrivateKey $privateKey, $algorithm = OPENSSL_ALG * * @see https://github.com/web-token/jwt-core/blob/master/Util/ECSignature.php */ - private function DERtoECDSA($der, $partLength) + private function DERtoECDSA($der, $partLength): string { $hex = unpack('H*', $der)[1]; if ('30' !== mb_substr($hex, 0, 2, '8bit')) { // SEQUENCE @@ -104,23 +102,6 @@ private function DERtoECDSA($der, $partLength) return pack('H*', $R.$S); } - /** - * The code is a copy/paste from another lib (web-token/jwt-core) which is not compatible with php <= 7.0. - * - * @see https://github.com/web-token/jwt-core/blob/master/Util/ECSignature.php - */ - private function preparePositiveInteger($data) - { - if (mb_substr($data, 0, 2, '8bit') > '7f') { - return '00'.$data; - } - while ('00' === mb_substr($data, 0, 2, '8bit') && mb_substr($data, 2, 2, '8bit') <= '7f') { - $data = mb_substr($data, 2, null, '8bit'); - } - - return $data; - } - /** * The code is a copy/paste from another lib (web-token/jwt-core) which is not compatible with php <= 7.0. * diff --git a/tests/Ssl/CertificateTest.php b/tests/Ssl/CertificateTest.php index 9531a7c4..2202b96d 100644 --- a/tests/Ssl/CertificateTest.php +++ b/tests/Ssl/CertificateTest.php @@ -17,7 +17,7 @@ class CertificateTest extends TestCase { - public function test getPublicKey returns a PublicKey() + public function test_getPublicKey_returns_a_PublicKey() { $certificate = new Certificate( ' @@ -61,7 +61,7 @@ public function test getPublicKey returns a PublicKey() $this->assertEquals('58b94e38ce0088f0ec5a0c38f04bd76c', md5($publicKey->getPEM())); } - public function test getPublicKeyResource returns a resource() + public function test_getPublicKeyResource_returns_a_resource() { $certificate = new Certificate( ' diff --git a/tests/Ssl/Generator/KeyPairGeneratorTest.php b/tests/Ssl/Generator/KeyPairGeneratorTest.php index e9d3612b..10ae5812 100644 --- a/tests/Ssl/Generator/KeyPairGeneratorTest.php +++ b/tests/Ssl/Generator/KeyPairGeneratorTest.php @@ -31,16 +31,7 @@ public function setUp(): void $this->service = new KeyPairGenerator(); } - /** - * @group legacy - */ - public function test generateKeyPair supports keysize() - { - $result = $this->service->generateKeyPair(1024); - $this->assertInstanceOf(KeyPair::class, $result); - } - - public function test generateKeyPair generate random instance of KeyPair() + public function test_generateKeyPair_generate_random_instance_of_KeyPair() { $result = $this->service->generateKeyPair(new RsaKeyOption(1024)); @@ -55,7 +46,7 @@ public function test generateKeyPair generate random instance of KeyPair() $this->assertArrayHasKey('rsa', $details); } - public function test generateKeyPair generate random instance of KeyPair using DH() + public function test_generateKeyPair_generate_random_instance_of_KeyPair_using_DH() { $result = $this->service->generateKeyPair(new DhKeyOption( 'dcf93a0b883972ec0e19989ac5a2ce310e1d37717e8d9571bb7623731866e61ef75a2e27898b057f9891c2e27a639c3f29b60814581cd3b2ca3986d2683705577d45c2e7e52dc81c7a171876e5cea74b1448bfdfaf18828efd2519f14e45e3826634af1949e5b535cc829a483b8a76223e5d490a257f05bdff16f2fb22c583ab', @@ -72,7 +63,7 @@ public function test generateKeyPair generate random instance of KeyPair  $this->assertArrayHasKey('dh', $details); } - public function test generateKeyPair generate random instance of KeyPair using DSA() + public function test_generateKeyPair_generate_random_instance_of_KeyPair_using_DSA() { $result = $this->service->generateKeyPair(new DsaKeyOption(1024)); @@ -90,7 +81,7 @@ public function test generateKeyPair generate random instance of KeyPair  /** * @requires PHP 7.1 */ - public function test generateKeyPair generate random instance of KeyPair using EC() + public function test_generateKeyPair_generate_random_instance_of_KeyPair_using_EC() { $result = $this->service->generateKeyPair(new EcKeyOption('secp112r1')); diff --git a/tests/Ssl/Parser/CertificateParserTest.php b/tests/Ssl/Parser/CertificateParserTest.php index 692f76a0..06fe6a37 100644 --- a/tests/Ssl/Parser/CertificateParserTest.php +++ b/tests/Ssl/Parser/CertificateParserTest.php @@ -28,13 +28,13 @@ public function setUp(): void $this->service = new CertificateParser(); } - public function test parse raise proper exception() + public function test_parse_raise_proper_exception() { $this->expectException('AcmePhp\Ssl\Exception\CertificateParsingException'); $this->service->parse(new Certificate('Not a cert')); } - public function test parse returns instance of ParsedCertificate() + public function test_parse_returns_instance_of_ParsedCertificate() { $result = $this->service->parse( new Certificate( @@ -88,7 +88,7 @@ public function test parse returns instance of ParsedCertificate() $this->assertFalse($result->isSelfSigned()); } - public function test parse without issuer CN returns instance of ParsedCertificate() + public function test_parse_without_issuer_CN_returns_instance_of_ParsedCertificate() { $result = $this->service->parse( new Certificate( diff --git a/tests/Ssl/Parser/KeyParserTest.php b/tests/Ssl/Parser/KeyParserTest.php index 0a2ea7fb..991ede29 100644 --- a/tests/Ssl/Parser/KeyParserTest.php +++ b/tests/Ssl/Parser/KeyParserTest.php @@ -30,30 +30,30 @@ public function setUp(): void $this->service = new KeyParser(); } - public function test parse PublicKey raise proper exception() + public function test_parse_PublicKey_raise_proper_exception() { $this->expectException('AcmePhp\Ssl\Exception\KeyParsingException'); $this->service->parse(new PublicKey('Not a key')); } - public function test parse PrivateKey raise proper exception() + public function test_parse_PrivateKey_raise_proper_exception() { $this->expectException('AcmePhp\Ssl\Exception\KeyParsingException'); $this->service->parse(new PrivateKey('Not a key')); } - public function test get PrivateKey has invalid detail() + public function test_get_PrivateKey_has_invalid_detail() { $this->assertFalse($this->service->parse($this->getPrivateKey())->hasDetail('invalid')); } - public function test get PrivateKey get invalid detail raise proper exception() + public function test_get_PrivateKey_get_invalid_detail_raise_proper_exception() { $this->expectException('InvalidArgumentException'); $this->service->parse($this->getPrivateKey())->getDetail('invalid'); } - public function test parse PrivateKey returns instance of ParsedKey() + public function test_parse_PrivateKey_returns_instance_of_ParsedKey() { $result = $this->service->parse($this->getPrivateKey()); @@ -67,7 +67,7 @@ public function test parse PrivateKey returns instance of ParsedKey() $this->assertEquals(trim($this->getPublicKey()->getPEM()), trim($result->getKey())); } - public function test parse PublicKey returns instance of ParsedKey() + public function test_parse_PublicKey_returns_instance_of_ParsedKey() { $result = $this->service->parse($this->getPublicKey()); diff --git a/tests/Ssl/PrivateKeyTest.php b/tests/Ssl/PrivateKeyTest.php index 2bc80dfe..bd3cd155 100644 --- a/tests/Ssl/PrivateKeyTest.php +++ b/tests/Ssl/PrivateKeyTest.php @@ -17,7 +17,7 @@ class PrivateKeyTest extends TestCase { - public function test getPublicKey returns a PublicKey() + public function test_getPublicKey_returns_a_PublicKey() { $privateKey = new PrivateKey('-----BEGIN PRIVATE KEY----- MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDH3IKV8sJZZHGd @@ -78,7 +78,7 @@ public function test getPublicKey returns a PublicKey() $this->assertEquals('80969771cf03d0331d1911810feff5fc', md5($publicKey->getPEM())); } - public function test fromDER returns a PrivateKey() + public function test_fromDER_returns_a_PrivateKey() { $derb64 = 'MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDH3IKV8sJZZHGd Q0vUN9GHJACixg8N1wFpUe763HmnWwiyCFHK9YjOfxkDSRK+2lP72Ns+RTBwtM8s @@ -137,7 +137,7 @@ public function test fromDER returns a PrivateKey() $this->assertEquals('a6dcb8eaae257961d2ee888899f087ef', md5($privateKey->getPEM())); } - public function test getDER returns a string() + public function test_getDER_returns_a_string() { $privateKey = new PrivateKey('-----BEGIN PRIVATE KEY----- MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDH3IKV8sJZZHGd diff --git a/tests/Ssl/PublicKeyTest.php b/tests/Ssl/PublicKeyTest.php index 35069b92..bc4034fe 100644 --- a/tests/Ssl/PublicKeyTest.php +++ b/tests/Ssl/PublicKeyTest.php @@ -16,7 +16,7 @@ class PublicKeyTest extends TestCase { - public function test fromDER returns a PublicKey() + public function test_fromDER_returns_a_PublicKey() { $derb64 = 'MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAx9yClfLCWWRxnUNL1DfR hyQAosYPDdcBaVHu+tx5p1sIsghRyvWIzn8ZA0kSvtpT+9jbPkUwcLTPLGW0SAC8 @@ -37,7 +37,7 @@ public function test fromDER returns a PublicKey() $this->assertEquals('48fa4235a71c704c815363702d7effbb', md5($publicKey->getPEM())); } - public function test getDER returns a string() + public function test_getDER_returns_a_string() { $publicKey = new PublicKey('-----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAx9yClfLCWWRxnUNL1DfR @@ -60,7 +60,7 @@ public function test getDER returns a string() $this->assertEquals('d2ea173bab74794037c74653b65433af', md5($der)); } - public function test getHPKP returns a string() + public function test_getHPKP_returns_a_string() { $publicKey = new PublicKey('-----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAx9yClfLCWWRxnUNL1DfR diff --git a/tests/Ssl/Signer/CertificateRequestSignerTest.php b/tests/Ssl/Signer/CertificateRequestSignerTest.php index 20a2ba8e..bb9a660b 100644 --- a/tests/Ssl/Signer/CertificateRequestSignerTest.php +++ b/tests/Ssl/Signer/CertificateRequestSignerTest.php @@ -30,7 +30,7 @@ public function setUp(): void $this->service = new CertificateRequestSigner(); } - public function test signCertificateRequest returns a certificate() + public function test_signCertificateRequest_returns_a_certificate() { $dummyDistinguishedName = new DistinguishedName( 'acmephp.com', @@ -59,7 +59,7 @@ public function test signCertificateRequest returns a certificate() ); } - public function test signCertificateRequest use default values() + public function test_signCertificateRequest_use_default_values() { $dummyDistinguishedName = new DistinguishedName( 'acmephp.com' @@ -80,7 +80,7 @@ public function test signCertificateRequest use default values() ); } - public function test signCertificateRequest with subject alternative names() + public function test_signCertificateRequest_with_subject_alternative_names() { $dummyDistinguishedName = new DistinguishedName( 'acmephp.com', diff --git a/tests/Ssl/Signer/DataSignerTest.php b/tests/Ssl/Signer/DataSignerTest.php index 91847b10..fce80d15 100644 --- a/tests/Ssl/Signer/DataSignerTest.php +++ b/tests/Ssl/Signer/DataSignerTest.php @@ -30,7 +30,7 @@ public function setUp(): void $this->service = new DataSigner(); } - public function test signData returns a signature() + public function test_signData_returns_a_signature() { $privateRsaKey = (new RsaKeyGenerator())->generatePrivateKey(new RsaKeyOption()); @@ -51,7 +51,7 @@ public function test signData returns a signature() /** * @requires PHP 7.1 */ - public function test signData returns a signature for ec keys() + public function test_signData_returns_a_signature_for_ec_keys() { $this->assertEquals(64, \strlen($this->service->signData('foo', (new EcKeyGenerator())->generatePrivateKey(new EcKeyOption('prime256v1')), OPENSSL_ALGO_SHA256, DataSigner::FORMAT_ECDSA))); $this->assertEquals(96, \strlen($this->service->signData('foo', (new EcKeyGenerator())->generatePrivateKey(new EcKeyOption('secp384r1')), OPENSSL_ALGO_SHA384, DataSigner::FORMAT_ECDSA))); From c061e3a8ccbd1a5f3853151f043afe6ea57ed5e6 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 17 Oct 2020 17:42:52 +0200 Subject: [PATCH 018/121] Migrate acmephp/core to use typehints --- src/Core/AcmeClient.php | 173 ++++++------- src/Core/AcmeClientInterface.php | 16 +- src/Core/Challenge/ChainValidator.php | 8 +- src/Core/Challenge/Dns/DnsDataExtractor.php | 17 +- .../Challenge/Dns/DnsResolverInterface.php | 14 +- src/Core/Challenge/Dns/DnsValidator.php | 18 +- src/Core/Challenge/Dns/GandiSolver.php | 24 +- src/Core/Challenge/Dns/LibDnsResolver.php | 13 +- src/Core/Challenge/Dns/Route53Solver.php | 17 +- src/Core/Challenge/Dns/SimpleDnsResolver.php | 4 +- src/Core/Challenge/Dns/SimpleDnsSolver.php | 6 +- src/Core/Challenge/Http/FilesystemSolver.php | 10 +- src/Core/Challenge/Http/HttpDataExtractor.php | 12 +- src/Core/Challenge/Http/HttpValidator.php | 8 +- src/Core/Challenge/Http/MockHttpValidator.php | 4 +- .../Challenge/Http/MockServerHttpSolver.php | 2 +- src/Core/Challenge/Http/SimpleHttpSolver.php | 10 +- src/Core/Challenge/SolverInterface.php | 2 +- src/Core/Challenge/ValidatorInterface.php | 4 +- src/Core/Challenge/WaitingValidator.php | 17 +- .../CertificateRequestFailedException.php | 2 +- .../CertificateRequestTimedOutException.php | 2 +- .../Server/BadCsrServerException.php | 2 +- .../Server/BadNonceServerException.php | 2 +- .../Exception/Server/CaaServerException.php | 2 +- .../Server/ConnectionServerException.php | 2 +- .../Exception/Server/DnsServerException.php | 2 +- .../IncorrectResponseServerException.php | 2 +- .../Server/InternalServerException.php | 2 +- .../Server/InvalidContactServerException.php | 2 +- .../Server/InvalidEmailServerException.php | 2 +- .../Server/MalformedServerException.php | 2 +- .../Server/OrderNotReadyServerException.php | 2 +- .../Server/RateLimitedServerException.php | 2 +- .../RejectedIdentifierServerException.php | 2 +- .../Exception/Server/TlsServerException.php | 2 +- .../Server/UnauthorizedServerException.php | 2 +- .../Server/UnknownHostServerException.php | 2 +- .../UnsupportedContactServerException.php | 2 +- .../UnsupportedIdentifierServerException.php | 2 +- .../UserActionRequiredServerException.php | 2 +- .../Filesystem/Adapter/FlysystemAdapter.php | 14 +- .../Adapter/FlysystemFtpFactory.php | 3 +- .../Adapter/FlysystemLocalFactory.php | 3 +- .../Adapter/FlysystemSftpFactory.php | 3 +- .../Filesystem/FilesystemFactoryInterface.php | 4 +- src/Core/Filesystem/FilesystemInterface.php | 13 +- src/Core/Http/Base64SafeEncoder.php | 14 +- src/Core/Http/SecureHttpClient.php | 244 ++++++++---------- src/Core/Http/SecureHttpClientFactory.php | 4 +- src/Core/Http/ServerErrorHandler.php | 14 +- src/Core/Protocol/AuthorizationChallenge.php | 93 ++----- src/Core/Protocol/CertificateOrder.php | 46 +--- src/Core/Protocol/ResourcesDirectory.php | 24 +- src/Core/Protocol/RevocationReason.php | 53 +--- src/Core/Util/JsonDecoder.php | 2 +- src/Ssl/Certificate.php | 2 +- src/Ssl/ParsedCertificate.php | 2 - 58 files changed, 346 insertions(+), 613 deletions(-) diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 75e84fda..a57f5163 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -66,10 +66,7 @@ class AcmeClient implements AcmeClientInterface */ private $account; - /** - * @param string $directoryUrl - */ - public function __construct(SecureHttpClient $httpClient, $directoryUrl, CertificateRequestSigner $csrSigner = null) + public function __construct(SecureHttpClient $httpClient, string $directoryUrl, CertificateRequestSigner $csrSigner = null) { $this->uninitializedHttpClient = $httpClient; $this->directoryUrl = $directoryUrl; @@ -79,21 +76,7 @@ public function __construct(SecureHttpClient $httpClient, $directoryUrl, Certifi /** * {@inheritdoc} */ - public function getHttpClient() - { - if (!$this->initializedHttpClient) { - $this->initializedHttpClient = $this->uninitializedHttpClient; - - $this->initializedHttpClient->setNonceEndpoint($this->getResourceUrl(ResourcesDirectory::NEW_NONCE)); - } - - return $this->initializedHttpClient; - } - - /** - * {@inheritdoc} - */ - public function registerAccount($agreement = null, $email = null) + public function registerAccount(string $agreement = null, string $email = null): array { Assert::nullOrString($agreement, 'registerAccount::$agreement expected a string or null. Got: %s'); Assert::nullOrString($email, 'registerAccount::$email expected a string or null. Got: %s'); @@ -117,27 +100,13 @@ public function registerAccount($agreement = null, $email = null) /** * {@inheritdoc} */ - public function requestAuthorization($domain) - { - $order = $this->requestOrder([$domain]); - - try { - return $order->getAuthorizationChallenges($domain); - } catch (AcmeCoreClientException $e) { - throw new ChallengeNotSupportedException(); - } - } - - /** - * {@inheritdoc} - */ - public function requestOrder(array $domains) + public function requestOrder(array $domains): CertificateOrder { Assert::allStringNotEmpty($domains, 'requestOrder::$domains expected a list of strings. Got: %s'); $payload = [ 'identifiers' => array_map( - function ($domain) { + static function ($domain) { return [ 'type' => 'dns', 'value' => $domain, @@ -154,6 +123,7 @@ function ($domain) { throw new ChallengeNotSupportedException(); } + $authorizationsChallenges = []; $orderEndpoint = $client->getLastLocation(); foreach ($response['authorizations'] as $authorizationEndpoint) { $authorizationsResponse = $client->request('POST', $authorizationEndpoint, $client->signKidPayload($authorizationEndpoint, $this->getResourceAccount(), null)); @@ -169,7 +139,61 @@ function ($domain) { /** * {@inheritdoc} */ - public function reloadAuthorization(AuthorizationChallenge $challenge) + public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, int $timeout = 180): CertificateResponse + { + Assert::integer($timeout, 'finalizeOrder::$timeout expected an integer. Got: %s'); + + $endTime = time() + $timeout; + $client = $this->getHttpClient(); + $orderEndpoint = $order->getOrderEndpoint(); + $response = $client->request('POST', $orderEndpoint, $client->signKidPayload($orderEndpoint, $this->getResourceAccount(), null)); + if (\in_array($response['status'], ['pending', 'processing', 'ready'])) { + $humanText = ['-----BEGIN CERTIFICATE REQUEST-----', '-----END CERTIFICATE REQUEST-----']; + + $csrContent = $this->csrSigner->signCertificateRequest($csr); + $csrContent = trim(str_replace($humanText, '', $csrContent)); + $csrContent = trim($client->getBase64Encoder()->encode(base64_decode($csrContent))); + + $response = $client->request('POST', $response['finalize'], $client->signKidPayload($response['finalize'], $this->getResourceAccount(), ['csr' => $csrContent])); + } + + // Waiting loop + while (time() <= $endTime && (!isset($response['status']) || \in_array($response['status'], ['pending', 'processing', 'ready']))) { + sleep(1); + $response = $client->request('POST', $orderEndpoint, $client->signKidPayload($orderEndpoint, $this->getResourceAccount(), null)); + } + + if ('valid' !== $response['status']) { + throw new CertificateRequestFailedException('The order has not been validated'); + } + + $response = $client->request('POST', $response['certificate'], $client->signKidPayload($response['certificate'], $this->getResourceAccount(), null), false); + $certificatesChain = null; + foreach (array_reverse(explode("\n\n", $response)) as $pem) { + $certificatesChain = new Certificate($pem, $certificatesChain); + } + + return new CertificateResponse($csr, $certificatesChain); + } + + /** + * {@inheritdoc} + */ + public function requestAuthorization(string $domain): array + { + $order = $this->requestOrder([$domain]); + + try { + return $order->getAuthorizationChallenges($domain); + } catch (AcmeCoreClientException $e) { + throw new ChallengeNotSupportedException(); + } + } + + /** + * {@inheritdoc} + */ + public function reloadAuthorization(AuthorizationChallenge $challenge): AuthorizationChallenge { $client = $this->getHttpClient(); $challengeUrl = $challenge->getUrl(); @@ -181,7 +205,7 @@ public function reloadAuthorization(AuthorizationChallenge $challenge) /** * {@inheritdoc} */ - public function challengeAuthorization(AuthorizationChallenge $challenge, $timeout = 180) + public function challengeAuthorization(AuthorizationChallenge $challenge, int $timeout = 180): array { Assert::integer($timeout, 'challengeAuthorization::$timeout expected an integer. Got: %s'); @@ -212,7 +236,7 @@ public function challengeAuthorization(AuthorizationChallenge $challenge, $timeo /** * {@inheritdoc} */ - public function requestCertificate($domain, CertificateRequest $csr, $timeout = 180) + public function requestCertificate(string $domain, CertificateRequest $csr, int $timeout = 180): CertificateResponse { Assert::stringNotEmpty($domain, 'requestCertificate::$domain expected a non-empty string. Got: %s'); Assert::integer($timeout, 'requestCertificate::$timeout expected an integer. Got: %s'); @@ -222,46 +246,6 @@ public function requestCertificate($domain, CertificateRequest $csr, $timeout = return $this->finalizeOrder($order, $csr, $timeout); } - /** - * {@inheritdoc} - */ - public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, $timeout = 180) - { - Assert::integer($timeout, 'finalizeOrder::$timeout expected an integer. Got: %s'); - - $endTime = time() + $timeout; - $client = $this->getHttpClient(); - $orderEndpoint = $order->getOrderEndpoint(); - $response = $client->request('POST', $orderEndpoint, $client->signKidPayload($orderEndpoint, $this->getResourceAccount(), null)); - if (\in_array($response['status'], ['pending', 'processing', 'ready'])) { - $humanText = ['-----BEGIN CERTIFICATE REQUEST-----', '-----END CERTIFICATE REQUEST-----']; - - $csrContent = $this->csrSigner->signCertificateRequest($csr); - $csrContent = trim(str_replace($humanText, '', $csrContent)); - $csrContent = trim($client->getBase64Encoder()->encode(base64_decode($csrContent))); - - $response = $client->request('POST', $response['finalize'], $client->signKidPayload($response['finalize'], $this->getResourceAccount(), ['csr' => $csrContent])); - } - - // Waiting loop - while (time() <= $endTime && (!isset($response['status']) || \in_array($response['status'], ['pending', 'processing', 'ready']))) { - sleep(1); - $response = $client->request('POST', $orderEndpoint, $client->signKidPayload($orderEndpoint, $this->getResourceAccount(), null)); - } - - if ('valid' !== $response['status']) { - throw new CertificateRequestFailedException('The order has not been validated'); - } - - $response = $client->request('POST', $response['certificate'], $client->signKidPayload($response['certificate'], $this->getResourceAccount(), null), false); - $certificatesChain = null; - foreach (array_reverse(explode("\n\n", $response)) as $pem) { - $certificatesChain = new Certificate($pem, $certificatesChain); - } - - return new CertificateResponse($csr, $certificatesChain); - } - /** * {@inheritdoc} */ @@ -297,13 +281,9 @@ public function revokeCertificate(Certificate $certificate, RevocationReason $re } /** - * Find a resource URL. - * - * @param string $resource - * - * @return string + * Find a resource URL from the Certificate Authority. */ - public function getResourceUrl($resource) + public function getResourceUrl(string $resource): string { if (!$this->directory) { $this->directory = new ResourcesDirectory( @@ -317,16 +297,12 @@ public function getResourceUrl($resource) /** * Request a resource (URL is found using ACME server directory). * - * @param string $method - * @param string $resource - * @param bool $returnJson - * * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * @throws AcmeCoreClientException when an error occured during response parsing * * @return array|string */ - protected function requestResource($method, $resource, array $payload, $returnJson = true) + protected function requestResource(string $method, string $resource, array $payload, bool $returnJson = true) { $client = $this->getHttpClient(); $endpoint = $this->getResourceUrl($resource); @@ -339,12 +315,7 @@ protected function requestResource($method, $resource, array $payload, $returnJs ); } - /** - * Retrieve the resource account. - * - * @return string - */ - private function getResourceAccount() + private function getResourceAccount(): string { if (!$this->account) { $payload = [ @@ -358,7 +329,7 @@ private function getResourceAccount() return $this->account; } - private function createAuthorizationChallenge($domain, array $response) + private function createAuthorizationChallenge($domain, array $response): AuthorizationChallenge { $base64encoder = $this->getHttpClient()->getBase64Encoder(); @@ -371,4 +342,14 @@ private function createAuthorizationChallenge($domain, array $response) $response['token'].'.'.$base64encoder->encode($this->getHttpClient()->getJWKThumbprint()) ); } + + private function getHttpClient(): SecureHttpClient + { + if (!$this->initializedHttpClient) { + $this->initializedHttpClient = $this->uninitializedHttpClient; + $this->initializedHttpClient->setNonceEndpoint($this->getResourceUrl(ResourcesDirectory::NEW_NONCE)); + } + + return $this->initializedHttpClient; + } } diff --git a/src/Core/AcmeClientInterface.php b/src/Core/AcmeClientInterface.php index 554c8484..23b21535 100644 --- a/src/Core/AcmeClientInterface.php +++ b/src/Core/AcmeClientInterface.php @@ -45,7 +45,7 @@ interface AcmeClientInterface * * @return array the Certificate Authority response decoded from JSON into an array */ - public function registerAccount($agreement = null, $email = null); + public function registerAccount(string $agreement = null, string $email = null): array; /** * Request authorization challenge data for a list of domains. @@ -63,7 +63,7 @@ public function registerAccount($agreement = null, $email = null); * * @return CertificateOrder the Order returned by the Certificate Authority */ - public function requestOrder(array $domains); + public function requestOrder(array $domains): CertificateOrder; /** * Request a certificate for the given domain. @@ -87,7 +87,7 @@ public function requestOrder(array $domains); * * @return CertificateResponse the certificate data to save it somewhere you want */ - public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, $timeout = 180); + public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, int $timeout = 180): CertificateResponse; /** * Request authorization challenge data for a given domain. @@ -105,7 +105,7 @@ public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, * * @return AuthorizationChallenge[] the list of challenges data returned by the Certificate Authority */ - public function requestAuthorization($domain); + public function requestAuthorization(string $domain): array; /** * Request the current status of an authorization challenge. @@ -114,7 +114,7 @@ public function requestAuthorization($domain); * * @return AuthorizationChallenge A new instance of the challenge */ - public function reloadAuthorization(AuthorizationChallenge $challenge); + public function reloadAuthorization(AuthorizationChallenge $challenge): AuthorizationChallenge; /** * Ask the Certificate Authority to challenge a given authorization. @@ -138,7 +138,7 @@ public function reloadAuthorization(AuthorizationChallenge $challenge); * * @return array the validate challenge response */ - public function challengeAuthorization(AuthorizationChallenge $challenge, $timeout = 180); + public function challengeAuthorization(AuthorizationChallenge $challenge, int $timeout = 180): array; /** * Request a certificate for the given domain. @@ -162,9 +162,11 @@ public function challengeAuthorization(AuthorizationChallenge $challenge, $timeo * * @return CertificateResponse the certificate data to save it somewhere you want */ - public function requestCertificate($domain, CertificateRequest $csr, $timeout = 180); + public function requestCertificate(string $domain, CertificateRequest $csr, int $timeout = 180): CertificateResponse; /** + * Revoke a given certificate from the Certificate Authority. + * * @throws CertificateRevocationException */ public function revokeCertificate(Certificate $certificate, RevocationReason $revocationReason = null); diff --git a/src/Core/Challenge/ChainValidator.php b/src/Core/Challenge/ChainValidator.php index cd05ba1d..30552372 100644 --- a/src/Core/Challenge/ChainValidator.php +++ b/src/Core/Challenge/ChainValidator.php @@ -21,9 +21,7 @@ */ class ChainValidator implements ValidatorInterface { - /** - * @var ValidatorInterface[] - */ + /** @var ValidatorInterface[] */ private $validators; /** @@ -37,7 +35,7 @@ public function __construct(array $validators) /** * {@inheritdoc} */ - public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) + public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { foreach ($this->validators as $validator) { if ($validator->supports($authorizationChallenge, $solver)) { @@ -51,7 +49,7 @@ public function supports(AuthorizationChallenge $authorizationChallenge, SolverI /** * {@inheritdoc} */ - public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) + public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { foreach ($this->validators as $validator) { if ($validator->supports($authorizationChallenge, $solver)) { diff --git a/src/Core/Challenge/Dns/DnsDataExtractor.php b/src/Core/Challenge/Dns/DnsDataExtractor.php index 00a7211f..b64ea733 100644 --- a/src/Core/Challenge/Dns/DnsDataExtractor.php +++ b/src/Core/Challenge/Dns/DnsDataExtractor.php @@ -21,35 +21,26 @@ */ class DnsDataExtractor { - /** - * @var Base64SafeEncoder - */ + /** @var Base64SafeEncoder */ private $encoder; - /** - * @param Base64SafeEncoder $encoder - */ public function __construct(Base64SafeEncoder $encoder = null) { - $this->encoder = null === $encoder ? new Base64SafeEncoder() : $encoder; + $this->encoder = $encoder ?: new Base64SafeEncoder(); } /** * Retrieves the name of the TXT record to register. - * - * @return string */ - public function getRecordName(AuthorizationChallenge $authorizationChallenge) + public function getRecordName(AuthorizationChallenge $authorizationChallenge): string { return sprintf('_acme-challenge.%s.', $authorizationChallenge->getDomain()); } /** * Retrieves the value of the TXT record to register. - * - * @return string */ - public function getRecordValue(AuthorizationChallenge $authorizationChallenge) + public function getRecordValue(AuthorizationChallenge $authorizationChallenge): string { return $this->encoder->encode(hash('sha256', $authorizationChallenge->getPayload(), true)); } diff --git a/src/Core/Challenge/Dns/DnsResolverInterface.php b/src/Core/Challenge/Dns/DnsResolverInterface.php index b44bf94e..908e17f4 100644 --- a/src/Core/Challenge/Dns/DnsResolverInterface.php +++ b/src/Core/Challenge/Dns/DnsResolverInterface.php @@ -19,18 +19,12 @@ interface DnsResolverInterface { /** - * Retrieves the list of TXT entries for the given domain. - * - * @param string $domain - * - * @return array + * Return whether or not the Resolver is supported. */ - public function getTxtEntries($domain); + public static function isSupported(): bool; /** - * Return whether or not the Resolver is supported. - * - * @return bool + * Retrieves the list of TXT entries for the given domain. */ - public static function isSupported(); + public function getTxtEntries(string $domain): array; } diff --git a/src/Core/Challenge/Dns/DnsValidator.php b/src/Core/Challenge/Dns/DnsValidator.php index edf086fc..998da9d9 100644 --- a/src/Core/Challenge/Dns/DnsValidator.php +++ b/src/Core/Challenge/Dns/DnsValidator.php @@ -33,20 +33,20 @@ class DnsValidator implements ValidatorInterface */ private $dnsResolver; - /** - * @param DnsDataExtractor $extractor - * @param DnsResolverInterface $dnsResolver - */ public function __construct(DnsDataExtractor $extractor = null, DnsResolverInterface $dnsResolver = null) { - $this->extractor = null === $extractor ? new DnsDataExtractor() : $extractor; - $this->dnsResolver = null === $dnsResolver ? (LibDnsResolver::isSupported() ? new LibDnsResolver() : new SimpleDnsResolver()) : $dnsResolver; + $this->extractor = $extractor ?: new DnsDataExtractor(); + + $this->dnsResolver = $dnsResolver; + if (!$this->dnsResolver) { + $this->dnsResolver = LibDnsResolver::isSupported() ? new LibDnsResolver() : new SimpleDnsResolver(); + } } /** * {@inheritdoc} */ - public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) + public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { return 'dns-01' === $authorizationChallenge->getType(); } @@ -54,13 +54,13 @@ public function supports(AuthorizationChallenge $authorizationChallenge, SolverI /** * {@inheritdoc} */ - public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) + public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { $recordName = $this->extractor->getRecordName($authorizationChallenge); $recordValue = $this->extractor->getRecordValue($authorizationChallenge); try { - return \in_array($recordValue, $this->dnsResolver->getTxtEntries($recordName)); + return \in_array($recordValue, $this->dnsResolver->getTxtEntries($recordName), false); } catch (AcmeDnsResolutionException $e) { return false; } diff --git a/src/Core/Challenge/Dns/GandiSolver.php b/src/Core/Challenge/Dns/GandiSolver.php index 28ec3bc2..a4ad28b5 100644 --- a/src/Core/Challenge/Dns/GandiSolver.php +++ b/src/Core/Challenge/Dns/GandiSolver.php @@ -28,6 +28,7 @@ class GandiSolver implements MultipleChallengesSolverInterface, ConfigurableServiceInterface { use LoggerAwareTrait; + /** * @var DnsDataExtractor */ @@ -48,16 +49,10 @@ class GandiSolver implements MultipleChallengesSolverInterface, ConfigurableServ */ private $apiKey; - /** - * @param DnsDataExtractor $extractor - * @param ClientInterface $client - */ - public function __construct( - DnsDataExtractor $extractor = null, - ClientInterface $client = null - ) { - $this->extractor = null === $extractor ? new DnsDataExtractor() : $extractor; - $this->client = null === $client ? new Client() : $client; + public function __construct(DnsDataExtractor $extractor = null, ClientInterface $client = null) + { + $this->extractor = $extractor ?: new DnsDataExtractor(); + $this->client = $client ?: new Client(); $this->logger = new NullLogger(); } @@ -72,7 +67,7 @@ public function configure(array $config) /** * {@inheritdoc} */ - public function supports(AuthorizationChallenge $authorizationChallenge) + public function supports(AuthorizationChallenge $authorizationChallenge): bool { return 'dns-01' === $authorizationChallenge->getType(); } @@ -150,12 +145,7 @@ public function cleanupAll(array $authorizationChallenges) } } - /** - * @param string $domain - * - * @return string - */ - protected function getTopLevelDomain($domain) + protected function getTopLevelDomain(string $domain): string { return \implode('.', \array_slice(\explode('.', $domain), -2)); } diff --git a/src/Core/Challenge/Dns/LibDnsResolver.php b/src/Core/Challenge/Dns/LibDnsResolver.php index e3d8438b..5cce509b 100644 --- a/src/Core/Challenge/Dns/LibDnsResolver.php +++ b/src/Core/Challenge/Dns/LibDnsResolver.php @@ -64,19 +64,18 @@ public function __construct( Decoder $decoder = null, $nameServer = '8.8.8.8' ) { - $this->questionFactory = null === $questionFactory ? new QuestionFactory() : $questionFactory; - $this->messageFactory = null === $messageFactory ? new MessageFactory() : $messageFactory; - $this->encoder = null === $encoder ? (new EncoderFactory())->create() : $encoder; - $this->decoder = null === $decoder ? (new DecoderFactory())->create() : $decoder; + $this->questionFactory = $questionFactory ?: new QuestionFactory(); + $this->messageFactory = $messageFactory ?: new MessageFactory(); + $this->encoder = $encoder ?: (new EncoderFactory())->create(); + $this->decoder = $decoder ?: (new DecoderFactory())->create(); $this->nameServer = $nameServer; - $this->logger = new NullLogger(); } /** * @{@inheritdoc} */ - public static function isSupported() + public static function isSupported(): bool { return class_exists(ResourceTypes::class); } @@ -84,7 +83,7 @@ public static function isSupported() /** * @{@inheritdoc} */ - public function getTxtEntries($domain) + public function getTxtEntries($domain): array { $domain = rtrim($domain, '.'); $nameServers = $this->getNameServers($domain); diff --git a/src/Core/Challenge/Dns/Route53Solver.php b/src/Core/Challenge/Dns/Route53Solver.php index 9486715d..6f28f8b2 100644 --- a/src/Core/Challenge/Dns/Route53Solver.php +++ b/src/Core/Challenge/Dns/Route53Solver.php @@ -27,6 +27,7 @@ class Route53Solver implements MultipleChallengesSolverInterface { use LoggerAwareTrait; + /** * @var DnsDataExtractor */ @@ -42,23 +43,17 @@ class Route53Solver implements MultipleChallengesSolverInterface */ private $cacheZones; - /** - * @param DnsDataExtractor $extractor - * @param Route53Client $client - */ - public function __construct( - DnsDataExtractor $extractor = null, - Route53Client $client = null - ) { - $this->extractor = null === $extractor ? new DnsDataExtractor() : $extractor; - $this->client = null === $client ? new Route53Client([]) : $client; + public function __construct(DnsDataExtractor $extractor = null, Route53Client $client = null) + { + $this->extractor = $extractor ?: new DnsDataExtractor(); + $this->client = $client ?: new Route53Client([]); $this->logger = new NullLogger(); } /** * {@inheritdoc} */ - public function supports(AuthorizationChallenge $authorizationChallenge) + public function supports(AuthorizationChallenge $authorizationChallenge): bool { return 'dns-01' === $authorizationChallenge->getType(); } diff --git a/src/Core/Challenge/Dns/SimpleDnsResolver.php b/src/Core/Challenge/Dns/SimpleDnsResolver.php index f7161214..cee454f7 100644 --- a/src/Core/Challenge/Dns/SimpleDnsResolver.php +++ b/src/Core/Challenge/Dns/SimpleDnsResolver.php @@ -21,7 +21,7 @@ class SimpleDnsResolver implements DnsResolverInterface /** * @{@inheritdoc} */ - public static function isSupported() + public static function isSupported(): bool { return \function_exists('dns_get_record'); } @@ -29,7 +29,7 @@ public static function isSupported() /** * @{@inheritdoc} */ - public function getTxtEntries($domain) + public function getTxtEntries($domain): array { $entries = []; foreach (dns_get_record($domain, DNS_TXT) as $record) { diff --git a/src/Core/Challenge/Dns/SimpleDnsSolver.php b/src/Core/Challenge/Dns/SimpleDnsSolver.php index 25e2b981..80535f09 100644 --- a/src/Core/Challenge/Dns/SimpleDnsSolver.php +++ b/src/Core/Challenge/Dns/SimpleDnsSolver.php @@ -39,14 +39,14 @@ class SimpleDnsSolver implements SolverInterface */ public function __construct(DnsDataExtractor $extractor = null, OutputInterface $output = null) { - $this->extractor = null === $extractor ? new DnsDataExtractor() : $extractor; - $this->output = null === $output ? new NullOutput() : $output; + $this->extractor = $extractor ?: new DnsDataExtractor(); + $this->output = $output ?: new NullOutput(); } /** * {@inheritdoc} */ - public function supports(AuthorizationChallenge $authorizationChallenge) + public function supports(AuthorizationChallenge $authorizationChallenge): bool { return 'dns-01' === $authorizationChallenge->getType(); } diff --git a/src/Core/Challenge/Http/FilesystemSolver.php b/src/Core/Challenge/Http/FilesystemSolver.php index 72652f46..8a4e879d 100644 --- a/src/Core/Challenge/Http/FilesystemSolver.php +++ b/src/Core/Challenge/Http/FilesystemSolver.php @@ -43,14 +43,10 @@ class FilesystemSolver implements SolverInterface, ConfigurableServiceInterface */ private $extractor; - /** - * @param ContainerInterface $filesystemFactoryLocator - * @param HttpDataExtractor $extractor - */ public function __construct(ContainerInterface $filesystemFactoryLocator = null, HttpDataExtractor $extractor = null) { - $this->filesystemFactoryLocator = $filesystemFactoryLocator = null ? new ServiceLocator([]) : $filesystemFactoryLocator; - $this->extractor = null === $extractor ? new HttpDataExtractor() : $extractor; + $this->filesystemFactoryLocator = $filesystemFactoryLocator ?: new ServiceLocator([]); + $this->extractor = $extractor ?: new HttpDataExtractor(); $this->filesystem = new NullAdapter(); } @@ -66,7 +62,7 @@ public function configure(array $config) /** * {@inheritdoc} */ - public function supports(AuthorizationChallenge $authorizationChallenge) + public function supports(AuthorizationChallenge $authorizationChallenge): bool { return 'http-01' === $authorizationChallenge->getType(); } diff --git a/src/Core/Challenge/Http/HttpDataExtractor.php b/src/Core/Challenge/Http/HttpDataExtractor.php index 21f97d3f..6e635505 100644 --- a/src/Core/Challenge/Http/HttpDataExtractor.php +++ b/src/Core/Challenge/Http/HttpDataExtractor.php @@ -22,10 +22,8 @@ class HttpDataExtractor { /** * Retrieves the absolute URL called by the CA. - * - * @return string */ - public function getCheckUrl(AuthorizationChallenge $authorizationChallenge) + public function getCheckUrl(AuthorizationChallenge $authorizationChallenge): string { return sprintf( 'http://%s%s', @@ -36,10 +34,8 @@ public function getCheckUrl(AuthorizationChallenge $authorizationChallenge) /** * Retrieves the absolute path called by the CA. - * - * @return string */ - public function getCheckPath(AuthorizationChallenge $authorizationChallenge) + public function getCheckPath(AuthorizationChallenge $authorizationChallenge): string { return sprintf( '/.well-known/acme-challenge/%s', @@ -49,10 +45,8 @@ public function getCheckPath(AuthorizationChallenge $authorizationChallenge) /** * Retrieves the content that should be returned in the response. - * - * @return string */ - public function getCheckContent(AuthorizationChallenge $authorizationChallenge) + public function getCheckContent(AuthorizationChallenge $authorizationChallenge): string { return $authorizationChallenge->getPayload(); } diff --git a/src/Core/Challenge/Http/HttpValidator.php b/src/Core/Challenge/Http/HttpValidator.php index a0be5ee6..643572a2 100644 --- a/src/Core/Challenge/Http/HttpValidator.php +++ b/src/Core/Challenge/Http/HttpValidator.php @@ -36,14 +36,14 @@ class HttpValidator implements ValidatorInterface public function __construct(HttpDataExtractor $extractor = null, Client $client = null) { - $this->extractor = null === $extractor ? new HttpDataExtractor() : $extractor; - $this->client = null === $client ? new Client() : $client; + $this->extractor = $extractor ?: new HttpDataExtractor(); + $this->client = $client ?: new Client(); } /** * {@inheritdoc} */ - public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) + public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { return 'http-01' === $authorizationChallenge->getType() && !$solver instanceof MockServerHttpSolver; } @@ -51,7 +51,7 @@ public function supports(AuthorizationChallenge $authorizationChallenge, SolverI /** * {@inheritdoc} */ - public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) + public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { $checkUrl = $this->extractor->getCheckUrl($authorizationChallenge); $checkContent = $this->extractor->getCheckContent($authorizationChallenge); diff --git a/src/Core/Challenge/Http/MockHttpValidator.php b/src/Core/Challenge/Http/MockHttpValidator.php index 68c1fb2d..d3d87ed6 100644 --- a/src/Core/Challenge/Http/MockHttpValidator.php +++ b/src/Core/Challenge/Http/MockHttpValidator.php @@ -25,7 +25,7 @@ class MockHttpValidator implements ValidatorInterface /** * {@inheritdoc} */ - public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) + public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { return 'http-01' === $authorizationChallenge->getType() && $solver instanceof MockServerHttpSolver; } @@ -33,7 +33,7 @@ public function supports(AuthorizationChallenge $authorizationChallenge, SolverI /** * {@inheritdoc} */ - public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) + public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { return true; } diff --git a/src/Core/Challenge/Http/MockServerHttpSolver.php b/src/Core/Challenge/Http/MockServerHttpSolver.php index 0dcb1e8d..72c73a2c 100644 --- a/src/Core/Challenge/Http/MockServerHttpSolver.php +++ b/src/Core/Challenge/Http/MockServerHttpSolver.php @@ -26,7 +26,7 @@ class MockServerHttpSolver implements SolverInterface /** * {@inheritdoc} */ - public function supports(AuthorizationChallenge $authorizationChallenge) + public function supports(AuthorizationChallenge $authorizationChallenge): bool { return 'http-01' === $authorizationChallenge->getType(); } diff --git a/src/Core/Challenge/Http/SimpleHttpSolver.php b/src/Core/Challenge/Http/SimpleHttpSolver.php index edc06058..7be53501 100644 --- a/src/Core/Challenge/Http/SimpleHttpSolver.php +++ b/src/Core/Challenge/Http/SimpleHttpSolver.php @@ -33,20 +33,16 @@ class SimpleHttpSolver implements SolverInterface */ private $output; - /** - * @param HttpDataExtractor $extractor - * @param OutputInterface $output - */ public function __construct(HttpDataExtractor $extractor = null, OutputInterface $output = null) { - $this->extractor = null === $extractor ? new HttpDataExtractor() : $extractor; - $this->output = null === $output ? new NullOutput() : $output; + $this->extractor = $extractor ?: new HttpDataExtractor(); + $this->output = $output ?: new NullOutput(); } /** * {@inheritdoc} */ - public function supports(AuthorizationChallenge $authorizationChallenge) + public function supports(AuthorizationChallenge $authorizationChallenge): bool { return 'http-01' === $authorizationChallenge->getType(); } diff --git a/src/Core/Challenge/SolverInterface.php b/src/Core/Challenge/SolverInterface.php index 3551a05d..59ef4204 100644 --- a/src/Core/Challenge/SolverInterface.php +++ b/src/Core/Challenge/SolverInterface.php @@ -25,7 +25,7 @@ interface SolverInterface * * @return bool The solver supports the given challenge's type */ - public function supports(AuthorizationChallenge $authorizationChallenge); + public function supports(AuthorizationChallenge $authorizationChallenge): bool; /** * Solve the given authorization challenge. diff --git a/src/Core/Challenge/ValidatorInterface.php b/src/Core/Challenge/ValidatorInterface.php index 894fe7ba..573e2d6a 100644 --- a/src/Core/Challenge/ValidatorInterface.php +++ b/src/Core/Challenge/ValidatorInterface.php @@ -25,12 +25,12 @@ interface ValidatorInterface * * @return bool The validator supports the given challenge's type */ - public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver); + public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool; /** * Internally validate the challenge by performing the same kind of test than the CA. * * @return bool The challenge is valid */ - public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver); + public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool; } diff --git a/src/Core/Challenge/WaitingValidator.php b/src/Core/Challenge/WaitingValidator.php index 6c2fe25d..b2802096 100644 --- a/src/Core/Challenge/WaitingValidator.php +++ b/src/Core/Challenge/WaitingValidator.php @@ -21,20 +21,13 @@ */ class WaitingValidator implements ValidatorInterface { - /** - * @var ValidatorInterface - */ + /** @var ValidatorInterface */ private $validator; - /** - * @var int - */ + /** @var int */ private $timeout; - /** - * @param int $timeout - */ - public function __construct(ValidatorInterface $validator, $timeout = 180) + public function __construct(ValidatorInterface $validator, int $timeout = 180) { $this->validator = $validator; $this->timeout = $timeout; @@ -43,7 +36,7 @@ public function __construct(ValidatorInterface $validator, $timeout = 180) /** * {@inheritdoc} */ - public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) + public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { return $this->validator->supports($authorizationChallenge, $solver); } @@ -51,7 +44,7 @@ public function supports(AuthorizationChallenge $authorizationChallenge, SolverI /** * {@inheritdoc} */ - public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver) + public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { $limitEndTime = time() + $this->timeout; diff --git a/src/Core/Exception/Protocol/CertificateRequestFailedException.php b/src/Core/Exception/Protocol/CertificateRequestFailedException.php index e6444f1f..36e12ca8 100644 --- a/src/Core/Exception/Protocol/CertificateRequestFailedException.php +++ b/src/Core/Exception/Protocol/CertificateRequestFailedException.php @@ -16,7 +16,7 @@ */ class CertificateRequestFailedException extends ProtocolException { - public function __construct($response) + public function __construct(string $response) { parent::__construct(sprintf('Certificate request failed (response: %s)', $response)); } diff --git a/src/Core/Exception/Protocol/CertificateRequestTimedOutException.php b/src/Core/Exception/Protocol/CertificateRequestTimedOutException.php index 7c320b69..ca44edef 100644 --- a/src/Core/Exception/Protocol/CertificateRequestTimedOutException.php +++ b/src/Core/Exception/Protocol/CertificateRequestTimedOutException.php @@ -16,7 +16,7 @@ */ class CertificateRequestTimedOutException extends ProtocolException { - public function __construct($response) + public function __construct(string $response) { parent::__construct(sprintf('Certificate request timed out (response: %s)', $response)); } diff --git a/src/Core/Exception/Server/BadCsrServerException.php b/src/Core/Exception/Server/BadCsrServerException.php index 2a211b8e..333e19ea 100644 --- a/src/Core/Exception/Server/BadCsrServerException.php +++ b/src/Core/Exception/Server/BadCsrServerException.php @@ -19,7 +19,7 @@ */ class BadCsrServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/BadNonceServerException.php b/src/Core/Exception/Server/BadNonceServerException.php index 61746c08..a731ce34 100644 --- a/src/Core/Exception/Server/BadNonceServerException.php +++ b/src/Core/Exception/Server/BadNonceServerException.php @@ -19,7 +19,7 @@ */ class BadNonceServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/CaaServerException.php b/src/Core/Exception/Server/CaaServerException.php index 3725b2a6..5def4239 100644 --- a/src/Core/Exception/Server/CaaServerException.php +++ b/src/Core/Exception/Server/CaaServerException.php @@ -19,7 +19,7 @@ */ class CaaServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/ConnectionServerException.php b/src/Core/Exception/Server/ConnectionServerException.php index 6fa70ef4..b53e0d9f 100644 --- a/src/Core/Exception/Server/ConnectionServerException.php +++ b/src/Core/Exception/Server/ConnectionServerException.php @@ -19,7 +19,7 @@ */ class ConnectionServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/DnsServerException.php b/src/Core/Exception/Server/DnsServerException.php index f739ca75..16d1b999 100644 --- a/src/Core/Exception/Server/DnsServerException.php +++ b/src/Core/Exception/Server/DnsServerException.php @@ -19,7 +19,7 @@ */ class DnsServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/IncorrectResponseServerException.php b/src/Core/Exception/Server/IncorrectResponseServerException.php index 046c51f1..e6d30612 100644 --- a/src/Core/Exception/Server/IncorrectResponseServerException.php +++ b/src/Core/Exception/Server/IncorrectResponseServerException.php @@ -19,7 +19,7 @@ */ class IncorrectResponseServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/InternalServerException.php b/src/Core/Exception/Server/InternalServerException.php index cf0e4923..299c3caa 100644 --- a/src/Core/Exception/Server/InternalServerException.php +++ b/src/Core/Exception/Server/InternalServerException.php @@ -19,7 +19,7 @@ */ class InternalServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/InvalidContactServerException.php b/src/Core/Exception/Server/InvalidContactServerException.php index c0095ae9..4a736be6 100644 --- a/src/Core/Exception/Server/InvalidContactServerException.php +++ b/src/Core/Exception/Server/InvalidContactServerException.php @@ -19,7 +19,7 @@ */ class InvalidContactServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/InvalidEmailServerException.php b/src/Core/Exception/Server/InvalidEmailServerException.php index b6a13525..f1875187 100644 --- a/src/Core/Exception/Server/InvalidEmailServerException.php +++ b/src/Core/Exception/Server/InvalidEmailServerException.php @@ -19,7 +19,7 @@ */ class InvalidEmailServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/MalformedServerException.php b/src/Core/Exception/Server/MalformedServerException.php index 198c6d87..d6a57a08 100644 --- a/src/Core/Exception/Server/MalformedServerException.php +++ b/src/Core/Exception/Server/MalformedServerException.php @@ -19,7 +19,7 @@ */ class MalformedServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/OrderNotReadyServerException.php b/src/Core/Exception/Server/OrderNotReadyServerException.php index 462cbf4e..6e6e88c4 100644 --- a/src/Core/Exception/Server/OrderNotReadyServerException.php +++ b/src/Core/Exception/Server/OrderNotReadyServerException.php @@ -16,7 +16,7 @@ class OrderNotReadyServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/RateLimitedServerException.php b/src/Core/Exception/Server/RateLimitedServerException.php index 5f2e7f62..4ff34410 100644 --- a/src/Core/Exception/Server/RateLimitedServerException.php +++ b/src/Core/Exception/Server/RateLimitedServerException.php @@ -19,7 +19,7 @@ */ class RateLimitedServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/RejectedIdentifierServerException.php b/src/Core/Exception/Server/RejectedIdentifierServerException.php index 43e1f988..7a0d53cd 100644 --- a/src/Core/Exception/Server/RejectedIdentifierServerException.php +++ b/src/Core/Exception/Server/RejectedIdentifierServerException.php @@ -19,7 +19,7 @@ */ class RejectedIdentifierServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/TlsServerException.php b/src/Core/Exception/Server/TlsServerException.php index 903fbf51..b7d04ed1 100644 --- a/src/Core/Exception/Server/TlsServerException.php +++ b/src/Core/Exception/Server/TlsServerException.php @@ -19,7 +19,7 @@ */ class TlsServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/UnauthorizedServerException.php b/src/Core/Exception/Server/UnauthorizedServerException.php index 8457f0e4..367d09f4 100644 --- a/src/Core/Exception/Server/UnauthorizedServerException.php +++ b/src/Core/Exception/Server/UnauthorizedServerException.php @@ -19,7 +19,7 @@ */ class UnauthorizedServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/UnknownHostServerException.php b/src/Core/Exception/Server/UnknownHostServerException.php index 6fdab76d..33628c0c 100644 --- a/src/Core/Exception/Server/UnknownHostServerException.php +++ b/src/Core/Exception/Server/UnknownHostServerException.php @@ -19,7 +19,7 @@ */ class UnknownHostServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/UnsupportedContactServerException.php b/src/Core/Exception/Server/UnsupportedContactServerException.php index 81d58a5a..44c10e7c 100644 --- a/src/Core/Exception/Server/UnsupportedContactServerException.php +++ b/src/Core/Exception/Server/UnsupportedContactServerException.php @@ -19,7 +19,7 @@ */ class UnsupportedContactServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/UnsupportedIdentifierServerException.php b/src/Core/Exception/Server/UnsupportedIdentifierServerException.php index d1ef9af7..cdd189cb 100644 --- a/src/Core/Exception/Server/UnsupportedIdentifierServerException.php +++ b/src/Core/Exception/Server/UnsupportedIdentifierServerException.php @@ -19,7 +19,7 @@ */ class UnsupportedIdentifierServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/UserActionRequiredServerException.php b/src/Core/Exception/Server/UserActionRequiredServerException.php index 7493f32a..b255970b 100644 --- a/src/Core/Exception/Server/UserActionRequiredServerException.php +++ b/src/Core/Exception/Server/UserActionRequiredServerException.php @@ -19,7 +19,7 @@ */ class UserActionRequiredServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Filesystem/Adapter/FlysystemAdapter.php b/src/Core/Filesystem/Adapter/FlysystemAdapter.php index a61cf0b0..ad5cb944 100644 --- a/src/Core/Filesystem/Adapter/FlysystemAdapter.php +++ b/src/Core/Filesystem/Adapter/FlysystemAdapter.php @@ -26,7 +26,7 @@ public function __construct(FlysystemFilesystemInterface $filesystem) $this->filesystem = $filesystem; } - public function write($path, $content) + public function write(string $path, string $content) { $isOnRemote = $this->filesystem->has($path); if ($isOnRemote && !$this->filesystem->update($path, $content)) { @@ -37,7 +37,7 @@ public function write($path, $content) } } - public function delete($path) + public function delete(string $path) { $isOnRemote = $this->filesystem->has($path); if ($isOnRemote && !$this->filesystem->delete($path)) { @@ -45,7 +45,7 @@ public function delete($path) } } - public function createDir($path) + public function createDir(string $path) { $isOnRemote = $this->filesystem->has($path); if (!$isOnRemote && !$this->filesystem->createDir($path)) { @@ -53,13 +53,7 @@ public function createDir($path) } } - /** - * @param string $path - * @param string $action - * - * @return \RuntimeException - */ - private function createRuntimeException($path, $action) + private function createRuntimeException(string $path, string $action): \RuntimeException { return new \RuntimeException( sprintf( diff --git a/src/Core/Filesystem/Adapter/FlysystemFtpFactory.php b/src/Core/Filesystem/Adapter/FlysystemFtpFactory.php index 58f4b9ee..7faf42c0 100644 --- a/src/Core/Filesystem/Adapter/FlysystemFtpFactory.php +++ b/src/Core/Filesystem/Adapter/FlysystemFtpFactory.php @@ -12,6 +12,7 @@ namespace AcmePhp\Core\Filesystem\Adapter; use AcmePhp\Core\Filesystem\FilesystemFactoryInterface; +use AcmePhp\Core\Filesystem\FilesystemInterface; use League\Flysystem\Adapter\Ftp; use League\Flysystem\Filesystem; @@ -20,7 +21,7 @@ class FlysystemFtpFactory implements FilesystemFactoryInterface /** * {@inheritdoc} */ - public function create(array $config) + public function create(array $config): FilesystemInterface { return new FlysystemAdapter(new Filesystem(new Ftp($config))); } diff --git a/src/Core/Filesystem/Adapter/FlysystemLocalFactory.php b/src/Core/Filesystem/Adapter/FlysystemLocalFactory.php index b19d9f45..a67e874b 100644 --- a/src/Core/Filesystem/Adapter/FlysystemLocalFactory.php +++ b/src/Core/Filesystem/Adapter/FlysystemLocalFactory.php @@ -12,6 +12,7 @@ namespace AcmePhp\Core\Filesystem\Adapter; use AcmePhp\Core\Filesystem\FilesystemFactoryInterface; +use AcmePhp\Core\Filesystem\FilesystemInterface; use League\Flysystem\Adapter\Local; use League\Flysystem\Filesystem; use Webmozart\Assert\Assert; @@ -21,7 +22,7 @@ class FlysystemLocalFactory implements FilesystemFactoryInterface /** * {@inheritdoc} */ - public function create(array $config) + public function create(array $config): FilesystemInterface { Assert::keyExists($config, 'root', 'create::$config expected an array with the key %s.'); diff --git a/src/Core/Filesystem/Adapter/FlysystemSftpFactory.php b/src/Core/Filesystem/Adapter/FlysystemSftpFactory.php index b2c1b2d3..fd6dd15f 100644 --- a/src/Core/Filesystem/Adapter/FlysystemSftpFactory.php +++ b/src/Core/Filesystem/Adapter/FlysystemSftpFactory.php @@ -12,6 +12,7 @@ namespace AcmePhp\Core\Filesystem\Adapter; use AcmePhp\Core\Filesystem\FilesystemFactoryInterface; +use AcmePhp\Core\Filesystem\FilesystemInterface; use League\Flysystem\Filesystem; use League\Flysystem\Sftp\SftpAdapter; @@ -20,7 +21,7 @@ class FlysystemSftpFactory implements FilesystemFactoryInterface /** * {@inheritdoc} */ - public function create(array $config) + public function create(array $config): FilesystemInterface { return new FlysystemAdapter(new Filesystem(new SftpAdapter($config))); } diff --git a/src/Core/Filesystem/FilesystemFactoryInterface.php b/src/Core/Filesystem/FilesystemFactoryInterface.php index 11377b6f..48b04d25 100644 --- a/src/Core/Filesystem/FilesystemFactoryInterface.php +++ b/src/Core/Filesystem/FilesystemFactoryInterface.php @@ -15,8 +15,6 @@ interface FilesystemFactoryInterface { /** * Create a new Filesystem. - * - * @return FilesystemInterface */ - public function create(array $config); + public function create(array $config): FilesystemInterface; } diff --git a/src/Core/Filesystem/FilesystemInterface.php b/src/Core/Filesystem/FilesystemInterface.php index c8843cd5..f7f6fae0 100644 --- a/src/Core/Filesystem/FilesystemInterface.php +++ b/src/Core/Filesystem/FilesystemInterface.php @@ -15,23 +15,16 @@ interface FilesystemInterface { /** * Write content to a file. - * - * @param string $path - * @param string $content */ - public function write($path, $content); + public function write(string $path, string $content); /** * Delete a file. - * - * @param string $path */ - public function delete($path); + public function delete(string $path); /** * Delete a directory. - * - * @param string $path */ - public function createDir($path); + public function createDir(string $path); } diff --git a/src/Core/Http/Base64SafeEncoder.php b/src/Core/Http/Base64SafeEncoder.php index 6370ff6f..f6d6796b 100644 --- a/src/Core/Http/Base64SafeEncoder.php +++ b/src/Core/Http/Base64SafeEncoder.php @@ -18,22 +18,12 @@ */ class Base64SafeEncoder { - /** - * @param string $input - * - * @return string - */ - public function encode($input) + public function encode(string $input): string { return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); } - /** - * @param string $input - * - * @return string - */ - public function decode($input) + public function decode(string $input): string { $remainder = \strlen($input) % 4; diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index 627ab05b..a178d54a 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -90,70 +90,19 @@ public function __construct( /** * Send a request encoded in the format defined by the ACME protocol. * - * @param string $method - * @param string $endpoint - * @param bool $returnJson - * * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * @throws AcmeCoreClientException when an error occured during response parsing * * @return array|string Array of parsed JSON if $returnJson = true, string otherwise */ - public function signedRequest($method, $endpoint, array $payload = [], $returnJson = true) + public function signedRequest(string $method, string $endpoint, array $payload = [], $returnJson = true) { @trigger_error('The method signedRequest is deprecated since version 1.1 and will be removed in 2.0. use methods request, signKidPayload instead', E_USER_DEPRECATED); return $this->request($method, $endpoint, $this->signJwkPayload($endpoint, $payload), $returnJson); } - private function getAlg() - { - $privateKey = $this->accountKeyPair->getPrivateKey(); - $parsedKey = $this->keyParser->parse($privateKey); - switch ($parsedKey->getType()) { - case OPENSSL_KEYTYPE_RSA: - return 'RS256'; - case OPENSSL_KEYTYPE_EC: - switch ($parsedKey->getBits()) { - case 256: - case 384: - return 'ES'.$parsedKey->getBits(); - case 521: - return 'ES512'; - } - // no break to let the default case - default: - throw new AcmeCoreClientException('Private key type is not supported'); - } - } - - private function extractSignOptionFromJWSAlg($alg) - { - if (!preg_match('/^([A-Z]+)(\d+)$/', $alg, $match)) { - throw new AcmeCoreClientException(sprintf('The given "%s" algorithm is not supported', $alg)); - } - - if (!\defined('OPENSSL_ALGO_SHA'.$match[2])) { - throw new AcmeCoreClientException(sprintf('The given "%s" algorithm is not supported', $alg)); - } - - $algorithm = \constant('OPENSSL_ALGO_SHA'.$match[2]); - - switch ($match[1]) { - case 'RS': - $format = DataSigner::FORMAT_DER; - break; - case 'ES': - $format = DataSigner::FORMAT_ECDSA; - break; - default: - throw new AcmeCoreClientException(sprintf('The given "%s" algorithm is not supported', $alg)); - } - - return [$algorithm, $format]; - } - - public function getJWK() + public function getJWK(): array { $privateKey = $this->accountKeyPair->getPrivateKey(); $parsedKey = $this->keyParser->parse($privateKey); @@ -166,6 +115,7 @@ public function getJWK() 'kty' => 'RSA', 'n' => $this->base64Encoder->encode($parsedKey->getDetail('n')), ]; + case OPENSSL_KEYTYPE_EC: return [ // this order matters @@ -174,26 +124,21 @@ public function getJWK() 'x' => $this->base64Encoder->encode($parsedKey->getDetail('x')), 'y' => $this->base64Encoder->encode($parsedKey->getDetail('y')), ]; + default: throw new AcmeCoreClientException('Private key type not supported'); } } - public function getJWKThumbprint() + public function getJWKThumbprint(): string { return hash('sha256', json_encode($this->getJWK()), true); } /** * Generates a payload signed with account's KID. - * - * @param string $endpoint - * @param string $account - * @param array $payload - * - * @return array the signed Pyaload */ - public function signKidPayload($endpoint, $account, array $payload = null) + public function signKidPayload(string $endpoint, string $account, array $payload = null): array { return $this->signPayload( [ @@ -227,54 +172,15 @@ public function signJwkPayload($endpoint, array $payload = null) ); } - /** - * Sign the given Payload. - * - * @return array - */ - private function signPayload(array $protected, array $payload = null) - { - if (!isset($protected['alg'])) { - throw new \InvalidArgumentException('The property "alg" is required in the protected array'); - } - $alg = $protected['alg']; - - $privateKey = $this->accountKeyPair->getPrivateKey(); - list($algorithm, $format) = $this->extractSignOptionFromJWSAlg($alg); - - $protected = $this->base64Encoder->encode(json_encode($protected, JSON_UNESCAPED_SLASHES)); - if (null === $payload) { - $payload = ''; - } elseif ([] === $payload) { - $payload = $this->base64Encoder->encode('{}'); - } else { - $payload = $this->base64Encoder->encode(json_encode($payload, JSON_UNESCAPED_SLASHES)); - } - $signature = $this->base64Encoder->encode( - $this->dataSigner->signData($protected.'.'.$payload, $privateKey, $algorithm, $format) - ); - - return [ - 'protected' => $protected, - 'payload' => $payload, - 'signature' => $signature, - ]; - } - /** * Send a request encoded in the format defined by the ACME protocol. * - * @param string $method - * @param string $endpoint - * @param string $account - * @param bool $returnJson - * * @throws AcmeCoreClientException when an error occured during response parsing * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * * @return array|string Array of parsed JSON if $returnJson = true, string otherwise */ - public function signedKidRequest($method, $endpoint, $account, array $payload = [], $returnJson = true) + public function signedKidRequest(string $method, string $endpoint, string $account, array $payload = [], bool $returnJson = true) { @trigger_error('The method signedKidRequest is deprecated since version 1.1 and will be removed in 2.0. use methods request, signKidPayload instead.', E_USER_DEPRECATED); @@ -284,18 +190,13 @@ public function signedKidRequest($method, $endpoint, $account, array $payload = /** * Send a request encoded in the format defined by the ACME protocol. * - * @param string $method - * @param string $endpoint - * @param array $data - * @param bool $returnJson - * * @throws AcmeCoreClientException when an error occured during response parsing * @throws ExpectedJsonException when $returnJson = true and the response is not valid JSON * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * * @return array|string Array of parsed JSON if $returnJson = true, string otherwise */ - public function unsignedRequest($method, $endpoint, array $data = null, $returnJson = true) + public function unsignedRequest(string $method, string $endpoint, array $data = null, bool $returnJson = true) { @trigger_error('The method unsignedRequest is deprecated since version 1.1 and will be removed in 2.0. use methods request instead.', E_USER_DEPRECATED); @@ -305,17 +206,13 @@ public function unsignedRequest($method, $endpoint, array $data = null, $returnJ /** * Send a request encoded in the format defined by the ACME protocol. * - * @param string $method - * @param string $endpoint - * @param bool $returnJson - * * @throws AcmeCoreClientException when an error occured during response parsing * @throws ExpectedJsonException when $returnJson = true and the response is not valid JSON * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * * @return array|string Array of parsed JSON if $returnJson = true, string otherwise */ - public function request($method, $endpoint, array $data = [], $returnJson = true) + public function request(string $method, string $endpoint, array $data = [], bool $returnJson = true) { $call = function () use ($method, $endpoint, $data) { $request = $this->createRequest($method, $endpoint, $data); @@ -358,68 +255,76 @@ public function setAccountKeyPair(KeyPair $keyPair) $this->accountKeyPair = $keyPair; } - /** - * @return int - */ - public function getLastCode() + public function getLastCode(): int { return $this->lastResponse->getStatusCode(); } - /** - * @return string - */ - public function getLastLocation() + public function getLastLocation(): string { return $this->lastResponse->getHeaderLine('Location'); } - /** - * @return array - */ - public function getLastLinks() + public function getLastLinks(): array { return \GuzzleHttp\Psr7\parse_header($this->lastResponse->getHeader('Link')); } - /** - * @return KeyPair - */ - public function getAccountKeyPair() + public function getAccountKeyPair(): KeyPair { return $this->accountKeyPair; } - /** - * @return KeyParser - */ - public function getKeyParser() + public function getKeyParser(): KeyParser { return $this->keyParser; } - /** - * @return DataSigner - */ - public function getDataSigner() + public function getDataSigner(): DataSigner { return $this->dataSigner; } - /** - * @param string $endpoint - */ - public function setNonceEndpoint($endpoint) + public function setNonceEndpoint(string $endpoint) { $this->nonceEndpoint = $endpoint; } + public function getBase64Encoder(): Base64SafeEncoder + { + return $this->base64Encoder; + } + /** - * @return Base64SafeEncoder + * Sign the given Payload. */ - public function getBase64Encoder() + private function signPayload(array $protected, array $payload = null): array { - return $this->base64Encoder; + if (!isset($protected['alg'])) { + throw new \InvalidArgumentException('The property "alg" is required in the protected array'); + } + $alg = $protected['alg']; + + $privateKey = $this->accountKeyPair->getPrivateKey(); + list($algorithm, $format) = $this->extractSignOptionFromJWSAlg($alg); + + $protected = $this->base64Encoder->encode(json_encode($protected, JSON_UNESCAPED_SLASHES)); + if (null === $payload) { + $payload = ''; + } elseif ([] === $payload) { + $payload = $this->base64Encoder->encode('{}'); + } else { + $payload = $this->base64Encoder->encode(json_encode($payload, JSON_UNESCAPED_SLASHES)); + } + $signature = $this->base64Encoder->encode( + $this->dataSigner->signData($protected.'.'.$payload, $privateKey, $algorithm, $format) + ); + + return [ + 'protected' => $protected, + 'payload' => $payload, + 'signature' => $signature, + ]; } private function createRequest($method, $endpoint, $data) @@ -454,9 +359,62 @@ private function getNonce() if (null !== $this->nonceEndpoint) { $this->request('HEAD', $this->nonceEndpoint, [], false); + if ($this->lastResponse->hasHeader('Replay-Nonce')) { return $this->lastResponse->getHeaderLine('Replay-Nonce'); } } } + + private function getAlg(): string + { + $privateKey = $this->accountKeyPair->getPrivateKey(); + $parsedKey = $this->keyParser->parse($privateKey); + + switch ($parsedKey->getType()) { + case OPENSSL_KEYTYPE_RSA: + return 'RS256'; + + case OPENSSL_KEYTYPE_EC: + switch ($parsedKey->getBits()) { + case 256: + case 384: + return 'ES'.$parsedKey->getBits(); + case 521: + return 'ES512'; + } + + // no break to let the default case + default: + throw new AcmeCoreClientException('Private key type is not supported'); + } + } + + private function extractSignOptionFromJWSAlg($alg): array + { + if (!preg_match('/^([A-Z]+)(\d+)$/', $alg, $match)) { + throw new AcmeCoreClientException(sprintf('The given "%s" algorithm is not supported', $alg)); + } + + if (!\defined('OPENSSL_ALGO_SHA'.$match[2])) { + throw new AcmeCoreClientException(sprintf('The given "%s" algorithm is not supported', $alg)); + } + + $algorithm = \constant('OPENSSL_ALGO_SHA'.$match[2]); + + switch ($match[1]) { + case 'RS': + $format = DataSigner::FORMAT_DER; + break; + + case 'ES': + $format = DataSigner::FORMAT_ECDSA; + break; + + default: + throw new AcmeCoreClientException(sprintf('The given "%s" algorithm is not supported', $alg)); + } + + return [$algorithm, $format]; + } } diff --git a/src/Core/Http/SecureHttpClientFactory.php b/src/Core/Http/SecureHttpClientFactory.php index e0b0cf0d..f80694ca 100644 --- a/src/Core/Http/SecureHttpClientFactory.php +++ b/src/Core/Http/SecureHttpClientFactory.php @@ -64,10 +64,8 @@ public function __construct( /** * Create a SecureHttpClient using a given account KeyPair. - * - * @return SecureHttpClient */ - public function createSecureHttpClient(KeyPair $accountKeyPair) + public function createSecureHttpClient(KeyPair $accountKeyPair): SecureHttpClient { return new SecureHttpClient( $accountKeyPair, diff --git a/src/Core/Http/ServerErrorHandler.php b/src/Core/Http/ServerErrorHandler.php index 84dc03ff..9b40c768 100644 --- a/src/Core/Http/ServerErrorHandler.php +++ b/src/Core/Http/ServerErrorHandler.php @@ -68,10 +68,8 @@ class ServerErrorHandler /** * Get a response summary (useful for exceptions). * Use Guzzle method if available (Guzzle 6.1.1+). - * - * @return string */ - public static function getResponseBodySummary(ResponseInterface $response) + public static function getResponseBodySummary(ResponseInterface $response): string { // Rewind the stream if possible to allow re-reading for the summary. if ($response->getBody()->isSeekable()) { @@ -91,14 +89,11 @@ public static function getResponseBodySummary(ResponseInterface $response) return $body; } - /** - * @return AcmeCoreServerException - */ public function createAcmeExceptionForResponse( RequestInterface $request, ResponseInterface $response, \Exception $previous = null - ) { + ): AcmeCoreServerException { $body = \GuzzleHttp\Psr7\copy_to_string($response->getBody()); try { @@ -128,14 +123,11 @@ public function createAcmeExceptionForResponse( ); } - /** - * @return AcmeCoreServerException - */ private function createDefaultExceptionForResponse( RequestInterface $request, ResponseInterface $response, \Exception $previous = null - ) { + ): AcmeCoreServerException { return new AcmeCoreServerException( $request, sprintf( diff --git a/src/Core/Protocol/AuthorizationChallenge.php b/src/Core/Protocol/AuthorizationChallenge.php index c9fa71d5..bfaee7b8 100644 --- a/src/Core/Protocol/AuthorizationChallenge.php +++ b/src/Core/Protocol/AuthorizationChallenge.php @@ -11,8 +11,6 @@ namespace AcmePhp\Core\Protocol; -use Webmozart\Assert\Assert; - /** * Represent a ACME challenge. * @@ -20,53 +18,26 @@ */ class AuthorizationChallenge { - /** - * @var string - */ + /** @var string */ private $domain; - /** - * @var string - */ + /** @var string */ private $status; - /** - * @var string - */ + /** @var string */ private $type; - /** - * @var string - */ + /** @var string */ private $url; - /** - * @var string - */ + /** @var string */ private $token; - /** - * @var string - */ + /** @var string */ private $payload; - /** - * @param string $domain - * @param string $status - * @param string $type - * @param string $url - * @param string $token - * @param string $payload - */ - public function __construct($domain, $status, $type, $url, $token, $payload) + public function __construct(string $domain, string $status, string $type, string $url, string $token, string $payload) { - Assert::stringNotEmpty($domain, 'Challenge::$domain expected a non-empty string. Got: %s'); - Assert::stringNotEmpty($status, 'Challenge::$status expected a non-empty string. Got: %s'); - Assert::stringNotEmpty($type, 'Challenge::$type expected a non-empty string. Got: %s'); - Assert::stringNotEmpty($url, 'Challenge::$url expected a non-empty string. Got: %s'); - Assert::stringNotEmpty($token, 'Challenge::$token expected a non-empty string. Got: %s'); - Assert::stringNotEmpty($payload, 'Challenge::$payload expected a non-empty string. Got: %s'); - $this->domain = $domain; $this->status = $status; $this->type = $type; @@ -75,10 +46,7 @@ public function __construct($domain, $status, $type, $url, $token, $payload) $this->payload = $payload; } - /** - * @return array - */ - public function toArray() + public function toArray(): array { return [ 'domain' => $this->getDomain(), @@ -90,10 +58,7 @@ public function toArray() ]; } - /** - * @return AuthorizationChallenge - */ - public static function fromArray(array $data) + public static function fromArray(array $data): self { return new self( $data['domain'], @@ -105,66 +70,42 @@ public static function fromArray(array $data) ); } - /** - * @return string - */ - public function getDomain() + public function getDomain(): string { return $this->domain; } - /** - * @return string - */ - public function getStatus() + public function getStatus(): string { return $this->status; } - /** - * @return bool - */ - public function isValid() + public function isValid(): bool { return 'valid' === $this->status; } - /** - * @return bool - */ - public function isPending() + public function isPending(): bool { return 'pending' === $this->status || 'processing' === $this->status; } - /** - * @return string - */ - public function getType() + public function getType(): string { return $this->type; } - /** - * @return string - */ - public function getUrl() + public function getUrl(): string { return $this->url; } - /** - * @return string - */ - public function getToken() + public function getToken(): string { return $this->token; } - /** - * @return string - */ - public function getPayload() + public function getPayload(): string { return $this->payload; } diff --git a/src/Core/Protocol/CertificateOrder.php b/src/Core/Protocol/CertificateOrder.php index 342bef17..a18236c3 100644 --- a/src/Core/Protocol/CertificateOrder.php +++ b/src/Core/Protocol/CertificateOrder.php @@ -12,7 +12,6 @@ namespace AcmePhp\Core\Protocol; use AcmePhp\Core\Exception\AcmeCoreClientException; -use Webmozart\Assert\Assert; /** * Represent an ACME order. @@ -21,29 +20,14 @@ */ class CertificateOrder { - /** - * @var AuthorizationChallenge[][] - */ + /** @var AuthorizationChallenge[][] */ private $authorizationsChallenges; - /** - * @var string - */ + /** @var string */ private $orderEndpoint; - /** - * @param string $domain - * @param string $type - * @param string $url - * @param string $token - * @param string $payload - * @param string $order - */ - public function __construct($authorizationsChallenges, $orderEndpoint = null) + public function __construct(array $authorizationsChallenges, string $orderEndpoint = null) { - Assert::isArray($authorizationsChallenges, 'Challenge::$authorizationsChallenges expected an array. Got: %s'); - Assert::nullOrString($orderEndpoint, 'Challenge::$orderEndpoint expected a string or null. Got: %s'); - foreach ($authorizationsChallenges as &$authorizationChallenges) { foreach ($authorizationChallenges as &$authorizationChallenge) { if (\is_array($authorizationChallenge)) { @@ -56,10 +40,7 @@ public function __construct($authorizationsChallenges, $orderEndpoint = null) $this->orderEndpoint = $orderEndpoint; } - /** - * @return array - */ - public function toArray() + public function toArray(): array { return [ 'authorizationsChallenges' => $this->getAuthorizationsChallenges(), @@ -67,15 +48,9 @@ public function toArray() ]; } - /** - * @return AuthorizationChallenge - */ - public static function fromArray(array $data) + public static function fromArray(array $data): self { - return new self( - $data['authorizationsChallenges'], - $data['orderEndpoint'] - ); + return new self($data['authorizationsChallenges'], $data['orderEndpoint']); } /** @@ -87,11 +62,9 @@ public function getAuthorizationsChallenges() } /** - * @param string $domain - * * @return AuthorizationChallenge[] */ - public function getAuthorizationChallenges($domain) + public function getAuthorizationChallenges(string $domain): array { if (!isset($this->authorizationsChallenges[$domain])) { throw new AcmeCoreClientException('The order does not contains any authorization challenge for the domain '.$domain); @@ -100,10 +73,7 @@ public function getAuthorizationChallenges($domain) return $this->authorizationsChallenges[$domain]; } - /** - * @return string - */ - public function getOrderEndpoint() + public function getOrderEndpoint(): string { return $this->orderEndpoint; } diff --git a/src/Core/Protocol/ResourcesDirectory.php b/src/Core/Protocol/ResourcesDirectory.php index 589ed6b5..b3c34d6c 100644 --- a/src/Core/Protocol/ResourcesDirectory.php +++ b/src/Core/Protocol/ResourcesDirectory.php @@ -20,14 +20,12 @@ */ class ResourcesDirectory { - const NEW_ACCOUNT = 'newAccount'; - const NEW_ORDER = 'newOrder'; - const NEW_NONCE = 'newNonce'; - const REVOKE_CERT = 'revokeCert'; + public const NEW_ACCOUNT = 'newAccount'; + public const NEW_ORDER = 'newOrder'; + public const NEW_NONCE = 'newNonce'; + public const REVOKE_CERT = 'revokeCert'; - /** - * @var array - */ + /** @var array */ private $serverResources; public function __construct(array $serverResources) @@ -38,7 +36,7 @@ public function __construct(array $serverResources) /** * @return string[] */ - public static function getResourcesNames() + public static function getResourcesNames(): array { return [ self::NEW_ACCOUNT, @@ -50,19 +48,15 @@ public static function getResourcesNames() /** * Find a resource URL. - * - * @param string $resource - * - * @return string */ - public function getResourceUrl($resource) + public function getResourceUrl(string $resource): string { Assert::oneOf( $resource, - self::getResourcesNames(), + array_keys($this->serverResources), 'Resource type "%s" is not supported by the ACME server (supported: %2$s)' ); - return isset($this->serverResources[$resource]) ? $this->serverResources[$resource] : null; + return $this->serverResources[$resource]; } } diff --git a/src/Core/Protocol/RevocationReason.php b/src/Core/Protocol/RevocationReason.php index e7995ed9..19ca9072 100644 --- a/src/Core/Protocol/RevocationReason.php +++ b/src/Core/Protocol/RevocationReason.php @@ -18,55 +18,36 @@ */ class RevocationReason { - const DEFAULT_REASON = self::REASON_UNSPECIFIED; - const REASON_UNSPECIFIED = 0; - const REASON_KEY_COMPROMISE = 1; - const REASON_AFFILLIATION_CHANGED = 3; - const REASON_SUPERCEDED = 4; - const REASON_CESSATION_OF_OPERATION = 5; + public const DEFAULT_REASON = self::REASON_UNSPECIFIED; + public const REASON_UNSPECIFIED = 0; + public const REASON_KEY_COMPROMISE = 1; + public const REASON_AFFILLIATION_CHANGED = 3; + public const REASON_SUPERCEDED = 4; + public const REASON_CESSATION_OF_OPERATION = 5; - /** - * @var int|null - */ - private $reasonType = null; + /** @var int|null */ + private $reasonType; - /** - * @param int $reasonType - * - * @throws \InvalidArgumentException - */ - public function __construct($reasonType) + public function __construct(int $reasonType) { - $reasonType = (int) $reasonType; - Assert::oneOf($reasonType, self::getReasons(), 'Revocation reason type "%s" is not supported by the ACME server (supported: %2$s)'); $this->reasonType = $reasonType; } - /** - * @return int - */ - public function getReasonType() + public function getReasonType(): int { return $this->reasonType; } - /** - * @return static - */ - public static function createDefaultReason() + public static function createDefaultReason(): self { return new static(self::DEFAULT_REASON); } - /** - * @return array - */ - public static function getFormattedReasons() + public static function getFormattedReasons(): array { $formatted = []; - foreach (self::getReasonLabelMap() as $reason => $label) { $formatted[] = $reason.' - '.$label; } @@ -74,10 +55,7 @@ public static function getFormattedReasons() return $formatted; } - /** - * @return array - */ - private static function getReasonLabelMap() + private static function getReasonLabelMap(): array { return [ self::REASON_UNSPECIFIED => 'unspecified', @@ -88,10 +66,7 @@ private static function getReasonLabelMap() ]; } - /** - * @return array - */ - public static function getReasons() + public static function getReasons(): array { return [ self::REASON_UNSPECIFIED, diff --git a/src/Core/Util/JsonDecoder.php b/src/Core/Util/JsonDecoder.php index 77c8bd9a..9b357ac6 100644 --- a/src/Core/Util/JsonDecoder.php +++ b/src/Core/Util/JsonDecoder.php @@ -36,7 +36,7 @@ class JsonDecoder * * @see http://www.php.net/manual/en/function.json-decode.php */ - public static function decode($json, $assoc = false, $depth = 512, $options = 0) + public static function decode(string $json, bool $assoc = false, int $depth = 512, int $options = 0) { $data = json_decode($json, $assoc, $depth, $options); diff --git a/src/Ssl/Certificate.php b/src/Ssl/Certificate.php index 5b279cdf..12465bd8 100644 --- a/src/Ssl/Certificate.php +++ b/src/Ssl/Certificate.php @@ -56,7 +56,7 @@ public function getPEM(): string return $this->certificatePEM; } - public function getIssuerCertificate(): ?Certificate + public function getIssuerCertificate(): ?self { return $this->issuerCertificate; } diff --git a/src/Ssl/ParsedCertificate.php b/src/Ssl/ParsedCertificate.php index 3facc645..f6adea4c 100644 --- a/src/Ssl/ParsedCertificate.php +++ b/src/Ssl/ParsedCertificate.php @@ -45,9 +45,7 @@ class ParsedCertificate private $subjectAlternativeNames; /** - * @param string $subject * @param string $issuer - * @param bool $selfSigned * @param \DateTime $validFrom * @param \DateTime $validTo * @param string $serialNumber From ab68cac006758710058608d95fa663f0b9e1786d Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 17 Oct 2020 17:47:50 +0200 Subject: [PATCH 019/121] Bump PHP-CS-Fixer version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3054ab55..acdc15c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,7 @@ jobs: - stage: coding-style php: 7.2 install: - - wget https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v2.16.1/php-cs-fixer.phar -q + - wget https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v2.16.4/php-cs-fixer.phar -q script: - php php-cs-fixer.phar fix --dry-run --diff From 7d6b566d043ea4de38d1b391fc31ca230ac3af33 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 17 Oct 2020 17:51:12 +0200 Subject: [PATCH 020/121] Fix tests --- .../Challenge/Http/FilesystemSolverTest.php | 35 +++++++++++-------- tests/setup.sh | 2 +- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/tests/Core/Challenge/Http/FilesystemSolverTest.php b/tests/Core/Challenge/Http/FilesystemSolverTest.php index 3c46c23c..3a66986a 100644 --- a/tests/Core/Challenge/Http/FilesystemSolverTest.php +++ b/tests/Core/Challenge/Http/FilesystemSolverTest.php @@ -14,8 +14,8 @@ use AcmePhp\Core\Challenge\Http\FilesystemSolver; use AcmePhp\Core\Challenge\Http\HttpDataExtractor; use AcmePhp\Core\Filesystem\FilesystemFactoryInterface; +use AcmePhp\Core\Filesystem\FilesystemInterface; use AcmePhp\Core\Protocol\AuthorizationChallenge; -use League\Flysystem\FilesystemInterface; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Psr\Container\ContainerInterface; @@ -43,21 +43,23 @@ public function testSolve() $checkPath = '/.challenge'; $checkContent = 'randomPayload'; - $mockExtractor = $this->prophesize(HttpDataExtractor::class); - $mockLocator = $this->prophesize(ContainerInterface::class); - $mockFlysystemFactory = $this->prophesize(FilesystemFactoryInterface::class); - $mockFlysystem = $this->prophesize(FilesystemInterface::class); $stubChallenge = $this->prophesize(AuthorizationChallenge::class); - $solver = new FilesystemSolver($mockLocator->reveal(), $mockExtractor->reveal()); - - $mockLocator->get('stub')->willReturn($mockFlysystemFactory->reveal()); - $mockFlysystemFactory->create(Argument::any())->willReturn($mockFlysystem->reveal()); + $mockExtractor = $this->prophesize(HttpDataExtractor::class); $mockExtractor->getCheckPath($stubChallenge->reveal())->willReturn($checkPath); $mockExtractor->getCheckContent($stubChallenge->reveal())->willReturn($checkContent); + $mockFlysystem = $this->prophesize(FilesystemInterface::class); $mockFlysystem->write($checkPath, $checkContent)->shouldBeCalled(); + $mockFlysystemFactory = $this->prophesize(FilesystemFactoryInterface::class); + $mockFlysystemFactory->create(Argument::any())->willReturn($mockFlysystem->reveal()); + + $mockLocator = $this->prophesize(ContainerInterface::class); + $mockLocator->get('stub')->willReturn($mockFlysystemFactory->reveal()); + + $solver = new FilesystemSolver($mockLocator->reveal(), $mockExtractor->reveal()); + $solver->configure(['adapter' => 'stub']); $solver->solve($stubChallenge->reveal()); } @@ -66,17 +68,20 @@ public function testCleanup() { $checkPath = '/.challenge'; + $stubChallenge = $this->prophesize(AuthorizationChallenge::class); + $mockExtractor = $this->prophesize(HttpDataExtractor::class); - $mockLocator = $this->prophesize(ContainerInterface::class); - $mockFlysystemFactory = $this->prophesize(FilesystemFactoryInterface::class); + $mockExtractor->getCheckPath($stubChallenge->reveal())->willReturn($checkPath); + $mockFlysystem = $this->prophesize(FilesystemInterface::class); - $stubChallenge = $this->prophesize(AuthorizationChallenge::class); - $solver = new FilesystemSolver($mockLocator->reveal(), $mockExtractor->reveal()); + $mockFlysystemFactory = $this->prophesize(FilesystemFactoryInterface::class); + $mockFlysystemFactory->create(Argument::any())->willReturn($mockFlysystem->reveal()); + $mockLocator = $this->prophesize(ContainerInterface::class); $mockLocator->get('stub')->willReturn($mockFlysystemFactory->reveal()); - $mockFlysystemFactory->create(Argument::any())->willReturn($mockFlysystem->reveal()); - $mockExtractor->getCheckPath($stubChallenge->reveal())->willReturn($checkPath); + + $solver = new FilesystemSolver($mockLocator->reveal(), $mockExtractor->reveal()); $mockFlysystem->delete($checkPath)->shouldBeCalled(); diff --git a/tests/setup.sh b/tests/setup.sh index 0d829e34..5ed674aa 100755 --- a/tests/setup.sh +++ b/tests/setup.sh @@ -12,4 +12,4 @@ docker run -d --name acme_server --net host letsencrypt/pebble-challtestsrv pebb docker run -d --name acme_pebble --net host -e PEBBLE_VA_NOSLEEP=1 -e PEBBLE_WFE_NONCEREJECT=0 letsencrypt/pebble pebble -dnsserver 127.0.0.1:8053 # Wait for boot to be completed -docker run --rm --net host martin/wait -c localhost:14000,localhost:80,localhost:8022,localhost:8053,localhost:5002 -t 120 +docker run --rm --net host martin/wait -c localhost:14000,localhost:8022,localhost:8053,localhost:5002 -t 120 From 2a473a8c4cc02b1a1460c9b3dafdbcc79623e546 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 17 Oct 2020 18:28:21 +0200 Subject: [PATCH 021/121] Migrate acmephp/cli to use typehints --- README.md | 12 +- res/acmephp.conf.dist | 116 ------------------- src/Cli/Action/AbstractAction.php | 6 +- src/Cli/Action/AbstractAwsAction.php | 16 ++- src/Cli/Action/ActionInterface.php | 2 +- src/Cli/Action/BuildNginxProxyAction.php | 2 +- src/Cli/Action/FilesystemAction.php | 45 +++---- src/Cli/Action/InstallAliyunCdnAction.php | 2 +- src/Cli/Action/InstallAliyunWafAction.php | 2 +- src/Cli/Action/PushFtpAction.php | 4 +- src/Cli/Action/PushRancherAction.php | 2 +- src/Cli/Action/PushSftpAction.php | 4 +- src/Cli/ActionHandler/ActionHandler.php | 83 ------------- src/Cli/Application.php | 16 --- src/Cli/Aws/ClientFactory.php | 6 +- src/Cli/Command/AbstractCommand.php | 51 +------- src/Cli/Exception/AcmeCliActionException.php | 2 +- src/Cli/Exception/CommandFlowException.php | 17 +-- src/Cli/Monolog/ConsoleHandler.php | 4 +- src/Cli/Repository/Repository.php | 78 ++++++------- src/Cli/Repository/RepositoryInterface.php | 96 ++++----------- src/Cli/Serializer/PemEncoder.php | 2 +- src/Cli/Serializer/PemNormalizer.php | 8 +- src/Core/Http/SecureHttpClient.php | 13 +-- 24 files changed, 118 insertions(+), 471 deletions(-) delete mode 100644 res/acmephp.conf.dist delete mode 100644 src/Cli/ActionHandler/ActionHandler.php diff --git a/README.md b/README.md index e0a8b7ae..75088a6b 100644 --- a/README.md +++ b/README.md @@ -25,10 +25,8 @@ by these features, have a look at the [acmephp/core](https://github.com/acmephp/ Acme PHP provides several major improvements over the default clients: - Acme PHP comes by nature as a single binary file: a single download and you are ready to start working ; -- Acme PHP is based on a configuration file (`~/.acmephp/acmephp.conf`) instead command line arguments. +- Acme PHP is based on a configuration file instead command line arguments. Thus, the configuration is much more expressive and the same setup is used at every renewal ; -- Acme PHP can monitor your CRONs and can send you alerts in many differents places: - E-mail, Slack, HipChat, Flowdock, Fleep (thanks to [Monolog](https://github.com/Seldaek/monolog)!) - Acme PHP is very extensible it to create the certificate files structure you need for your webserver. It brings several default formatters to create classical file structures (nginx, nginx-proxy, haproxy, etc.) but you can very easily create your own if you need to ; @@ -45,14 +43,6 @@ Acme PHP follows a strict BC policy by sticking carefully to [semantic versionin your scripts, your CRON tasks and your code will keep working properly even when you update Acme PHP (either the CLI tool or the library), as long as you keep the same major version (1.X.X, 2.X.X, etc.). -In addition of semantic versioning of stable versions for the CLI and the library, Acme PHP also follows -certain rules **for the CLI only**: -- an alpha release can break BC with previous alpha releases of the same version - (1.1.0-alpha2 can break BC with features introduced by 1.1.0-alpha1 but can't break BC with 1.0.0 features). -- a beta release cannot break BC with previous beta releases - (1.1.0-beta4 have to be BC with 1.1.0-beta3, 1.1.0-beta2, 1.1.0-beta1 and 1.0.0). New features can be added in beta - as long as they don't break BC. - ## Launch the Test suite The Acme PHP test suite uses the Docker Boulder image to create an ACME server. diff --git a/res/acmephp.conf.dist b/res/acmephp.conf.dist deleted file mode 100644 index 753a6b70..00000000 --- a/res/acmephp.conf.dist +++ /dev/null @@ -1,116 +0,0 @@ -# -# acmephp.conf - Configuration file for Acme PHP client. -# -# See https://github.com/acmephp/acmephp for more informations -# - -################################################################### -# Storage -# -# Configure here where and how you want to save your certificates -# and SSL keys. -# - -storage: - - # - # By default, Acme PHP will create a backup of every file - # before any modification. You can disable this mechanism here. - # - enable_backup: true - - # - # Actions to execute right after the generation of a file (key, CSR or certificate). - # Actions are executed in the order provided in configuration. - # - # See the documentation to learn how to create your own action. - # - post_generate: ~ - - # - # Build a file structure suited for nginx-proxy (https://github.com/jwilder/nginx-proxy) - # - # - action: build_nginxproxy - - # - # Mirror your local storage directory on a FTP host - # - # - action: mirror_file - # adapter: ftp - # root: /acmephp - # host: ftp.example.com - # username: username - # password: password - # # port: 21 - # # passive: true - # # ssl: true - # # timeout: 30 - - # - # Mirror your local storage directory on a SFTP host - # - # - action: mirror_file - # adapter: sftp - # root: /acmephp - # host: sftp.example.com - # username: username - # password: password - # # port: 22 - # # privateKey: path/to/or/contents/of/privatekey - # # timeout: 30 - - # - # Mirror your local storage directory on a an other location - # - # - action: mirror_file - # adapter: local - # root: /var/www/html - - # - # Install the certificate in an AWS ELB (Classic Load Balancer) - # - # - action: install_aws_elb - # region: eu-west-1 - # loadbalancer: my_elb - # # certificate_prefix: acmephp_ - # # cleanup_old_certificate: true - # # listener: 443 - - # - # Install the certificate in an AWS ELBv2 (Application Load Balancer) - # - # - action: install_aws_elbv2 - # region: eu-west-1 - # loadbalancer: my_elb - # # certificate_prefix: acmephp_ - # # cleanup_old_certificate: true - # # listener: 443 - -################################################################### -# Monitoring -# -# This section let you configure a simple monitoring mechanism that -# will warn you if an error occurs during a CRON job. -# - -monitoring: ~ # Monitoring is disabled by default - -# You can enabled it by configuring at least one alert handler. -# You can change the default handler level to decide when to be alerted -# (only when an error occurs or every time the CRON is started). -#monitoring: -# email: # Only SMTP(S) support for now -# to: galopintitouan@gmail.com -# host: smtp.example.com -# # port: 25 -# # username: ~ -# # password: ~ -# # encryption: ~ -# # subject: An error occured during Acme PHP CRON renewal -# # level: error # By default, only on error for email handler -# -# slack: -# token: your_token -# channel: general # Channel name without hashtag -# # username: Acme PHP -# # level: info # By default, on every CRON for slack handler diff --git a/src/Cli/Action/AbstractAction.php b/src/Cli/Action/AbstractAction.php index 51f19e18..8d497461 100644 --- a/src/Cli/Action/AbstractAction.php +++ b/src/Cli/Action/AbstractAction.php @@ -18,11 +18,7 @@ */ abstract class AbstractAction implements ActionInterface { - /** - * @param array $configuration - * @param array $keys - */ - protected function assertConfiguration($configuration, $keys) + protected function assertConfiguration(array $configuration, array $keys) { foreach ($keys as $key) { Assert::keyExists( diff --git a/src/Cli/Action/AbstractAwsAction.php b/src/Cli/Action/AbstractAwsAction.php index 0fd44209..baea6df9 100644 --- a/src/Cli/Action/AbstractAwsAction.php +++ b/src/Cli/Action/AbstractAwsAction.php @@ -35,7 +35,7 @@ public function __construct(ClientFactory $clientFactory) /** * {@inheritdoc} */ - public function handle($config, CertificateResponse $response) + public function handle(array $config, CertificateResponse $response) { $this->assertConfiguration($config, ['loadbalancer', 'region']); @@ -66,14 +66,12 @@ private function uploadCertificate(CertificateResponse $response, $region, $cert } $chainPem = implode("\n", $issuerChain); - $response = $iamClient->uploadServerCertificate( - [ - 'ServerCertificateName' => $certificateName, - 'CertificateBody' => $response->getCertificate()->getPEM(), - 'PrivateKey' => $response->getCertificateRequest()->getKeyPair()->getPrivateKey()->getPEM(), - 'CertificateChain' => $chainPem, - ] - ); + $response = $iamClient->uploadServerCertificate([ + 'ServerCertificateName' => $certificateName, + 'CertificateBody' => $response->getCertificate()->getPEM(), + 'PrivateKey' => $response->getCertificateRequest()->getKeyPair()->getPrivateKey()->getPEM(), + 'CertificateChain' => $chainPem, + ]); return $response['ServerCertificateMetadata']['Arn']; } diff --git a/src/Cli/Action/ActionInterface.php b/src/Cli/Action/ActionInterface.php index aac92f6b..5fdfe353 100644 --- a/src/Cli/Action/ActionInterface.php +++ b/src/Cli/Action/ActionInterface.php @@ -24,5 +24,5 @@ interface ActionInterface * * @param array $config */ - public function handle($config, CertificateResponse $response); + public function handle(array $config, CertificateResponse $response); } diff --git a/src/Cli/Action/BuildNginxProxyAction.php b/src/Cli/Action/BuildNginxProxyAction.php index 60c91b2c..dac28a9b 100644 --- a/src/Cli/Action/BuildNginxProxyAction.php +++ b/src/Cli/Action/BuildNginxProxyAction.php @@ -37,7 +37,7 @@ public function __construct(RepositoryInterface $repository) /** * {@inheritdoc} */ - public function handle($config, CertificateResponse $response) + public function handle(array $config, CertificateResponse $response) { $domain = $response->getCertificateRequest()->getDistinguishedName()->getCommonName(); $privateKey = $response->getCertificateRequest()->getKeyPair()->getPrivateKey(); diff --git a/src/Cli/Action/FilesystemAction.php b/src/Cli/Action/FilesystemAction.php index 75672799..ce7a4b52 100644 --- a/src/Cli/Action/FilesystemAction.php +++ b/src/Cli/Action/FilesystemAction.php @@ -26,27 +26,23 @@ class FilesystemAction extends AbstractAction /** * @var FlysystemFilesystemInterface */ - protected $master; + protected $storage; + /** * @var ContainerInterface */ protected $filesystemFactoryLocator; - /** - * @param ContainerInterface $filesystemFactoryLocator - */ - public function __construct( - FlysystemFilesystemInterface $master, - ContainerInterface $filesystemFactoryLocator = null - ) { - $this->filesystemFactoryLocator = $filesystemFactoryLocator = null ? new ServiceLocator([]) : $filesystemFactoryLocator; - $this->master = $master; + public function __construct(FlysystemFilesystemInterface $storage, ContainerInterface $locator = null) + { + $this->storage = $storage; + $this->filesystemFactoryLocator = $locator ?: new ServiceLocator([]); } /** * {@inheritdoc} */ - public function handle($config, CertificateResponse $response) + public function handle(array $config, CertificateResponse $response) { $this->assertConfiguration($config, ['adapter']); @@ -54,7 +50,7 @@ public function handle($config, CertificateResponse $response) $factory = $this->filesystemFactoryLocator->get($config['adapter']); $filesystem = $factory->create($config); - $files = $this->master->listContents('.', true); + $files = $this->storage->listContents('.', true); foreach ($files as $file) { if (0 === strpos($file['basename'], '.')) { continue; @@ -64,11 +60,8 @@ public function handle($config, CertificateResponse $response) } } - /** - * @param string $type - * @param string $path - */ - private function mirror($type, $path, FilesystemInterface $filesystem) + + private function mirror(string $type, string $path, FilesystemInterface $filesystem) { if ('dir' === $type) { $this->mirrorDirectory($path, $filesystem); @@ -79,25 +72,19 @@ private function mirror($type, $path, FilesystemInterface $filesystem) $this->mirrorFile($path, $filesystem); } - /** - * @param string $path - */ - private function mirrorDirectory($path, FilesystemInterface $filesystem) + private function mirrorDirectory(string $path, FilesystemInterface $filesystem) { $filesystem->createDir($path); } - /** - * @param string $path - */ - private function mirrorFile($path, FilesystemInterface $filesystem) + private function mirrorFile(string $path, FilesystemInterface $filesystem) { - $masterContent = $this->master->read($path); + $storageContent = $this->storage->read($path); - if (!\is_string($masterContent)) { - throw new \RuntimeException(sprintf('File %s could not be read on master storage', $path)); + if (!\is_string($storageContent)) { + throw new \RuntimeException(sprintf('File %s could not be read on storage storage', $path)); } - $filesystem->write($path, $masterContent); + $filesystem->write($path, $storageContent); } } diff --git a/src/Cli/Action/InstallAliyunCdnAction.php b/src/Cli/Action/InstallAliyunCdnAction.php index 6d50a13b..a30018e8 100644 --- a/src/Cli/Action/InstallAliyunCdnAction.php +++ b/src/Cli/Action/InstallAliyunCdnAction.php @@ -25,7 +25,7 @@ class InstallAliyunCdnAction extends AbstractAction /** * {@inheritdoc} */ - public function handle($config, CertificateResponse $response) + public function handle(array $config, CertificateResponse $response) { $issuerChain = []; $issuerChain[] = $response->getCertificate()->getPEM(); diff --git a/src/Cli/Action/InstallAliyunWafAction.php b/src/Cli/Action/InstallAliyunWafAction.php index 678e883a..86bcb01e 100644 --- a/src/Cli/Action/InstallAliyunWafAction.php +++ b/src/Cli/Action/InstallAliyunWafAction.php @@ -25,7 +25,7 @@ class InstallAliyunWafAction extends AbstractAction /** * {@inheritdoc} */ - public function handle($config, CertificateResponse $response) + public function handle(array $config, CertificateResponse $response) { $issuerChain = []; $issuerChain[] = $response->getCertificate()->getPEM(); diff --git a/src/Cli/Action/PushFtpAction.php b/src/Cli/Action/PushFtpAction.php index 5001f72a..c491a8de 100644 --- a/src/Cli/Action/PushFtpAction.php +++ b/src/Cli/Action/PushFtpAction.php @@ -38,10 +38,10 @@ public function __construct(FilesystemAction $filesystemAction) /** * {@inheritdoc} */ - public function handle($config, CertificateResponse $response) + public function handle(array $config, CertificateResponse $response) { $config['adapter'] = 'ftp'; - return $this->filesystemAction->handle($config, $response); + $this->filesystemAction->handle($config, $response); } } diff --git a/src/Cli/Action/PushRancherAction.php b/src/Cli/Action/PushRancherAction.php index 9b03c39b..c1785a42 100644 --- a/src/Cli/Action/PushRancherAction.php +++ b/src/Cli/Action/PushRancherAction.php @@ -38,7 +38,7 @@ public function __construct(Client $httpClient) /** * {@inheritdoc} */ - public function handle($config, CertificateResponse $response) + public function handle(array $config, CertificateResponse $response) { $payload = $this->createRancherPayloadFromResponse($response); diff --git a/src/Cli/Action/PushSftpAction.php b/src/Cli/Action/PushSftpAction.php index 18955271..47979e14 100644 --- a/src/Cli/Action/PushSftpAction.php +++ b/src/Cli/Action/PushSftpAction.php @@ -38,10 +38,10 @@ public function __construct(FilesystemAction $filesystemAction) /** * {@inheritdoc} */ - public function handle($config, CertificateResponse $response) + public function handle(array $config, CertificateResponse $response) { $config['adapter'] = 'sftp'; - return $this->filesystemAction->handle($config, $response); + $this->filesystemAction->handle($config, $response); } } diff --git a/src/Cli/ActionHandler/ActionHandler.php b/src/Cli/ActionHandler/ActionHandler.php deleted file mode 100644 index a4d4f4a0..00000000 --- a/src/Cli/ActionHandler/ActionHandler.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\ActionHandler; - -use AcmePhp\Cli\Exception\AcmeCliActionException; -use AcmePhp\Cli\Exception\AcmeCliException; -use AcmePhp\Ssl\CertificateResponse; -use Psr\Container\ContainerInterface; -use Psr\Log\LoggerInterface; - -/** - * @author Titouan Galopin - */ -class ActionHandler -{ - /** - * @var ContainerInterface - */ - private $actionLocator; - - /** - * @var LoggerInterface - */ - private $cliLogger; - - /** - * @var array - */ - private $postGenerateConfig; - - public function __construct(ContainerInterface $actionLocator, LoggerInterface $cliLogger, array $postGenerateConfig) - { - $this->actionLocator = $actionLocator; - $this->cliLogger = $cliLogger; - $this->postGenerateConfig = $postGenerateConfig; - } - - /** - * Apply all the registered actions to the given certificate response. - * - * @throws AcmeCliException if the configuration is invalid - * @throws AcmeCliActionException if there is a problem during the execution of an action - */ - public function handle(CertificateResponse $response) - { - $actions = []; - - // Prepare - foreach ($this->postGenerateConfig as $key => $actionConfig) { - if (empty($actionConfig['action'])) { - throw new AcmeCliException(sprintf('No action was configured at key storage.post_generate.%s, a non-empty "action" key is required.', $key)); - } - - $name = $actionConfig['action']; - unset($actionConfig['action']); - - $actions[] = [ - 'handler' => $this->actionLocator->get($name), - 'name' => $name, - 'config' => $actionConfig, - ]; - } - - // Handle - foreach ($actions as $action) { - try { - $this->cliLogger->info(' - Running '.$action['name'].'...'); - $action['handler']->handle($action['config'], $response); - } catch (\Exception $exception) { - throw new AcmeCliActionException($action['name'], $exception); - } - } - } -} diff --git a/src/Cli/Application.php b/src/Cli/Application.php index f3e1dd9d..dd10c4d1 100644 --- a/src/Cli/Application.php +++ b/src/Cli/Application.php @@ -75,22 +75,6 @@ protected function getDefaultInputDefinition() return $definition; } - /** - * @return string - */ - public function getConfigFile() - { - return Path::canonicalize('~/.acmephp/acmephp.conf'); - } - - /** - * @return string - */ - public function getConfigReferenceFile() - { - return Path::canonicalize(__DIR__.'/../../res/acmephp.conf.dist'); - } - /** * @return string */ diff --git a/src/Cli/Aws/ClientFactory.php b/src/Cli/Aws/ClientFactory.php index 76754b28..7117e785 100644 --- a/src/Cli/Aws/ClientFactory.php +++ b/src/Cli/Aws/ClientFactory.php @@ -17,19 +17,19 @@ class ClientFactory { - public function getIamClient($region = null) + public function getIamClient($region = null): IamClient { return new IamClient($this->getClientArgs(['region' => $region, 'version' => '2010-05-08'])); } - public function getElbClient($region = null) + public function getElbClient($region = null): ElasticLoadBalancingClient { return new ElasticLoadBalancingClient( $this->getClientArgs(['region' => $region, 'version' => '2012-06-01']) ); } - public function getElbv2Client($region = null) + public function getElbv2Client($region = null): ElasticLoadBalancingV2Client { return new ElasticLoadBalancingV2Client( $this->getClientArgs(['region' => $region, 'version' => '2015-12-01']) diff --git a/src/Cli/Command/AbstractCommand.php b/src/Cli/Command/AbstractCommand.php index d4566f2f..4382dd99 100644 --- a/src/Cli/Command/AbstractCommand.php +++ b/src/Cli/Command/AbstractCommand.php @@ -26,9 +26,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\Yaml\Yaml; /** * @author Titouan Galopin @@ -45,11 +42,6 @@ abstract class AbstractCommand extends Command implements LoggerInterface */ protected $output; - /** - * @var array|null - */ - private $configuration; - /** * @var ContainerBuilder|null */ @@ -64,20 +56,14 @@ protected function initialize(InputInterface $input, OutputInterface $output) $this->output = $output; } - /** - * @return RepositoryInterface - */ - protected function getRepository() + protected function getRepository(): RepositoryInterface { $this->debug('Loading repository'); return $this->getContainer()->get('repository'); } - /** - * @return AcmeClient - */ - protected function getClient() + protected function getClient(): AcmeClient { $this->debug('Creating Acme client'); $this->notice('Loading account key pair...'); @@ -96,18 +82,12 @@ protected function getClient() return new AcmeClient($httpClient, $this->input->getOption('server'), $csrSigner); } - /** - * @return LoggerInterface - */ - protected function getCliLogger() + protected function getCliLogger(): LoggerInterface { return $this->getContainer()->get('cli_logger'); } - /** - * @return ContainerBuilder - */ - protected function getContainer() + protected function getContainer(): ContainerBuilder { if (null === $this->container) { $this->initializeContainer(); @@ -118,10 +98,6 @@ protected function getContainer() private function initializeContainer() { - if (null === $this->configuration) { - $this->initializeConfiguration(); - } - $this->container = new ContainerBuilder(); // Application services and parameters @@ -158,25 +134,6 @@ private function initializeContainer() $this->container->set('output', $this->output); } - private function initializeConfiguration() - { - $configFile = $this->getApplication()->getConfigFile(); - $referenceFile = $this->getApplication()->getConfigReferenceFile(); - - if (!file_exists($configFile)) { - $filesystem = new Filesystem(); - $filesystem->dumpFile($configFile, file_get_contents($referenceFile)); - - $this->notice('Configuration file '.$configFile.' did not exist and has been created.'); - } - - if (!is_readable($configFile)) { - throw new IOException('Configuration file '.$configFile.' is not readable.'); - } - - $this->configuration = ['acmephp' => Yaml::parse(file_get_contents($configFile))]; - } - /** * {@inheritdoc} */ diff --git a/src/Cli/Exception/AcmeCliActionException.php b/src/Cli/Exception/AcmeCliActionException.php index a18f3e6f..fa785b61 100644 --- a/src/Cli/Exception/AcmeCliActionException.php +++ b/src/Cli/Exception/AcmeCliActionException.php @@ -16,7 +16,7 @@ */ class AcmeCliActionException extends AcmeCliException { - public function __construct($actionName, \Exception $previous = null) + public function __construct(string $actionName, \Exception $previous = null) { parent::__construct(sprintf('An exception was thrown during action "%s"', $actionName), $previous); } diff --git a/src/Cli/Exception/CommandFlowException.php b/src/Cli/Exception/CommandFlowException.php index 3f3b3cb9..fcaafe96 100644 --- a/src/Cli/Exception/CommandFlowException.php +++ b/src/Cli/Exception/CommandFlowException.php @@ -25,7 +25,7 @@ class CommandFlowException extends AcmeCliException * @param string $command Name of the command to run in order to fix the flow * @param array $arguments Optional list of missing arguments */ - public function __construct($missing, $command, array $arguments = [], \Exception $previous = null) + public function __construct(string $missing, string $command, array $arguments = [], \Exception $previous = null) { $this->missing = $missing; $this->command = $command; @@ -43,26 +43,17 @@ public function __construct($missing, $command, array $arguments = [], \Exceptio parent::__construct($message, $previous); } - /** - * @return string - */ - public function getMissing() + public function getMissing(): string { return $this->missing; } - /** - * @return string - */ - public function getCommand() + public function getCommand(): string { return $this->command; } - /** - * @return array - */ - public function getArguments() + public function getArguments(): array { return $this->arguments; } diff --git a/src/Cli/Monolog/ConsoleHandler.php b/src/Cli/Monolog/ConsoleHandler.php index ea364e90..94692863 100644 --- a/src/Cli/Monolog/ConsoleHandler.php +++ b/src/Cli/Monolog/ConsoleHandler.php @@ -13,7 +13,6 @@ use Monolog\Handler\AbstractProcessingHandler; use Monolog\Logger; -use Symfony\Component\Console\Output\Output; use Symfony\Component\Console\Output\OutputInterface; /** @@ -52,9 +51,10 @@ class ConsoleHandler extends AbstractProcessingHandler * @param array $verbosityLevelMap Array that maps the OutputInterface verbosity to a minimum logging * level (leave empty to use the default mapping) */ - public function __construct(OutputInterface $output = null, $bubble = true, array $verbosityLevelMap = []) + public function __construct(OutputInterface $output = null, bool $bubble = true, array $verbosityLevelMap = []) { parent::__construct(Logger::DEBUG, $bubble); + $this->output = $output; if ($verbosityLevelMap) { diff --git a/src/Cli/Repository/Repository.php b/src/Cli/Repository/Repository.php index 68a21dc9..0b475b16 100644 --- a/src/Cli/Repository/Repository.php +++ b/src/Cli/Repository/Repository.php @@ -30,19 +30,19 @@ */ class Repository implements RepositoryInterface { - const PATH_ACCOUNT_KEY_PRIVATE = 'account/key.private.pem'; - const PATH_ACCOUNT_KEY_PUBLIC = 'account/key.public.pem'; + public const PATH_ACCOUNT_KEY_PRIVATE = 'account/key.private.pem'; + public const PATH_ACCOUNT_KEY_PUBLIC = 'account/key.public.pem'; - const PATH_DOMAIN_KEY_PUBLIC = 'certs/{domain}/private/key.public.pem'; - const PATH_DOMAIN_KEY_PRIVATE = 'certs/{domain}/private/key.private.pem'; - const PATH_DOMAIN_CERT_CERT = 'certs/{domain}/public/cert.pem'; - const PATH_DOMAIN_CERT_CHAIN = 'certs/{domain}/public/chain.pem'; - const PATH_DOMAIN_CERT_FULLCHAIN = 'certs/{domain}/public/fullchain.pem'; - const PATH_DOMAIN_CERT_COMBINED = 'certs/{domain}/private/combined.pem'; + public const PATH_DOMAIN_KEY_PUBLIC = 'certs/{domain}/private/key.public.pem'; + public const PATH_DOMAIN_KEY_PRIVATE = 'certs/{domain}/private/key.private.pem'; + public const PATH_DOMAIN_CERT_CERT = 'certs/{domain}/public/cert.pem'; + public const PATH_DOMAIN_CERT_CHAIN = 'certs/{domain}/public/chain.pem'; + public const PATH_DOMAIN_CERT_FULLCHAIN = 'certs/{domain}/public/fullchain.pem'; + public const PATH_DOMAIN_CERT_COMBINED = 'certs/{domain}/private/combined.pem'; - const PATH_CACHE_AUTHORIZATION_CHALLENGE = 'var/{domain}/authorization_challenge.json'; - const PATH_CACHE_DISTINGUISHED_NAME = 'var/{domain}/distinguished_name.json'; - const PATH_CACHE_CERTIFICATE_ORDER = 'var/{domains}/certificate_order.json'; + public const PATH_CACHE_AUTHORIZATION_CHALLENGE = 'var/{domain}/authorization_challenge.json'; + public const PATH_CACHE_DISTINGUISHED_NAME = 'var/{domain}/distinguished_name.json'; + public const PATH_CACHE_CERTIFICATE_ORDER = 'var/{domains}/certificate_order.json'; /** * @var SerializerInterface @@ -93,20 +93,10 @@ public function storeAccountKeyPair(KeyPair $keyPair) } } - private function getPathForDomain($path, $domain) - { - return strtr($path, ['{domain}' => $this->normalizeDomain($domain)]); - } - - private function getPathForDomainList($path, array $domains) - { - return strtr($path, ['{domains}' => $this->normalizeDomainList($domains)]); - } - /** * {@inheritdoc} */ - public function hasAccountKeyPair() + public function hasAccountKeyPair(): bool { return $this->storage->has(self::PATH_ACCOUNT_KEY_PRIVATE); } @@ -114,7 +104,7 @@ public function hasAccountKeyPair() /** * {@inheritdoc} */ - public function loadAccountKeyPair() + public function loadAccountKeyPair(): KeyPair { try { $publicKeyPem = $this->storage->read(self::PATH_ACCOUNT_KEY_PUBLIC); @@ -132,7 +122,7 @@ public function loadAccountKeyPair() /** * {@inheritdoc} */ - public function storeDomainKeyPair($domain, KeyPair $keyPair) + public function storeDomainKeyPair(string $domain, KeyPair $keyPair) { try { $this->save( @@ -152,7 +142,7 @@ public function storeDomainKeyPair($domain, KeyPair $keyPair) /** * {@inheritdoc} */ - public function hasDomainKeyPair($domain) + public function hasDomainKeyPair(string $domain): bool { return $this->storage->has($this->getPathForDomain(self::PATH_DOMAIN_KEY_PRIVATE, $domain)); } @@ -160,7 +150,7 @@ public function hasDomainKeyPair($domain) /** * {@inheritdoc} */ - public function loadDomainKeyPair($domain) + public function loadDomainKeyPair(string $domain): KeyPair { try { $publicKeyPem = $this->storage->read($this->getPathForDomain(self::PATH_DOMAIN_KEY_PUBLIC, $domain)); @@ -178,7 +168,7 @@ public function loadDomainKeyPair($domain) /** * {@inheritdoc} */ - public function storeDomainAuthorizationChallenge($domain, AuthorizationChallenge $authorizationChallenge) + public function storeDomainAuthorizationChallenge(string $domain, AuthorizationChallenge $authorizationChallenge) { try { $this->save( @@ -193,7 +183,7 @@ public function storeDomainAuthorizationChallenge($domain, AuthorizationChalleng /** * {@inheritdoc} */ - public function hasDomainAuthorizationChallenge($domain) + public function hasDomainAuthorizationChallenge(string $domain): bool { return $this->storage->has($this->getPathForDomain(self::PATH_CACHE_AUTHORIZATION_CHALLENGE, $domain)); } @@ -201,7 +191,7 @@ public function hasDomainAuthorizationChallenge($domain) /** * {@inheritdoc} */ - public function loadDomainAuthorizationChallenge($domain) + public function loadDomainAuthorizationChallenge(string $domain): AuthorizationChallenge { try { $json = $this->storage->read($this->getPathForDomain(self::PATH_CACHE_AUTHORIZATION_CHALLENGE, $domain)); @@ -215,7 +205,7 @@ public function loadDomainAuthorizationChallenge($domain) /** * {@inheritdoc} */ - public function storeDomainDistinguishedName($domain, DistinguishedName $distinguishedName) + public function storeDomainDistinguishedName(string $domain, DistinguishedName $distinguishedName) { try { $this->save( @@ -230,7 +220,7 @@ public function storeDomainDistinguishedName($domain, DistinguishedName $disting /** * {@inheritdoc} */ - public function hasDomainDistinguishedName($domain) + public function hasDomainDistinguishedName(string $domain): bool { return $this->storage->has($this->getPathForDomain(self::PATH_CACHE_DISTINGUISHED_NAME, $domain)); } @@ -238,7 +228,7 @@ public function hasDomainDistinguishedName($domain) /** * {@inheritdoc} */ - public function loadDomainDistinguishedName($domain) + public function loadDomainDistinguishedName(string $domain): DistinguishedName { try { $json = $this->storage->read($this->getPathForDomain(self::PATH_CACHE_DISTINGUISHED_NAME, $domain)); @@ -252,7 +242,7 @@ public function loadDomainDistinguishedName($domain) /** * {@inheritdoc} */ - public function storeDomainCertificate($domain, Certificate $certificate) + public function storeDomainCertificate(string $domain, Certificate $certificate) { // Simple certificate $certPem = $this->serializer->serialize($certificate, PemEncoder::FORMAT); @@ -285,7 +275,7 @@ public function storeDomainCertificate($domain, Certificate $certificate) /** * {@inheritdoc} */ - public function hasDomainCertificate($domain) + public function hasDomainCertificate(string $domain): bool { return $this->storage->has($this->getPathForDomain(self::PATH_DOMAIN_CERT_FULLCHAIN, $domain)); } @@ -293,7 +283,7 @@ public function hasDomainCertificate($domain) /** * {@inheritdoc} */ - public function loadDomainCertificate($domain) + public function loadDomainCertificate(string $domain): Certificate { try { $pems = explode('-----BEGIN CERTIFICATE-----', $this->storage->read($this->getPathForDomain(self::PATH_DOMAIN_CERT_FULLCHAIN, $domain))); @@ -337,7 +327,7 @@ public function storeCertificateOrder(array $domains, CertificateOrder $order) /** * {@inheritdoc} */ - public function hasCertificateOrder(array $domains) + public function hasCertificateOrder(array $domains): bool { return $this->storage->has($this->getPathForDomainList(self::PATH_CACHE_CERTIFICATE_ORDER, $domains)); } @@ -345,7 +335,7 @@ public function hasCertificateOrder(array $domains) /** * {@inheritdoc} */ - public function loadCertificateOrder(array $domains) + public function loadCertificateOrder(array $domains): CertificateOrder { try { $json = $this->storage->read($this->getPathForDomainList(self::PATH_CACHE_CERTIFICATE_ORDER, $domains)); @@ -359,7 +349,7 @@ public function loadCertificateOrder(array $domains) /** * {@inheritdoc} */ - public function save($path, $content, $visibility = self::VISIBILITY_PRIVATE) + public function save(string $path, string $content, string $visibility = self::VISIBILITY_PRIVATE) { if (!$this->storage->has($path)) { $this->storage->write($path, $content); @@ -370,9 +360,19 @@ public function save($path, $content, $visibility = self::VISIBILITY_PRIVATE) $this->storage->setVisibility($path, $visibility); } + private function getPathForDomain($path, $domain) + { + return strtr($path, ['{domain}' => $this->normalizeDomain($domain)]); + } + + private function getPathForDomainList($path, array $domains) + { + return strtr($path, ['{domains}' => $this->normalizeDomainList($domains)]); + } + private function normalizeDomain($domain) { - return $domain; + return trim($domain); } private function normalizeDomainList(array $domains) diff --git a/src/Cli/Repository/RepositoryInterface.php b/src/Cli/Repository/RepositoryInterface.php index c04f1fe1..e1011cbe 100644 --- a/src/Cli/Repository/RepositoryInterface.php +++ b/src/Cli/Repository/RepositoryInterface.php @@ -24,8 +24,8 @@ */ interface RepositoryInterface { - const VISIBILITY_PUBLIC = 'public'; - const VISIBILITY_PRIVATE = 'private'; + public const VISIBILITY_PUBLIC = 'public'; + public const VISIBILITY_PRIVATE = 'private'; /** * Store a given certificate as associated to a given domain. @@ -35,24 +35,16 @@ interface RepositoryInterface public function storeCertificateOrder(array $domains, CertificateOrder $order); /** - * Check if there is a certificate associated to the given domain in the repository. - * - * @param string $domain - * - * @return bool + * Check if there is a certificate order associated to given domains in the repository. */ - public function hasCertificateOrder(array $domains); + public function hasCertificateOrder(array $domains): bool; /** - * Load the certificate associated to a given domain. - * - * @param string $domain + * Load the certificate irder associated to given domains. * * @throws AcmeCliException - * - * @return CertificateOrder */ - public function loadCertificateOrder(array $domains); + public function loadCertificateOrder(array $domains): CertificateOrder; /** * Extract important elements from the given certificate response and store them @@ -77,142 +69,96 @@ public function storeAccountKeyPair(KeyPair $keyPair); /** * Check if there is an account key pair in the repository. - * - * @return bool */ - public function hasAccountKeyPair(); + public function hasAccountKeyPair(): bool; /** * Load the account key pair. * * @throws AcmeCliException - * - * @return KeyPair */ - public function loadAccountKeyPair(); + public function loadAccountKeyPair(): KeyPair; /** * Store a given key pair as associated to a given domain. * - * @param string $domain - * * @throws AcmeCliException */ - public function storeDomainKeyPair($domain, KeyPair $keyPair); + public function storeDomainKeyPair(string $domain, KeyPair $keyPair); /** * Check if there is a key pair associated to the given domain in the repository. - * - * @param string $domain - * - * @return bool */ - public function hasDomainKeyPair($domain); + public function hasDomainKeyPair(string $domain): bool; /** * Load the key pair associated to a given domain. * - * @param string $domain - * * @throws AcmeCliException - * - * @return KeyPair */ - public function loadDomainKeyPair($domain); + public function loadDomainKeyPair(string $domain): KeyPair; /** * Store a given authorization challenge as associated to a given domain. * - * @param string $domain - * * @throws AcmeCliException */ - public function storeDomainAuthorizationChallenge($domain, AuthorizationChallenge $authorizationChallenge); + public function storeDomainAuthorizationChallenge(string $domain, AuthorizationChallenge $authorizationChallenge); /** * Check if there is an authorization challenge associated to the given domain in the repository. - * - * @param string $domain - * - * @return bool */ - public function hasDomainAuthorizationChallenge($domain); + public function hasDomainAuthorizationChallenge(string $domain): bool; /** * Load the authorization challenge associated to a given domain. * - * @param string $domain - * * @throws AcmeCliException - * - * @return AuthorizationChallenge */ - public function loadDomainAuthorizationChallenge($domain); + public function loadDomainAuthorizationChallenge(string $domain): AuthorizationChallenge; /** * Store a given distinguished name as associated to a given domain. * - * @param string $domain - * * @throws AcmeCliException */ - public function storeDomainDistinguishedName($domain, DistinguishedName $distinguishedName); + public function storeDomainDistinguishedName(string $domain, DistinguishedName $distinguishedName); /** * Check if there is a distinguished name associated to the given domain in the repository. - * - * @param string $domain - * - * @return bool */ - public function hasDomainDistinguishedName($domain); + public function hasDomainDistinguishedName(string $domain): bool; /** * Load the distinguished name associated to a given domain. * - * @param string $domain - * * @throws AcmeCliException - * - * @return DistinguishedName */ - public function loadDomainDistinguishedName($domain); + public function loadDomainDistinguishedName(string $domain): DistinguishedName; /** * Store a given certificate as associated to a given domain. * - * @param string $domain - * * @throws AcmeCliException */ - public function storeDomainCertificate($domain, Certificate $certificate); + public function storeDomainCertificate(string $domain, Certificate $certificate); /** * Check if there is a certificate associated to the given domain in the repository. * - * @param string $domain - * * @return bool */ - public function hasDomainCertificate($domain); + public function hasDomainCertificate(string $domain); /** * Load the certificate associated to a given domain. * - * @param string $domain - * * @throws AcmeCliException - * - * @return Certificate */ - public function loadDomainCertificate($domain); + public function loadDomainCertificate(string $domain): Certificate; /** * Save a given string into a given path handling backup. - * - * @param string $path - * @param string $content - * @param string $visibility the visibilty to use for this file */ - public function save($path, $content, $visibility = self::VISIBILITY_PRIVATE); + public function save(string $path, string $content, string $visibility = self::VISIBILITY_PRIVATE); } diff --git a/src/Cli/Serializer/PemEncoder.php b/src/Cli/Serializer/PemEncoder.php index 01d3a15d..d5f8d117 100644 --- a/src/Cli/Serializer/PemEncoder.php +++ b/src/Cli/Serializer/PemEncoder.php @@ -19,7 +19,7 @@ */ class PemEncoder implements EncoderInterface, DecoderInterface { - const FORMAT = 'pem'; + public const FORMAT = 'pem'; /** * {@inheritdoc} diff --git a/src/Cli/Serializer/PemNormalizer.php b/src/Cli/Serializer/PemNormalizer.php index 90104696..8053fbb1 100644 --- a/src/Cli/Serializer/PemNormalizer.php +++ b/src/Cli/Serializer/PemNormalizer.php @@ -24,7 +24,7 @@ class PemNormalizer implements NormalizerInterface, DenormalizerInterface /** * {@inheritdoc} */ - public function normalize($object, $format = null, array $context = []) + public function normalize($object, string $format = null, array $context = []) { return $object->getPEM(); } @@ -32,7 +32,7 @@ public function normalize($object, $format = null, array $context = []) /** * {@inheritdoc} */ - public function denormalize($data, $class, $format = null, array $context = []) + public function denormalize($data, $class, string $format = null, array $context = []) { return new $class($data); } @@ -40,7 +40,7 @@ public function denormalize($data, $class, $format = null, array $context = []) /** * {@inheritdoc} */ - public function supportsNormalization($data, $format = null) + public function supportsNormalization($data, string $format = null) { return \is_object($data) && ($data instanceof Certificate || $data instanceof Key); } @@ -48,7 +48,7 @@ public function supportsNormalization($data, $format = null) /** * {@inheritdoc} */ - public function supportsDenormalization($data, $type, $format = null) + public function supportsDenormalization($data, $type, string $format = null) { return \is_string($data); } diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index a178d54a..3d3070d4 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -153,13 +153,8 @@ public function signKidPayload(string $endpoint, string $account, array $payload /** * Generates a payload signed with JWK. - * - * @param string $endpoint - * @param array $payload - * - * @return array the signed Payload */ - public function signJwkPayload($endpoint, array $payload = null) + public function signJwkPayload(string $endpoint, array $payload = null): array { return $this->signPayload( [ @@ -327,7 +322,7 @@ private function signPayload(array $protected, array $payload = null): array ]; } - private function createRequest($method, $endpoint, $data) + private function createRequest($method, $endpoint, $data): Request { $request = new Request($method, $endpoint); $request = $request->withHeader('Accept', 'application/json,application/jose+json,'); @@ -351,7 +346,7 @@ private function handleClientException(Request $request, \Exception $exception) throw new AcmeCoreClientException(sprintf('An error occured during request "%s %s"', $request->getMethod(), $request->getUri()), $exception); } - private function getNonce() + private function getNonce(): ?string { if ($this->lastResponse && $this->lastResponse->hasHeader('Replay-Nonce')) { return $this->lastResponse->getHeaderLine('Replay-Nonce'); @@ -364,6 +359,8 @@ private function getNonce() return $this->lastResponse->getHeaderLine('Replay-Nonce'); } } + + return null; } private function getAlg(): string From ccbba760f080f19c979f8db9be3a2885c6381275 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 7 Nov 2020 19:21:35 +0100 Subject: [PATCH 022/121] Fix CS --- src/Cli/Action/ActionInterface.php | 2 -- src/Cli/Action/FilesystemAction.php | 1 - 2 files changed, 3 deletions(-) diff --git a/src/Cli/Action/ActionInterface.php b/src/Cli/Action/ActionInterface.php index 5fdfe353..0cfafe2e 100644 --- a/src/Cli/Action/ActionInterface.php +++ b/src/Cli/Action/ActionInterface.php @@ -21,8 +21,6 @@ interface ActionInterface /** * Get a certificate response and execute the action with it. * Use the given configuration if needed. - * - * @param array $config */ public function handle(array $config, CertificateResponse $response); } diff --git a/src/Cli/Action/FilesystemAction.php b/src/Cli/Action/FilesystemAction.php index ce7a4b52..cede6e8d 100644 --- a/src/Cli/Action/FilesystemAction.php +++ b/src/Cli/Action/FilesystemAction.php @@ -60,7 +60,6 @@ public function handle(array $config, CertificateResponse $response) } } - private function mirror(string $type, string $path, FilesystemInterface $filesystem) { if ('dir' === $type) { From 3fc8c60af2c1e9890109c974ca1c052b6befd886 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 7 Nov 2020 19:23:30 +0100 Subject: [PATCH 023/121] Remove deprecated features --- src/Cli/Action/PushFtpAction.php | 47 ------------------- src/Cli/Action/PushSftpAction.php | 47 ------------------- .../Exception/AcmeDnsResolutionException.php | 25 ---------- src/Cli/Resources/services.xml | 8 ---- src/Core/Http/SecureHttpClient.php | 46 ------------------ 5 files changed, 173 deletions(-) delete mode 100644 src/Cli/Action/PushFtpAction.php delete mode 100644 src/Cli/Action/PushSftpAction.php delete mode 100644 src/Cli/Exception/AcmeDnsResolutionException.php diff --git a/src/Cli/Action/PushFtpAction.php b/src/Cli/Action/PushFtpAction.php deleted file mode 100644 index c491a8de..00000000 --- a/src/Cli/Action/PushFtpAction.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\Action; - -use AcmePhp\Ssl\CertificateResponse; - -/** - * Action to write files using a Flysystem adapter. - * - * @author Titouan Galopin - */ -class PushFtpAction implements ActionInterface -{ - /** - * @var FilesystemAction - */ - private $filesystemAction; - - /** - * @param FilesystemAction - */ - public function __construct(FilesystemAction $filesystemAction) - { - @trigger_error('The "push_ftp" action is deprecated since version 1.0 and will be removed in 2.0. Use "mirror_file" action instead', E_USER_DEPRECATED); - - $this->filesystemAction = $filesystemAction; - } - - /** - * {@inheritdoc} - */ - public function handle(array $config, CertificateResponse $response) - { - $config['adapter'] = 'ftp'; - - $this->filesystemAction->handle($config, $response); - } -} diff --git a/src/Cli/Action/PushSftpAction.php b/src/Cli/Action/PushSftpAction.php deleted file mode 100644 index 47979e14..00000000 --- a/src/Cli/Action/PushSftpAction.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\Action; - -use AcmePhp\Ssl\CertificateResponse; - -/** - * Action to write files using a Flysystem adapter. - * - * @author Titouan Galopin - */ -class PushSftpAction implements ActionInterface -{ - /** - * @var FilesystemAction - */ - private $filesystemAction; - - /** - * @param FilesystemAction - */ - public function __construct(FilesystemAction $filesystemAction) - { - @trigger_error('The "push_sftp" action is deprecated since version 1.0 and will be removed in 2.0. Use "mirror_file" action instead', E_USER_DEPRECATED); - - $this->filesystemAction = $filesystemAction; - } - - /** - * {@inheritdoc} - */ - public function handle(array $config, CertificateResponse $response) - { - $config['adapter'] = 'sftp'; - - $this->filesystemAction->handle($config, $response); - } -} diff --git a/src/Cli/Exception/AcmeDnsResolutionException.php b/src/Cli/Exception/AcmeDnsResolutionException.php deleted file mode 100644 index 1dd14ead..00000000 --- a/src/Cli/Exception/AcmeDnsResolutionException.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\Exception; - -@trigger_error(sprintf('The "%s" class is deprecated since version 1.1 and will be removed in 2.0., use %s instead.', AcmeDnsResolutionException::class, \AcmePhp\Core\Exception\AcmeDnsResolutionException::class), E_USER_DEPRECATED); - -/** - * @author Jérémy Derussé - */ -class AcmeDnsResolutionException extends AcmeCliException -{ - public function __construct($message, \Exception $previous = null) - { - parent::__construct(null === $message ? 'An exception was thrown during resolution of DNS' : $message, $previous); - } -} diff --git a/src/Cli/Resources/services.xml b/src/Cli/Resources/services.xml index 0cff0233..15e3b5b9 100644 --- a/src/Cli/Resources/services.xml +++ b/src/Cli/Resources/services.xml @@ -87,14 +87,6 @@ - - - - - - - - diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index 3d3070d4..663bc2de 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -87,21 +87,6 @@ public function __construct( $this->errorHandler = $errorHandler; } - /** - * Send a request encoded in the format defined by the ACME protocol. - * - * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code - * @throws AcmeCoreClientException when an error occured during response parsing - * - * @return array|string Array of parsed JSON if $returnJson = true, string otherwise - */ - public function signedRequest(string $method, string $endpoint, array $payload = [], $returnJson = true) - { - @trigger_error('The method signedRequest is deprecated since version 1.1 and will be removed in 2.0. use methods request, signKidPayload instead', E_USER_DEPRECATED); - - return $this->request($method, $endpoint, $this->signJwkPayload($endpoint, $payload), $returnJson); - } - public function getJWK(): array { $privateKey = $this->accountKeyPair->getPrivateKey(); @@ -167,37 +152,6 @@ public function signJwkPayload(string $endpoint, array $payload = null): array ); } - /** - * Send a request encoded in the format defined by the ACME protocol. - * - * @throws AcmeCoreClientException when an error occured during response parsing - * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code - * - * @return array|string Array of parsed JSON if $returnJson = true, string otherwise - */ - public function signedKidRequest(string $method, string $endpoint, string $account, array $payload = [], bool $returnJson = true) - { - @trigger_error('The method signedKidRequest is deprecated since version 1.1 and will be removed in 2.0. use methods request, signKidPayload instead.', E_USER_DEPRECATED); - - return $this->request($method, $endpoint, $this->signKidPayload($endpoint, $account, $payload), $returnJson); - } - - /** - * Send a request encoded in the format defined by the ACME protocol. - * - * @throws AcmeCoreClientException when an error occured during response parsing - * @throws ExpectedJsonException when $returnJson = true and the response is not valid JSON - * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code - * - * @return array|string Array of parsed JSON if $returnJson = true, string otherwise - */ - public function unsignedRequest(string $method, string $endpoint, array $data = null, bool $returnJson = true) - { - @trigger_error('The method unsignedRequest is deprecated since version 1.1 and will be removed in 2.0. use methods request instead.', E_USER_DEPRECATED); - - return $this->request($method, $endpoint, $data, $returnJson); - } - /** * Send a request encoded in the format defined by the ACME protocol. * From 5ef66813107e85932c8a38e154da4c4c53570be8 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 7 Nov 2020 19:27:37 +0100 Subject: [PATCH 024/121] Migrate to Github Actions --- .github/workflows/test-build.yaml | 30 ++++++++++++- .travis.yml | 70 ------------------------------- composer.json | 2 +- 3 files changed, 30 insertions(+), 72 deletions(-) delete mode 100644 .travis.yml diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 4164a9ee..aa61c80e 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -15,7 +15,7 @@ jobs: wget https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v2.16.4/php-cs-fixer.phar -q php php-cs-fixer.phar fix --dry-run --diff - tests: + tests-php72-deps-low: runs-on: ubuntu-latest steps: - uses: shivammathur/setup-php@v2 @@ -30,3 +30,31 @@ jobs: run: ./tests/setup.sh - name: Running tests run: ./tests/run.sh + + tests-php73-deps-lock: + runs-on: ubuntu-latest + steps: + - uses: shivammathur/setup-php@v2 + with: + php-version: '7.3' + - uses: actions/checkout@master + - name: Install dependencies + run: composer install + - name: Preparing tests + run: ./tests/setup.sh + - name: Running tests + run: ./tests/run.sh + + tests-php74-deps-high: + runs-on: ubuntu-latest + steps: + - uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + - uses: actions/checkout@master + - name: Install dependencies + run: composer update --no-interaction --no-progress --ansi --prefer-stable + - name: Preparing tests + run: ./tests/setup.sh + - name: Running tests + run: ./tests/run.sh diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index acdc15c5..00000000 --- a/.travis.yml +++ /dev/null @@ -1,70 +0,0 @@ -sudo: false - -language: php - -branches: - only: - - master - -notifications: - email: false - -.template_phpunit: &phpunit - stage: test - services: - - docker - before_install: - - stty cols 120 - - phpenv config-rm xdebug.ini || echo "xdebug not available" - - ./tests/setup.sh - install: composer install --no-interaction --no-progress --ansi - script: ./tests/run.sh - cache: - directories: - - $HOME/.composer/cache/files - -.template_phpunit_low: &phpunit_low - <<: *phpunit - install: - - composer require --dev "sebastian/comparator:^2.0" - - composer update --no-interaction --no-progress --ansi --prefer-lowest --prefer-stable - env: - COMMENT: "low deps" - -.template_phpunit_high: &phpunit_high - <<: *phpunit - install: composer update --no-interaction --no-progress --ansi - env: - COMMENT: "high deps" - -jobs: - include: - - stage: coding-style - php: 7.2 - install: - - wget https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v2.16.4/php-cs-fixer.phar -q - script: - - php php-cs-fixer.phar fix --dry-run --diff - - - <<: *phpunit_low - php: 7.2 - - <<: *phpunit - php: 7.3 - - <<: *phpunit_high - php: 7.4 - - - stage: split - php: 7.4 - cache: - directories: - - $HOME/.gitsplit/cache - if: branch = master AND fork = false - install: - - docker pull jderusse/gitsplit:2.0 - - git config remote.origin.fetch "+refs/*:refs/*" - - git config remote.origin.mirror true - - git fetch --unshallow || true - env: - COMMENT: "split repo" - script: - - docker run --rm -t -e GH_TOKEN -v "$HOME/.gitsplit/cache":/cache/gitsplit -v ${PWD}:/srv jderusse/gitsplit:2.0 gitsplit --ref "${TRAVIS_BRANCH}"; diff --git a/composer.json b/composer.json index 2e601379..8d1dae3f 100644 --- a/composer.json +++ b/composer.json @@ -85,7 +85,7 @@ }, "config": { "platform": { - "php": "7.2.5" + "php": "7.2.34" } } } From 518301978997acfc6815cd0a5d817695bc403527 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 7 Nov 2020 19:48:24 +0100 Subject: [PATCH 025/121] Remove legacy tests --- tests/Core/Http/SecureHttpClientTest.php | 111 ----------------------- 1 file changed, 111 deletions(-) diff --git a/tests/Core/Http/SecureHttpClientTest.php b/tests/Core/Http/SecureHttpClientTest.php index 7d20abf2..649bc9da 100644 --- a/tests/Core/Http/SecureHttpClientTest.php +++ b/tests/Core/Http/SecureHttpClientTest.php @@ -142,9 +142,6 @@ public function testInvalidJsonRequest() $client->request('GET', '/foo', ['foo' => 'bar'], true); } - /** - * @group legacy - */ public function testRequestPayload() { $container = []; @@ -189,112 +186,4 @@ public function testRequestPayload() $this->assertArrayHasKey('signature', $payload); $this->assertEquals('Zm9vYmFy', $payload['signature']); } - - /** - * @group legacy - */ - public function testValidUnsignedStringRequest() - { - $client = $this->createMockedClient([new Response(200, [], 'foo')], false); - $body = $client->unsignedRequest('GET', '/foo', ['foo' => 'bar'], false); - $this->assertEquals('foo', $body); - } - - /** - * @group legacy - */ - public function testValidUnsignedJsonRequest() - { - $client = $this->createMockedClient([new Response(200, [], json_encode(['test' => 'ok']))], false); - $data = $client->unsignedRequest('GET', '/foo', ['foo' => 'bar'], true); - $this->assertEquals(['test' => 'ok'], $data); - } - - /** - * @group legacy - */ - public function testInvalidUnsignedJsonRequest() - { - $this->expectException('AcmePhp\Core\Exception\Protocol\ExpectedJsonException'); - $client = $this->createMockedClient([new Response(200, [], 'invalid json')], false); - $client->unsignedRequest('GET', '/foo', ['foo' => 'bar'], true); - } - - /** - * @group legacy - */ - public function testValidSignedStringRequest() - { - $client = $this->createMockedClient([new Response(200, [], 'foo')], false); - $body = $client->signedRequest('GET', '/foo', ['foo' => 'bar'], false); - $this->assertEquals('foo', $body); - } - - /** - * @group legacy - */ - public function testValidSignedJsonRequest() - { - $client = $this->createMockedClient([new Response(200, [], json_encode(['test' => 'ok']))], false); - $data = $client->signedRequest('GET', '/foo', ['foo' => 'bar'], true); - $this->assertEquals(['test' => 'ok'], $data); - } - - /** - * @group legacy - */ - public function testInvalidSignedJsonRequest() - { - $this->expectException('AcmePhp\Core\Exception\Protocol\ExpectedJsonException'); - $client = $this->createMockedClient([new Response(200, [], 'invalid json')], false); - $client->signedRequest('GET', '/foo', ['foo' => 'bar'], true); - } - - /** - * @group legacy - */ - public function testSignedRequestPayload() - { - $container = []; - - $stack = HandlerStack::create(new MockHandler([new Response(200, [], json_encode(['test' => 'ok']))])); - $stack->push(Middleware::history($container)); - - $keyPairGenerator = new KeyPairGenerator(); - - $dataSigner = $this->getMockBuilder(DataSigner::class)->getMock(); - $dataSigner->expects($this->once()) - ->method('signData') - ->willReturn('foobar'); - - $client = new SecureHttpClient( - $keyPairGenerator->generateKeyPair(), - new Client(['handler' => $stack]), - new Base64SafeEncoder(), - new KeyParser(), - $dataSigner, - $this->getMockBuilder(ServerErrorHandler::class)->getMock() - ); - - $client->signedRequest('POST', '/acme/new-reg', ['contact' => 'foo@bar.com'], true); - - // Check request object - $this->assertCount(1, $container); - - /** @var RequestInterface $request */ - $request = $container[0]['request']; - - $this->assertInstanceOf(RequestInterface::class, $request); - $this->assertEquals('POST', $request->getMethod()); - $this->assertEquals('/acme/new-reg', ($request->getUri() instanceof Uri) ? $request->getUri()->getPath() : $request->getUri()); - - $body = \GuzzleHttp\Psr7\copy_to_string($request->getBody()); - $payload = @json_decode($body, true); - - $this->assertIsArray($payload); - $this->assertArrayHasKey('protected', $payload); - $this->assertArrayHasKey('payload', $payload); - $this->assertArrayHasKey('signature', $payload); - $this->assertEquals('Zm9vYmFy', $payload['signature']); - } } From 701b864d0a8a783422c4535d8ef371cefbc7272f Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 7 Nov 2020 19:50:52 +0100 Subject: [PATCH 026/121] Bump minimum version of Symfony --- composer.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 8d1dae3f..a506c49e 100644 --- a/composer.json +++ b/composer.json @@ -53,12 +53,12 @@ "psr/http-message": "^1.0", "psr/log": "^1.0", "swiftmailer/swiftmailer": "^5.4|^6.0", - "symfony/config": "^4.4|^5.0", - "symfony/console": "^4.4|^5.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/filesystem": "^4.4|^5.0", - "symfony/serializer": "^4.4|^5.0", - "symfony/yaml": "^4.4|^5.0", + "symfony/config": "^5.0", + "symfony/console": "^5.0", + "symfony/dependency-injection": "^5.0", + "symfony/filesystem": "^5.0", + "symfony/serializer": "^5.0", + "symfony/yaml": "^5.0", "webmozart/assert": "^1.0", "webmozart/path-util": "^2.3", "alibabacloud/cdn": "^1.7", @@ -69,9 +69,9 @@ }, "require-dev": { "phpspec/prophecy": "^1.9", - "symfony/finder": "^4.4|^5.0", + "symfony/finder": "^5.0", "symfony/phpunit-bridge": "^5.0", - "symfony/var-dumper": "^4.4|^5.0" + "symfony/var-dumper": "^5.0" }, "autoload": { "psr-4": { From 7fa7a2acbd072fa450d5ae643c71656b95bffed9 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 7 Nov 2020 20:40:03 +0100 Subject: [PATCH 027/121] Add EAB test --- .github/workflows/test-build.yaml | 8 +++++--- tests/Fixtures/pebble-config-default.json | 12 ++++++++++++ tests/Fixtures/pebble-config-eab.json | 12 ++++++++++++ tests/setup.sh | 4 +++- 4 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 tests/Fixtures/pebble-config-default.json create mode 100644 tests/Fixtures/pebble-config-eab.json diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index aa61c80e..12569935 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -15,7 +15,7 @@ jobs: wget https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v2.16.4/php-cs-fixer.phar -q php php-cs-fixer.phar fix --dry-run --diff - tests-php72-deps-low: + tests-php72-deps-low-pebble-default: runs-on: ubuntu-latest steps: - uses: shivammathur/setup-php@v2 @@ -31,7 +31,7 @@ jobs: - name: Running tests run: ./tests/run.sh - tests-php73-deps-lock: + tests-php73-deps-lock-pebble-default: runs-on: ubuntu-latest steps: - uses: shivammathur/setup-php@v2 @@ -45,8 +45,10 @@ jobs: - name: Running tests run: ./tests/run.sh - tests-php74-deps-high: + tests-php74-deps-high-pebble-eab: runs-on: ubuntu-latest + env: + PEBBLE_MODE: eab steps: - uses: shivammathur/setup-php@v2 with: diff --git a/tests/Fixtures/pebble-config-default.json b/tests/Fixtures/pebble-config-default.json new file mode 100644 index 00000000..1127939e --- /dev/null +++ b/tests/Fixtures/pebble-config-default.json @@ -0,0 +1,12 @@ +{ + "pebble": { + "listenAddress": "0.0.0.0:14000", + "managementListenAddress": "0.0.0.0:15000", + "certificate": "test/certs/localhost/cert.pem", + "privateKey": "test/certs/localhost/key.pem", + "httpPort": 5002, + "tlsPort": 5001, + "ocspResponderURL": "", + "externalAccountBindingRequired": false + } +} diff --git a/tests/Fixtures/pebble-config-eab.json b/tests/Fixtures/pebble-config-eab.json new file mode 100644 index 00000000..7f635fc8 --- /dev/null +++ b/tests/Fixtures/pebble-config-eab.json @@ -0,0 +1,12 @@ +{ + "pebble": { + "listenAddress": "0.0.0.0:14000", + "managementListenAddress": "0.0.0.0:15000", + "certificate": "test/certs/localhost/cert.pem", + "privateKey": "test/certs/localhost/key.pem", + "httpPort": 5002, + "tlsPort": 5001, + "ocspResponderURL": "", + "externalAccountBindingRequired": true + } +} diff --git a/tests/setup.sh b/tests/setup.sh index 5ed674aa..904148e8 100755 --- a/tests/setup.sh +++ b/tests/setup.sh @@ -8,8 +8,10 @@ cd .. docker run -d --name acme_sftp -p 8022:22 atmoz/sftp acmephp:acmephp:::share # pebble +MODE=${PEBBLE_MODE:-default} + docker run -d --name acme_server --net host letsencrypt/pebble-challtestsrv pebble-challtestsrv -defaultIPv6 "" -defaultIPv4 127.0.0.1 -docker run -d --name acme_pebble --net host -e PEBBLE_VA_NOSLEEP=1 -e PEBBLE_WFE_NONCEREJECT=0 letsencrypt/pebble pebble -dnsserver 127.0.0.1:8053 +docker run -d --name acme_pebble --net host -e PEBBLE_VA_NOSLEEP=1 -e PEBBLE_WFE_NONCEREJECT=0 -v $(pwd)/tests/Fixtures/pebble-config-$MODE.json:/test/config/pebble-config.json letsencrypt/pebble pebble -dnsserver 127.0.0.1:8053 # Wait for boot to be completed docker run --rm --net host martin/wait -c localhost:14000,localhost:8022,localhost:8053,localhost:5002 -t 120 From 12533bab92309db71e7fca90dc8f6b79d1fbd767 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 7 Nov 2020 20:52:03 +0100 Subject: [PATCH 028/121] Add EAB structure --- src/Cli/Application.php | 25 ++----- src/Cli/Command/AbstractCommand.php | 4 +- src/Cli/Command/RevokeCommand.php | 14 +++- src/Cli/Command/RunCommand.php | 69 ++++++++++++++----- src/Cli/Configuration/DomainConfiguration.php | 15 ++++ src/Core/AcmeClient.php | 24 ++++--- src/Core/AcmeClientInterface.php | 7 +- src/Core/Http/SecureHttpClient.php | 19 ++--- src/Core/Protocol/ExternalAccount.php | 42 +++++++++++ src/Ssl/Generator/EcKey/EcKeyOption.php | 4 -- tests/Cli/AbstractApplicationTest.php | 1 - tests/Core/AcmeClientTest.php | 30 ++++---- 12 files changed, 177 insertions(+), 77 deletions(-) create mode 100644 src/Core/Protocol/ExternalAccount.php diff --git a/src/Cli/Application.php b/src/Cli/Application.php index dd10c4d1..d14ed45a 100644 --- a/src/Cli/Application.php +++ b/src/Cli/Application.php @@ -17,7 +17,6 @@ use AcmePhp\Cli\Command\SelfUpdateCommand; use AcmePhp\Cli\Command\StatusCommand; use Symfony\Component\Console\Application as BaseApplication; -use Symfony\Component\Console\Input\InputOption; use Webmozart\PathUtil\Path; /** @@ -25,6 +24,12 @@ */ class Application extends BaseApplication { + public const PROVIDERS = [ + 'letsencrypt' => 'https://acme-v02.api.letsencrypt.org/directory', + 'zerossl' => 'https://acme.zerossl.com/v2/DV90', + 'localhost' => 'https://localhost:14000/dir', + ]; + /** * {@inheritdoc} */ @@ -57,24 +62,6 @@ protected function getDefaultHelperSet() return $set; } - /** - * {@inheritdoc} - */ - protected function getDefaultInputDefinition() - { - $definition = parent::getDefaultInputDefinition(); - - $definition->addOption(new InputOption( - 'server', - null, - InputOption::VALUE_REQUIRED, - 'Set the ACME server directory to use', - 'https://acme-v02.api.letsencrypt.org/directory' - )); - - return $definition; - } - /** * @return string */ diff --git a/src/Cli/Command/AbstractCommand.php b/src/Cli/Command/AbstractCommand.php index 4382dd99..35162886 100644 --- a/src/Cli/Command/AbstractCommand.php +++ b/src/Cli/Command/AbstractCommand.php @@ -63,7 +63,7 @@ protected function getRepository(): RepositoryInterface return $this->getContainer()->get('repository'); } - protected function getClient(): AcmeClient + protected function getClient(string $directoryUrl): AcmeClient { $this->debug('Creating Acme client'); $this->notice('Loading account key pair...'); @@ -79,7 +79,7 @@ protected function getClient(): AcmeClient /** @var CertificateRequestSigner $csrSigner */ $csrSigner = $this->getContainer()->get('ssl.csr_signer'); - return new AcmeClient($httpClient, $this->input->getOption('server'), $csrSigner); + return new AcmeClient($httpClient, $directoryUrl, $csrSigner); } protected function getCliLogger(): LoggerInterface diff --git a/src/Cli/Command/RevokeCommand.php b/src/Cli/Command/RevokeCommand.php index 0a6db097..29f85def 100644 --- a/src/Cli/Command/RevokeCommand.php +++ b/src/Cli/Command/RevokeCommand.php @@ -11,6 +11,7 @@ namespace AcmePhp\Cli\Command; +use AcmePhp\Cli\Application; use AcmePhp\Core\Exception\Protocol\CertificateRevocationException; use AcmePhp\Core\Protocol\RevocationReason; use Symfony\Component\Console\Input\InputArgument; @@ -31,6 +32,13 @@ protected function configure() ->setDefinition([ new InputArgument('domain', InputArgument::REQUIRED, 'The domain revoke a certificate for'), new InputArgument('reason-code', InputOption::VALUE_OPTIONAL, 'The reason code for revocation:'.PHP_EOL.$reasons), + new InputOption( + 'provider', + null, + InputOption::VALUE_REQUIRED, + 'Certificate provider to use (supported: '.implode(', ', Application::PROVIDERS).')', + 'letsencrypt' + ), ]) ->setDescription('Revoke a SSL certificate for a domain') ->setHelp('The %command.name% command revoke a previously obtained certificate for a given domain'); @@ -41,8 +49,12 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { + if (!isset(Application::PROVIDERS[$this->input->getOption('provider')])) { + throw new \InvalidArgumentException('Invalid provider, supported: '.implode(', ', Application::PROVIDERS)); + } + $repository = $this->getRepository(); - $client = $this->getClient(); + $client = $this->getClient(Application::PROVIDERS[$this->input->getOption('provider')]); $domain = (string) $input->getArgument('domain'); $reasonCode = $input->getArgument('reason-code'); // ok to be null. LE expects 0 as default reason diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index b3cd6493..b80136ba 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -43,6 +43,8 @@ class RunCommand extends AbstractCommand { use KeyOptionCommandTrait; + private $config; + /** * {@inheritdoc} */ @@ -61,12 +63,8 @@ protected function configure() ), ] ) - ->setDescription('Automaticaly chalenge domain and request certificates configured in the given file') - ->setHelp( - <<<'EOF' - The %command.name% challenge the domains, request the certificates and install them following a given configuration. -EOF - ); + ->setDescription('Automatically challenge domains and request certificates configured in the given file') + ->setHelp('The %command.name% challenge the domains, request the certificates and install them following a given configuration.'); } /** @@ -74,12 +72,12 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $config = $this->getConfig(Path::makeAbsolute($input->getArgument('config'), getcwd())); - $keyOption = $this->createKeyOption($config['key_type']); + $this->config = $this->getConfig(Path::makeAbsolute($input->getArgument('config'), getcwd())); - $this->register($config['contact_email'], $keyOption); + $keyOption = $this->createKeyOption($this->config['key_type']); + $this->register($this->config['contact_email'], $keyOption); - foreach ($config['certificates'] as $domainConfig) { + foreach ($this->config['certificates'] as $domainConfig) { $domain = $domainConfig['domain']; if ($this->isUpToDate($domain, $domainConfig, (int) $input->getOption('delay'))) { @@ -126,17 +124,56 @@ private function register($email, KeyOption $keyOption) $repository->storeAccountKeyPair($accountKeyPair); } - $client = $this->getClient(); + $client = $this->getClient(self::PROVIDERS[$this->config['provider']]); $this->output->writeln('Registering on the ACME server...'); try { - $client->registerAccount(null, $email); + $client->registerAccount($email, $this->resolveEabKid()); $this->output->writeln('Account registered successfully!'); } catch (MalformedServerException $e) { $this->output->writeln('Account already registered!'); } } + private function resolveEabKid(): ?string + { + if ('zerossl' !== $this->config['provider']) { + return null; + } + + // If an API is provided, use it + if ($this->config['zerossl_api_key']) { + $eabCredentials = \GuzzleHttp\json_decode( + (new Client()) + ->post('https://api.zerossl.com/acme/eab-credentials/?access_key='.$this->config['zerossl_api_key']) + ->getBody() + ->getContents() + ); + + if (!isset($eabCredentials->success) || !$eabCredentials->success) { + throw new AcmeCliException('ZeroSSL External account Binding failed: are you sure your API key is valid?'); + } + + return $eabCredentials->eab_kid; + } + + // Otherwise register on the fly + $eabCredentials = \GuzzleHttp\json_decode( + (new Client()) + ->post('https://api.zerossl.com/acme/eab-credentials-email', [ + 'form_params' => ['email' => $this->config['contact_email']], + ]) + ->getBody() + ->getContents() + ); + + if (!isset($eabCredentials->success) || !$eabCredentials->success) { + throw new AcmeCliException('ZeroSSL External account Binding failed: registering your email failed.'); + } + + return $eabCredentials->eab_kid; + } + private function installCertificate(CertificateResponse $response, array $actions) { $this->output->writeln( @@ -200,7 +237,7 @@ private function requestCertificate(CertificateOrder $order, $domainConfig, KeyO $this->output->writeln(sprintf('Requesting certificate for domain %s...', $domain)); $repository = $this->getRepository(); - $client = $this->getClient(); + $client = $this->getClient(self::PROVIDERS[$this->config['provider']]); $distinguishedName = new DistinguishedName( $domainConfig['domain'], $domainConfig['distinguished_name']['country'], @@ -246,7 +283,7 @@ private function challengeDomains(array $domainConfig) /** @var ValidatorInterface $validator */ $validator = $this->getContainer()->get('challenge_validator'); - $client = $this->getClient(); + $client = $this->getClient(self::PROVIDERS[$this->config['provider']]); $domains = array_unique(array_merge([$domain], $domainConfig['subject_alternative_names'])); $this->output->writeln('Requesting certificate order...'); @@ -314,9 +351,7 @@ private function challengeDomains(array $domainConfig) private function getConfig($configFile) { - return $this->resolveConfig( - $this->loadConfig($configFile) - ); + return $this->resolveConfig($this->loadConfig($configFile)); } private function loadConfig($configFile) diff --git a/src/Cli/Configuration/DomainConfiguration.php b/src/Cli/Configuration/DomainConfiguration.php index 717551ce..4899e821 100644 --- a/src/Cli/Configuration/DomainConfiguration.php +++ b/src/Cli/Configuration/DomainConfiguration.php @@ -11,6 +11,7 @@ namespace AcmePhp\Cli\Configuration; +use AcmePhp\Cli\Application; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; @@ -56,6 +57,20 @@ public function getConfigTreeBuilder() ->thenInvalid('The email %s is not valid.') ->end() ->end() + ->scalarNode('provider') + ->info('Certificate provider to use (supported: '.implode(', ', Application::PROVIDERS).')') + ->defaultValue('letsencrypt') + ->validate() + ->ifTrue(function ($item) { + return !isset(Application::PROVIDERS[$item]); + }) + ->thenInvalid('The certificate provider %s is not valid (supported: '.implode(', ', Application::PROVIDERS).').') + ->end() + ->end() + ->scalarNode('zerossl_api_key') + ->info('ZeroSSL API key to use if you already have one (one will be created automatically otherwise)') + ->defaultNull() + ->end() ->scalarNode('key_type') ->info('Type of private key (RSA or EC).') ->defaultValue('RSA') diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index a57f5163..88665bd1 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -21,6 +21,7 @@ use AcmePhp\Core\Http\SecureHttpClient; use AcmePhp\Core\Protocol\AuthorizationChallenge; use AcmePhp\Core\Protocol\CertificateOrder; +use AcmePhp\Core\Protocol\ExternalAccount; use AcmePhp\Core\Protocol\ResourcesDirectory; use AcmePhp\Core\Protocol\RevocationReason; use AcmePhp\Ssl\Certificate; @@ -76,23 +77,31 @@ public function __construct(SecureHttpClient $httpClient, string $directoryUrl, /** * {@inheritdoc} */ - public function registerAccount(string $agreement = null, string $email = null): array + public function registerAccount(string $email = null, ExternalAccount $externalAccount = null): array { - Assert::nullOrString($agreement, 'registerAccount::$agreement expected a string or null. Got: %s'); - Assert::nullOrString($email, 'registerAccount::$email expected a string or null. Got: %s'); + $client = $this->getHttpClient(); $payload = [ 'termsOfServiceAgreed' => true, 'contact' => [], ]; - if (\is_string($email)) { + if ($email) { $payload['contact'][] = 'mailto:'.$email; } + /* + if ($externalAccount) { + $payload['externalAccountBinding'] = $client->signKidPayload( + $this->getResourceUrl(ResourcesDirectory::NEW_ACCOUNT), + $eabKid, + null + ); + } + */ + $this->requestResource('POST', ResourcesDirectory::NEW_ACCOUNT, $payload); $account = $this->getResourceAccount(); - $client = $this->getHttpClient(); return $client->request('POST', $account, $client->signKidPayload($account, $account, null)); } @@ -141,8 +150,6 @@ static function ($domain) { */ public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, int $timeout = 180): CertificateResponse { - Assert::integer($timeout, 'finalizeOrder::$timeout expected an integer. Got: %s'); - $endTime = time() + $timeout; $client = $this->getHttpClient(); $orderEndpoint = $order->getOrderEndpoint(); @@ -207,8 +214,6 @@ public function reloadAuthorization(AuthorizationChallenge $challenge): Authoriz */ public function challengeAuthorization(AuthorizationChallenge $challenge, int $timeout = 180): array { - Assert::integer($timeout, 'challengeAuthorization::$timeout expected an integer. Got: %s'); - $endTime = time() + $timeout; $client = $this->getHttpClient(); $challengeUrl = $challenge->getUrl(); @@ -239,7 +244,6 @@ public function challengeAuthorization(AuthorizationChallenge $challenge, int $t public function requestCertificate(string $domain, CertificateRequest $csr, int $timeout = 180): CertificateResponse { Assert::stringNotEmpty($domain, 'requestCertificate::$domain expected a non-empty string. Got: %s'); - Assert::integer($timeout, 'requestCertificate::$timeout expected an integer. Got: %s'); $order = $this->requestOrder(array_unique(array_merge([$domain], $csr->getDistinguishedName()->getSubjectAlternativeNames()))); diff --git a/src/Core/AcmeClientInterface.php b/src/Core/AcmeClientInterface.php index 23b21535..2739912d 100644 --- a/src/Core/AcmeClientInterface.php +++ b/src/Core/AcmeClientInterface.php @@ -21,6 +21,7 @@ use AcmePhp\Core\Exception\Protocol\ChallengeTimedOutException; use AcmePhp\Core\Protocol\AuthorizationChallenge; use AcmePhp\Core\Protocol\CertificateOrder; +use AcmePhp\Core\Protocol\ExternalAccount; use AcmePhp\Core\Protocol\RevocationReason; use AcmePhp\Ssl\Certificate; use AcmePhp\Ssl\CertificateRequest; @@ -36,8 +37,8 @@ interface AcmeClientInterface /** * Register the local account KeyPair in the Certificate Authority. * - * @param string|null $agreement an optionnal URI referring to a subscriber agreement or terms of service - * @param string|null $email an optionnal e-mail to associate with the account + * @param string|null $email an optionnal e-mail to associate with the account + * @param ExternalAccount|null $externalAccount an optionnal External Account to use for External Account Binding * * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * (the exception will be more specific if detail is provided) @@ -45,7 +46,7 @@ interface AcmeClientInterface * * @return array the Certificate Authority response decoded from JSON into an array */ - public function registerAccount(string $agreement = null, string $email = null): array; + public function registerAccount(string $email = null, ExternalAccount $externalAccount = null): array; /** * Request authorization challenge data for a list of domains. diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index 663bc2de..eeb9afe7 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -252,31 +252,34 @@ private function signPayload(array $protected, array $payload = null): array if (!isset($protected['alg'])) { throw new \InvalidArgumentException('The property "alg" is required in the protected array'); } + $alg = $protected['alg']; $privateKey = $this->accountKeyPair->getPrivateKey(); list($algorithm, $format) = $this->extractSignOptionFromJWSAlg($alg); - $protected = $this->base64Encoder->encode(json_encode($protected, JSON_UNESCAPED_SLASHES)); + $encodedProtected = $this->base64Encoder->encode(json_encode($protected, JSON_UNESCAPED_SLASHES)); + if (null === $payload) { - $payload = ''; + $encodedPayload = ''; } elseif ([] === $payload) { - $payload = $this->base64Encoder->encode('{}'); + $encodedPayload = $this->base64Encoder->encode('{}'); } else { - $payload = $this->base64Encoder->encode(json_encode($payload, JSON_UNESCAPED_SLASHES)); + $encodedPayload = $this->base64Encoder->encode(json_encode($payload, JSON_UNESCAPED_SLASHES)); } + $signature = $this->base64Encoder->encode( - $this->dataSigner->signData($protected.'.'.$payload, $privateKey, $algorithm, $format) + $this->dataSigner->signData($encodedProtected.'.'.$encodedPayload, $privateKey, $algorithm, $format) ); return [ - 'protected' => $protected, - 'payload' => $payload, + 'protected' => $encodedProtected, + 'payload' => $encodedPayload, 'signature' => $signature, ]; } - private function createRequest($method, $endpoint, $data): Request + private function createRequest($method, $endpoint, $data) { $request = new Request($method, $endpoint); $request = $request->withHeader('Accept', 'application/json,application/jose+json,'); diff --git a/src/Core/Protocol/ExternalAccount.php b/src/Core/Protocol/ExternalAccount.php new file mode 100644 index 00000000..eb70f96e --- /dev/null +++ b/src/Core/Protocol/ExternalAccount.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Core\Protocol; + +/** + * Represent an ACME External Account to be used for External Account Binding. + * + * @author Titouan Galopin + */ +class ExternalAccount +{ + /** @var string */ + private $id; + + /** @var string */ + private $hmacKey; + + public function __construct(string $id, string $hmacKey) + { + $this->id = $id; + $this->hmacKey = $hmacKey; + } + + public function getId(): string + { + return $this->id; + } + + public function getHmacKey(): string + { + return $this->hmacKey; + } +} diff --git a/src/Ssl/Generator/EcKey/EcKeyOption.php b/src/Ssl/Generator/EcKey/EcKeyOption.php index 3f564ca1..c2747415 100644 --- a/src/Ssl/Generator/EcKey/EcKeyOption.php +++ b/src/Ssl/Generator/EcKey/EcKeyOption.php @@ -21,10 +21,6 @@ class EcKeyOption implements KeyOption public function __construct(string $curveName = 'secp384r1') { - if (\PHP_VERSION_ID < 70100) { - throw new \LogicException('The generation of ECDSA requires a version of PHP >= 7.1'); - } - Assert::oneOf($curveName, openssl_get_curve_names(), 'The given curve %s is not supported. Available curves are: %s'); $this->curveName = $curveName; diff --git a/tests/Cli/AbstractApplicationTest.php b/tests/Cli/AbstractApplicationTest.php index 622817f6..800db41f 100644 --- a/tests/Cli/AbstractApplicationTest.php +++ b/tests/Cli/AbstractApplicationTest.php @@ -46,7 +46,6 @@ public function testFullProcess() $runTester->execute([ 'command' => 'run', 'config' => $this->getConfigFile(), - '--server' => 'https://localhost:14000/dir', ]); $output = $runTester->getDisplay(); diff --git a/tests/Core/AcmeClientTest.php b/tests/Core/AcmeClientTest.php index 8a0f6bcf..e5d1b619 100644 --- a/tests/Core/AcmeClientTest.php +++ b/tests/Core/AcmeClientTest.php @@ -18,6 +18,7 @@ use AcmePhp\Core\Http\SecureHttpClient; use AcmePhp\Core\Http\ServerErrorHandler; use AcmePhp\Core\Protocol\AuthorizationChallenge; +use AcmePhp\Core\Protocol\ExternalAccount; use AcmePhp\Ssl\Certificate; use AcmePhp\Ssl\CertificateRequest; use AcmePhp\Ssl\CertificateResponse; @@ -32,8 +33,16 @@ class AcmeClientTest extends AbstractFunctionnalTest { + public function provideKeyOptions() + { + yield 'rsa1024' => [new RsaKeyOption(1024)]; + yield 'rsa4098' => [new RsaKeyOption(4098)]; + yield 'ecprime256v1' => [new EcKeyOption('prime256v1')]; + yield 'ecsecp384r1' => [new EcKeyOption('secp384r1')]; + } + /** - * @dataProvider getKeyOptions + * @dataProvider provideKeyOptions */ public function testFullProcess(KeyOption $keyOption) { @@ -51,7 +60,14 @@ public function testFullProcess(KeyOption $keyOption) /* * Register account */ - $data = $client->registerAccount(); + if ('eab' === getenv('PEBBLE_MODE')) { + $data = $client->registerAccount('titouan.galopin@acmephp.io', new ExternalAccount( + 'ofX3Rmny4aAD3bwTBZnhJw', + 'pfVgkYJ-mbNLtmVjpgYeH_s_VYv30MjTmqYXrh3HG0syrG-JtNV3SOQedzV-rPY314qY8v3b7Sc443e65nBLYw' + )); + } else { + $data = $client->registerAccount(); + } $this->assertIsArray($data); $this->assertArrayHasKey('key', $data); @@ -107,14 +123,4 @@ public function testFullProcess(KeyOption $keyOption) $this->assertStringContainsString('Unable to find specified certificate', $e->getPrevious()->getPrevious()->getMessage()); } } - - public function getKeyOptions() - { - yield [new RsaKeyOption(1024)]; - yield [new RsaKeyOption(4098)]; - if (\PHP_VERSION_ID >= 70100) { - yield [new EcKeyOption('prime256v1')]; - yield [new EcKeyOption('secp384r1')]; - } - } } From 8f10906088b6a29e0a5c53107f48a7e30c78b334 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 7 Nov 2020 21:06:11 +0100 Subject: [PATCH 029/121] Adapt tests --- src/Cli/Command/RunCommand.php | 12 +++++++++--- tests/Cli/Fixtures/config_sfpt_nginxproxy.yaml | 1 + tests/Cli/Fixtures/config_simple.yaml | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index b80136ba..9a7d803f 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -11,8 +11,10 @@ namespace AcmePhp\Cli\Command; +use AcmePhp\Cli\Application; use AcmePhp\Cli\Command\Helper\KeyOptionCommandTrait; use AcmePhp\Cli\Configuration\DomainConfiguration; +use AcmePhp\Cli\Exception\AcmeCliException; use AcmePhp\Core\Challenge\ConfigurableServiceInterface; use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface; use AcmePhp\Core\Challenge\SolverInterface; @@ -27,11 +29,13 @@ use AcmePhp\Ssl\Generator\KeyOption; use AcmePhp\Ssl\KeyPair; use AcmePhp\Ssl\ParsedCertificate; +use GuzzleHttp\Client; use Symfony\Component\Config\Definition\Processor; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Yaml\Yaml; use Webmozart\PathUtil\Path; @@ -124,7 +128,7 @@ private function register($email, KeyOption $keyOption) $repository->storeAccountKeyPair($accountKeyPair); } - $client = $this->getClient(self::PROVIDERS[$this->config['provider']]); + $client = $this->getClient(Application::PROVIDERS[$this->config['provider']]); $this->output->writeln('Registering on the ACME server...'); try { @@ -237,7 +241,7 @@ private function requestCertificate(CertificateOrder $order, $domainConfig, KeyO $this->output->writeln(sprintf('Requesting certificate for domain %s...', $domain)); $repository = $this->getRepository(); - $client = $this->getClient(self::PROVIDERS[$this->config['provider']]); + $client = $this->getClient(Application::PROVIDERS[$this->config['provider']]); $distinguishedName = new DistinguishedName( $domainConfig['domain'], $domainConfig['distinguished_name']['country'], @@ -273,7 +277,9 @@ private function challengeDomains(array $domainConfig) $solverConfig = $domainConfig['solver']; $domain = $domainConfig['domain']; + /** @var ServiceLocator $solverLocator */ $solverLocator = $this->getContainer()->get('acmephp.challenge_solver.locator'); + /** @var SolverInterface $solver */ $solver = $solverLocator->get($solverConfig['name']); if ($solver instanceof ConfigurableServiceInterface) { @@ -283,7 +289,7 @@ private function challengeDomains(array $domainConfig) /** @var ValidatorInterface $validator */ $validator = $this->getContainer()->get('challenge_validator'); - $client = $this->getClient(self::PROVIDERS[$this->config['provider']]); + $client = $this->getClient(Application::PROVIDERS[$this->config['provider']]); $domains = array_unique(array_merge([$domain], $domainConfig['subject_alternative_names'])); $this->output->writeln('Requesting certificate order...'); diff --git a/tests/Cli/Fixtures/config_sfpt_nginxproxy.yaml b/tests/Cli/Fixtures/config_sfpt_nginxproxy.yaml index e8226e3f..7a6274a5 100644 --- a/tests/Cli/Fixtures/config_sfpt_nginxproxy.yaml +++ b/tests/Cli/Fixtures/config_sfpt_nginxproxy.yaml @@ -1,5 +1,6 @@ contact_email: foo@example.com key_type: RSA +provider: localhost defaults: distinguished_name: diff --git a/tests/Cli/Fixtures/config_simple.yaml b/tests/Cli/Fixtures/config_simple.yaml index 8ba9ae3f..b814b95c 100644 --- a/tests/Cli/Fixtures/config_simple.yaml +++ b/tests/Cli/Fixtures/config_simple.yaml @@ -1,5 +1,6 @@ contact_email: foo@example.com key_type: RSA +provider: localhost defaults: distinguished_name: From c2bf09b13a1a05752cec7f553388233298e01f4f Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 7 Nov 2020 22:11:45 +0100 Subject: [PATCH 030/121] Finalize EAB support --- composer.json | 10 ++-- src/Cli/Command/RunCommand.php | 7 +-- src/Core/AcmeClient.php | 10 ++-- src/Core/Http/SecureHttpClient.php | 67 +++++++++++++++++++-------- src/Core/composer.json | 4 ++ src/Ssl/composer.json | 3 ++ tests/Core/AcmeClientTest.php | 7 +-- tests/Fixtures/pebble-config-eab.json | 5 +- 8 files changed, 75 insertions(+), 38 deletions(-) diff --git a/composer.json b/composer.json index a506c49e..d4687458 100644 --- a/composer.json +++ b/composer.json @@ -35,15 +35,18 @@ ], "require": { "php": ">=7.2.5", + "ext-filter": "*", "ext-hash": "*", "ext-json": "*", - "ext-filter": "*", "ext-mbstring": "*", "ext-openssl": "*", "lib-openssl": ">=0.9.8", + "alibabacloud/cdn": "^1.7", + "alibabacloud/wafopenapi": "^1.7", "aws/aws-sdk-php": "^3.38", "guzzlehttp/guzzle": "^6.0|^7.0", "guzzlehttp/psr7": "^1.0", + "lcobucci/jwt": "^3.3", "league/flysystem": "^1.0.19", "league/flysystem-memory": "^1.0", "league/flysystem-sftp": "^1.0.7", @@ -60,9 +63,7 @@ "symfony/serializer": "^5.0", "symfony/yaml": "^5.0", "webmozart/assert": "^1.0", - "webmozart/path-util": "^2.3", - "alibabacloud/cdn": "^1.7", - "alibabacloud/wafopenapi": "^1.7" + "webmozart/path-util": "^2.3" }, "suggest": { "daverandom/libdns": "^2.0" @@ -84,6 +85,7 @@ } }, "config": { + "sort-packages": true, "platform": { "php": "7.2.34" } diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index 9a7d803f..cf7fbe5d 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -23,6 +23,7 @@ use AcmePhp\Core\Exception\Server\MalformedServerException; use AcmePhp\Core\Protocol\AuthorizationChallenge; use AcmePhp\Core\Protocol\CertificateOrder; +use AcmePhp\Core\Protocol\ExternalAccount; use AcmePhp\Ssl\CertificateRequest; use AcmePhp\Ssl\CertificateResponse; use AcmePhp\Ssl\DistinguishedName; @@ -139,7 +140,7 @@ private function register($email, KeyOption $keyOption) } } - private function resolveEabKid(): ?string + private function resolveEabKid(): ?ExternalAccount { if ('zerossl' !== $this->config['provider']) { return null; @@ -158,7 +159,7 @@ private function resolveEabKid(): ?string throw new AcmeCliException('ZeroSSL External account Binding failed: are you sure your API key is valid?'); } - return $eabCredentials->eab_kid; + return new ExternalAccount($eabCredentials->eab_kid, $eabCredentials->eab_hmac_key); } // Otherwise register on the fly @@ -175,7 +176,7 @@ private function resolveEabKid(): ?string throw new AcmeCliException('ZeroSSL External account Binding failed: registering your email failed.'); } - return $eabCredentials->eab_kid; + return new ExternalAccount($eabCredentials->eab_kid, $eabCredentials->eab_hmac_key); } private function installCertificate(CertificateResponse $response, array $actions) diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 88665bd1..990f9e47 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -28,6 +28,7 @@ use AcmePhp\Ssl\CertificateRequest; use AcmePhp\Ssl\CertificateResponse; use AcmePhp\Ssl\Signer\CertificateRequestSigner; +use Lcobucci\JWT\Signer\Hmac\Sha256; use Webmozart\Assert\Assert; /** @@ -90,15 +91,12 @@ public function registerAccount(string $email = null, ExternalAccount $externalA $payload['contact'][] = 'mailto:'.$email; } - /* if ($externalAccount) { - $payload['externalAccountBinding'] = $client->signKidPayload( - $this->getResourceUrl(ResourcesDirectory::NEW_ACCOUNT), - $eabKid, - null + $payload['externalAccountBinding'] = $client->createExternalAccountPayload( + $externalAccount, + $this->getResourceUrl(ResourcesDirectory::NEW_ACCOUNT) ); } - */ $this->requestResource('POST', ResourcesDirectory::NEW_ACCOUNT, $payload); $account = $this->getResourceAccount(); diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index eeb9afe7..78b88a2b 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -15,6 +15,7 @@ use AcmePhp\Core\Exception\AcmeCoreServerException; use AcmePhp\Core\Exception\Protocol\ExpectedJsonException; use AcmePhp\Core\Exception\Server\BadNonceServerException; +use AcmePhp\Core\Protocol\ExternalAccount; use AcmePhp\Core\Util\JsonDecoder; use AcmePhp\Ssl\KeyPair; use AcmePhp\Ssl\Parser\KeyParser; @@ -22,6 +23,7 @@ use GuzzleHttp\ClientInterface; use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Psr7\Request; +use Lcobucci\JWT\Signer\Hmac\Sha256; use Psr\Http\Message\ResponseInterface; /** @@ -122,34 +124,61 @@ public function getJWKThumbprint(): string /** * Generates a payload signed with account's KID. + * + * @param string|array|null $payload */ - public function signKidPayload(string $endpoint, string $account, array $payload = null): array + public function signKidPayload(string $endpoint, string $account, $payload = null, bool $withNonce = true): array { - return $this->signPayload( - [ - 'alg' => $this->getAlg(), - 'kid' => $account, - 'nonce' => $this->getNonce(), - 'url' => $endpoint, - ], - $payload - ); + $protected = ['alg' => $this->getAlg(), 'kid' => $account, 'url' => $endpoint]; + if ($withNonce) { + $protected['nonce'] = $this->getNonce(); + } + + return $this->signPayload($protected, $payload); } /** * Generates a payload signed with JWK. + * + * @param string|array|null $payload + */ + public function signJwkPayload(string $endpoint, $payload = null, bool $withNonce = true): array + { + $protected = ['alg' => $this->getAlg(), 'jwk' => $this->getJWK(), 'url' => $endpoint]; + if ($withNonce) { + $protected['nonce'] = $this->getNonce(); + } + + return $this->signPayload($protected, $payload); + } + + /** + * Generates an External Account Binding payload signed with JWS. + * + * @param string|array|null $payload */ - public function signJwkPayload(string $endpoint, array $payload = null): array + public function createExternalAccountPayload(ExternalAccount $externalAccount, string $url): array { - return $this->signPayload( - [ - 'alg' => $this->getAlg(), - 'jwk' => $this->getJWK(), - 'nonce' => $this->getNonce(), - 'url' => $endpoint, - ], - $payload + $signer = new Sha256(); + + $protected = [ + 'alg' => $signer->getAlgorithmId(), + 'kid' => $externalAccount->getId(), + 'url' => $url, + ]; + + $encodedProtected = $this->base64Encoder->encode(json_encode($protected, JSON_UNESCAPED_SLASHES)); + $encodedPayload = $this->base64Encoder->encode(json_encode($this->getJWK(), JSON_UNESCAPED_SLASHES)); + + $signature = $this->base64Encoder->encode( + (string) $signer->sign($encodedProtected.'.'.$encodedPayload, $externalAccount->getHmacKey()) ); + + return [ + 'protected' => $encodedProtected, + 'payload' => $encodedPayload, + 'signature' => $signature, + ]; } /** diff --git a/src/Core/composer.json b/src/Core/composer.json index c3204a06..211721a9 100644 --- a/src/Core/composer.json +++ b/src/Core/composer.json @@ -34,6 +34,7 @@ "acmephp/ssl": "^1.0", "guzzlehttp/guzzle": "^6.0|^7.0", "guzzlehttp/psr7": "^1.0", + "lcobucci/jwt": "^3.3", "psr/http-message": "^1.0", "psr/log": "^1.0", "webmozart/assert": "^1.0" @@ -42,5 +43,8 @@ "psr-4": { "AcmePhp\\Core\\": "." } + }, + "config": { + "sort-packages": true } } diff --git a/src/Ssl/composer.json b/src/Ssl/composer.json index e25c462c..f097d057 100644 --- a/src/Ssl/composer.json +++ b/src/Ssl/composer.json @@ -37,5 +37,8 @@ "ext-openssl": "*", "lib-openssl": ">=0.9.8", "webmozart/assert": "^1.0" + }, + "config": { + "sort-packages": true } } diff --git a/tests/Core/AcmeClientTest.php b/tests/Core/AcmeClientTest.php index e5d1b619..3c8969c0 100644 --- a/tests/Core/AcmeClientTest.php +++ b/tests/Core/AcmeClientTest.php @@ -61,12 +61,9 @@ public function testFullProcess(KeyOption $keyOption) * Register account */ if ('eab' === getenv('PEBBLE_MODE')) { - $data = $client->registerAccount('titouan.galopin@acmephp.io', new ExternalAccount( - 'ofX3Rmny4aAD3bwTBZnhJw', - 'pfVgkYJ-mbNLtmVjpgYeH_s_VYv30MjTmqYXrh3HG0syrG-JtNV3SOQedzV-rPY314qY8v3b7Sc443e65nBLYw' - )); + $data = $client->registerAccount('titouan.galopin@acmephp.com', new ExternalAccount('kid1', 'testing')); } else { - $data = $client->registerAccount(); + $data = $client->registerAccount('titouan.galopin@acmephp.com'); } $this->assertIsArray($data); diff --git a/tests/Fixtures/pebble-config-eab.json b/tests/Fixtures/pebble-config-eab.json index 7f635fc8..6c01253f 100644 --- a/tests/Fixtures/pebble-config-eab.json +++ b/tests/Fixtures/pebble-config-eab.json @@ -7,6 +7,9 @@ "httpPort": 5002, "tlsPort": 5001, "ocspResponderURL": "", - "externalAccountBindingRequired": true + "externalAccountBindingRequired": true, + "externalAccountMACKeys": { + "kid1": "dGVzdGluZw" + } } } From e3550c555844ec53a3d6682537a89f77a42f0d22 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 7 Nov 2020 22:27:17 +0100 Subject: [PATCH 031/121] Allow to configure directly EAB credentials --- src/Cli/Command/RunCommand.php | 48 +++++++++++-------- src/Cli/Configuration/DomainConfiguration.php | 8 ++++ src/Core/AcmeClient.php | 1 - tests/Cli/AbstractApplicationTest.php | 4 +- .../sfpt_nginxproxy/default.yaml} | 0 .../Fixtures/config/sfpt_nginxproxy/eab.yaml | 25 ++++++++++ .../simple/default.yaml} | 0 tests/Cli/Fixtures/config/simple/eab.yaml | 16 +++++++ tests/Cli/SftpNginxProxyApplicationTest.php | 4 +- tests/Cli/SimpleApplicationTest.php | 4 +- 10 files changed, 82 insertions(+), 28 deletions(-) rename tests/Cli/Fixtures/{config_sfpt_nginxproxy.yaml => config/sfpt_nginxproxy/default.yaml} (100%) create mode 100644 tests/Cli/Fixtures/config/sfpt_nginxproxy/eab.yaml rename tests/Cli/Fixtures/{config_simple.yaml => config/simple/default.yaml} (100%) create mode 100644 tests/Cli/Fixtures/config/simple/eab.yaml diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index cf7fbe5d..1a70880c 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -142,41 +142,47 @@ private function register($email, KeyOption $keyOption) private function resolveEabKid(): ?ExternalAccount { - if ('zerossl' !== $this->config['provider']) { - return null; + // If an External Account is provided, use it + if ($this->config['eab_kid'] && $this->config['eab_hmac_key']) { + return new ExternalAccount($this->config['eab_kid'], $this->config['eab_hmac_key']); } - // If an API is provided, use it - if ($this->config['zerossl_api_key']) { + // If using ZeroSSL ... + if ('zerossl' === $this->config['provider']) { + // If an API key is provided, use it + if ($this->config['zerossl_api_key']) { + $eabCredentials = \GuzzleHttp\json_decode( + (new Client()) + ->post('https://api.zerossl.com/acme/eab-credentials/?access_key='.$this->config['zerossl_api_key']) + ->getBody() + ->getContents() + ); + + if (!isset($eabCredentials->success) || !$eabCredentials->success) { + throw new AcmeCliException('ZeroSSL External account Binding failed: are you sure your API key is valid?'); + } + + return new ExternalAccount($eabCredentials->eab_kid, $eabCredentials->eab_hmac_key); + } + + // Otherwise register on the fly $eabCredentials = \GuzzleHttp\json_decode( (new Client()) - ->post('https://api.zerossl.com/acme/eab-credentials/?access_key='.$this->config['zerossl_api_key']) + ->post('https://api.zerossl.com/acme/eab-credentials-email', [ + 'form_params' => ['email' => $this->config['contact_email']], + ]) ->getBody() ->getContents() ); if (!isset($eabCredentials->success) || !$eabCredentials->success) { - throw new AcmeCliException('ZeroSSL External account Binding failed: are you sure your API key is valid?'); + throw new AcmeCliException('ZeroSSL External account Binding failed: registering your email failed.'); } return new ExternalAccount($eabCredentials->eab_kid, $eabCredentials->eab_hmac_key); } - // Otherwise register on the fly - $eabCredentials = \GuzzleHttp\json_decode( - (new Client()) - ->post('https://api.zerossl.com/acme/eab-credentials-email', [ - 'form_params' => ['email' => $this->config['contact_email']], - ]) - ->getBody() - ->getContents() - ); - - if (!isset($eabCredentials->success) || !$eabCredentials->success) { - throw new AcmeCliException('ZeroSSL External account Binding failed: registering your email failed.'); - } - - return new ExternalAccount($eabCredentials->eab_kid, $eabCredentials->eab_hmac_key); + return null; } private function installCertificate(CertificateResponse $response, array $actions) diff --git a/src/Cli/Configuration/DomainConfiguration.php b/src/Cli/Configuration/DomainConfiguration.php index 4899e821..99040495 100644 --- a/src/Cli/Configuration/DomainConfiguration.php +++ b/src/Cli/Configuration/DomainConfiguration.php @@ -67,6 +67,14 @@ public function getConfigTreeBuilder() ->thenInvalid('The certificate provider %s is not valid (supported: '.implode(', ', Application::PROVIDERS).').') ->end() ->end() + ->scalarNode('eab_kid') + ->info('External Account Binding identifier (optional)') + ->defaultNull() + ->end() + ->scalarNode('eab_hmac_key') + ->info('External Account Binding HMAC key (optional)') + ->defaultNull() + ->end() ->scalarNode('zerossl_api_key') ->info('ZeroSSL API key to use if you already have one (one will be created automatically otherwise)') ->defaultNull() diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 990f9e47..4ac7ecf6 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -28,7 +28,6 @@ use AcmePhp\Ssl\CertificateRequest; use AcmePhp\Ssl\CertificateResponse; use AcmePhp\Ssl\Signer\CertificateRequestSigner; -use Lcobucci\JWT\Signer\Hmac\Sha256; use Webmozart\Assert\Assert; /** diff --git a/tests/Cli/AbstractApplicationTest.php b/tests/Cli/AbstractApplicationTest.php index 800db41f..be8da5c0 100644 --- a/tests/Cli/AbstractApplicationTest.php +++ b/tests/Cli/AbstractApplicationTest.php @@ -26,7 +26,7 @@ abstract class AbstractApplicationTest extends AbstractFunctionnalTest abstract protected function getFixturesDirectories(): array; - abstract protected function getConfigFile(): string; + abstract protected function getConfigDir(): string; public function setUp(): void { @@ -45,7 +45,7 @@ public function testFullProcess() $runTester = new CommandTester($this->application->find('run')); $runTester->execute([ 'command' => 'run', - 'config' => $this->getConfigFile(), + 'config' => $this->getConfigDir().'/'.('eab' === getenv('PEBBLE_MODE') ? 'eab' : 'default').'.yaml', ]); $output = $runTester->getDisplay(); diff --git a/tests/Cli/Fixtures/config_sfpt_nginxproxy.yaml b/tests/Cli/Fixtures/config/sfpt_nginxproxy/default.yaml similarity index 100% rename from tests/Cli/Fixtures/config_sfpt_nginxproxy.yaml rename to tests/Cli/Fixtures/config/sfpt_nginxproxy/default.yaml diff --git a/tests/Cli/Fixtures/config/sfpt_nginxproxy/eab.yaml b/tests/Cli/Fixtures/config/sfpt_nginxproxy/eab.yaml new file mode 100644 index 00000000..251e9d6a --- /dev/null +++ b/tests/Cli/Fixtures/config/sfpt_nginxproxy/eab.yaml @@ -0,0 +1,25 @@ +contact_email: foo@example.com +key_type: RSA +provider: localhost +eab_kid: kid1 +eab_hmac_key: testing + +defaults: + distinguished_name: + country: FR + locality: Paris + organization_name: Acme PHP + +certificates: + - domain: acmephp.com + solver: + name: mock-server + install: + - action: build_nginxproxy + - action: mirror_file + adapter: sftp + root: /share + host: localhost + username: acmephp + password: acmephp + port: 8022 diff --git a/tests/Cli/Fixtures/config_simple.yaml b/tests/Cli/Fixtures/config/simple/default.yaml similarity index 100% rename from tests/Cli/Fixtures/config_simple.yaml rename to tests/Cli/Fixtures/config/simple/default.yaml diff --git a/tests/Cli/Fixtures/config/simple/eab.yaml b/tests/Cli/Fixtures/config/simple/eab.yaml new file mode 100644 index 00000000..626de431 --- /dev/null +++ b/tests/Cli/Fixtures/config/simple/eab.yaml @@ -0,0 +1,16 @@ +contact_email: foo@example.com +key_type: RSA +provider: localhost +eab_kid: kid1 +eab_hmac_key: testing + +defaults: + distinguished_name: + country: FR + locality: Paris + organization_name: Acme PHP + +certificates: + - domain: acmephp.com + solver: + name: mock-server diff --git a/tests/Cli/SftpNginxProxyApplicationTest.php b/tests/Cli/SftpNginxProxyApplicationTest.php index 1128d41b..a2754462 100644 --- a/tests/Cli/SftpNginxProxyApplicationTest.php +++ b/tests/Cli/SftpNginxProxyApplicationTest.php @@ -25,9 +25,9 @@ protected function getFixturesDirectories(): array ]; } - protected function getConfigFile(): string + protected function getConfigDir(): string { - return __DIR__.'/Fixtures/config_sfpt_nginxproxy.yaml'; + return __DIR__.'/Fixtures/config/sfpt_nginxproxy'; } public function testFullProcess() diff --git a/tests/Cli/SimpleApplicationTest.php b/tests/Cli/SimpleApplicationTest.php index 7bb5c3c6..edc40fcb 100644 --- a/tests/Cli/SimpleApplicationTest.php +++ b/tests/Cli/SimpleApplicationTest.php @@ -20,8 +20,8 @@ protected function getFixturesDirectories(): array ]; } - protected function getConfigFile(): string + protected function getConfigDir(): string { - return __DIR__.'/Fixtures/config_simple.yaml'; + return __DIR__.'/Fixtures/config/simple'; } } From 7e96853dece3d7a1e8a95afbdcb4d2283e1411bb Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sun, 13 Dec 2020 21:00:34 +0100 Subject: [PATCH 032/121] Fix encoding issue --- src/Core/Http/SecureHttpClient.php | 2 +- tests/Cli/Fixtures/config/sfpt_nginxproxy/eab.yaml | 2 +- tests/Cli/Fixtures/config/simple/eab.yaml | 2 +- tests/Core/AcmeClientTest.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index 78b88a2b..febef73a 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -171,7 +171,7 @@ public function createExternalAccountPayload(ExternalAccount $externalAccount, s $encodedPayload = $this->base64Encoder->encode(json_encode($this->getJWK(), JSON_UNESCAPED_SLASHES)); $signature = $this->base64Encoder->encode( - (string) $signer->sign($encodedProtected.'.'.$encodedPayload, $externalAccount->getHmacKey()) + (string) $signer->sign($encodedProtected.'.'.$encodedPayload, $this->base64Encoder->decode($externalAccount->getHmacKey())) ); return [ diff --git a/tests/Cli/Fixtures/config/sfpt_nginxproxy/eab.yaml b/tests/Cli/Fixtures/config/sfpt_nginxproxy/eab.yaml index 251e9d6a..c3f20952 100644 --- a/tests/Cli/Fixtures/config/sfpt_nginxproxy/eab.yaml +++ b/tests/Cli/Fixtures/config/sfpt_nginxproxy/eab.yaml @@ -2,7 +2,7 @@ contact_email: foo@example.com key_type: RSA provider: localhost eab_kid: kid1 -eab_hmac_key: testing +eab_hmac_key: dGVzdGluZw defaults: distinguished_name: diff --git a/tests/Cli/Fixtures/config/simple/eab.yaml b/tests/Cli/Fixtures/config/simple/eab.yaml index 626de431..12f78b1e 100644 --- a/tests/Cli/Fixtures/config/simple/eab.yaml +++ b/tests/Cli/Fixtures/config/simple/eab.yaml @@ -2,7 +2,7 @@ contact_email: foo@example.com key_type: RSA provider: localhost eab_kid: kid1 -eab_hmac_key: testing +eab_hmac_key: dGVzdGluZw defaults: distinguished_name: diff --git a/tests/Core/AcmeClientTest.php b/tests/Core/AcmeClientTest.php index 3c8969c0..02621bf9 100644 --- a/tests/Core/AcmeClientTest.php +++ b/tests/Core/AcmeClientTest.php @@ -61,7 +61,7 @@ public function testFullProcess(KeyOption $keyOption) * Register account */ if ('eab' === getenv('PEBBLE_MODE')) { - $data = $client->registerAccount('titouan.galopin@acmephp.com', new ExternalAccount('kid1', 'testing')); + $data = $client->registerAccount('titouan.galopin@acmephp.com', new ExternalAccount('kid1', 'dGVzdGluZw')); } else { $data = $client->registerAccount('titouan.galopin@acmephp.com'); } From 258ea5ccd8c851a76b7a5d2fdb576549c56f3f98 Mon Sep 17 00:00:00 2001 From: Tal Paparne Date: Wed, 23 Sep 2020 15:59:18 -0400 Subject: [PATCH 033/121] Add option that allows a client to download the alternate certificate link instead of the default one --- src/Core/AcmeClient.php | 39 ++++++++++++++----- src/Core/AcmeClientInterface.php | 24 ++++++++---- src/Core/Http/SecureHttpClient.php | 60 +++++++++++++++++++----------- 3 files changed, 83 insertions(+), 40 deletions(-) diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 4ac7ecf6..eefaa8fd 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -28,6 +28,7 @@ use AcmePhp\Ssl\CertificateRequest; use AcmePhp\Ssl\CertificateResponse; use AcmePhp\Ssl\Signer\CertificateRequestSigner; +use GuzzleHttp\Psr7\Utils; use Webmozart\Assert\Assert; /** @@ -145,7 +146,7 @@ static function ($domain) { /** * {@inheritdoc} */ - public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, int $timeout = 180): CertificateResponse + public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, int $timeout = 180, bool $returnAlternateCertificateIfAvailable = false): CertificateResponse { $endTime = time() + $timeout; $client = $this->getHttpClient(); @@ -171,13 +172,23 @@ public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, throw new CertificateRequestFailedException('The order has not been validated'); } - $response = $client->request('POST', $response['certificate'], $client->signKidPayload($response['certificate'], $this->getResourceAccount(), null), false); - $certificatesChain = null; - foreach (array_reverse(explode("\n\n", $response)) as $pem) { - $certificatesChain = new Certificate($pem, $certificatesChain); + $response = $client->rawRequest('POST', $response['certificate'], $client->signKidPayload($response['certificate'], $this->getResourceAccount(), null)); + $responseHeaders = $response->getHeaders(); + + if ($returnAlternateCertificateIfAvailable && isset($responseHeaders['Link'][1])) { + $matches = []; + preg_match('/<(http.*acme\/cert\/.*\/\d)>;rel="alternate"/', $responseHeaders['Link'][1], $matches); + + // If response headers include a valid alternate certificate link, return that certificate instead + if (isset($matches[1])) { + return $this->createCertificateResponse( + $csr, + $client->request('POST', $matches[1], $client->signKidPayload($matches[1], $this->getResourceAccount(), null), false) + ); + } } - return new CertificateResponse($csr, $certificatesChain); + return $this->createCertificateResponse($csr, Utils::copyToString($response->getBody())); } /** @@ -238,13 +249,11 @@ public function challengeAuthorization(AuthorizationChallenge $challenge, int $t /** * {@inheritdoc} */ - public function requestCertificate(string $domain, CertificateRequest $csr, int $timeout = 180): CertificateResponse + public function requestCertificate(string $domain, CertificateRequest $csr, int $timeout = 180, bool $returnAlternateCertificateIfAvailable = false): CertificateResponse { - Assert::stringNotEmpty($domain, 'requestCertificate::$domain expected a non-empty string. Got: %s'); - $order = $this->requestOrder(array_unique(array_merge([$domain], $csr->getDistinguishedName()->getSubjectAlternativeNames()))); - return $this->finalizeOrder($order, $csr, $timeout); + return $this->finalizeOrder($order, $csr, $timeout, $returnAlternateCertificateIfAvailable); } /** @@ -316,6 +325,16 @@ protected function requestResource(string $method, string $resource, array $payl ); } + private function createCertificateResponse(CertificateRequest $csr, string $certificate): CertificateResponse + { + $certificatesChain = null; + foreach (array_reverse(explode("\n\n", $certificate)) as $pem) { + $certificatesChain = new Certificate($pem, $certificatesChain); + } + + return new CertificateResponse($csr, $certificatesChain); + } + private function getResourceAccount(): string { if (!$this->account) { diff --git a/src/Core/AcmeClientInterface.php b/src/Core/AcmeClientInterface.php index 2739912d..f307fb7b 100644 --- a/src/Core/AcmeClientInterface.php +++ b/src/Core/AcmeClientInterface.php @@ -76,9 +76,13 @@ public function requestOrder(array $domains): CertificateOrder; * wait for the Certificate Authority to validate the certificate and * this operation could be long. * - * @param CertificateOrder $order the Order returned by the Certificate Authority - * @param CertificateRequest $csr the Certificate Signing Request (informations for the certificate) - * @param int $timeout the timeout period + * @param CertificateOrder $order the Order returned by the Certificate Authority + * @param CertificateRequest $csr the Certificate Signing Request (informations for the certificate) + * @param int $timeout the timeout period + * @param bool $returnAlternateCertificateIfAvailable whether the alternate certificate provided by + * the CA should be returned instead of the main one. + * This is especially useful following + * https://letsencrypt.org/2019/04/15/transitioning-to-isrg-root.html. * * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * (the exception will be more specific if detail is provided) @@ -88,7 +92,7 @@ public function requestOrder(array $domains): CertificateOrder; * * @return CertificateResponse the certificate data to save it somewhere you want */ - public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, int $timeout = 180): CertificateResponse; + public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, int $timeout = 180, bool $returnAlternateCertificateIfAvailable = false): CertificateResponse; /** * Request authorization challenge data for a given domain. @@ -151,9 +155,13 @@ public function challengeAuthorization(AuthorizationChallenge $challenge, int $t * wait for the Certificate Authority to validate the certificate and * this operation could be long. * - * @param string $domain the domain to request a certificate for - * @param CertificateRequest $csr the Certificate Signing Request (informations for the certificate) - * @param int $timeout the timeout period + * @param string $domain the domain to request a certificate for + * @param CertificateRequest $csr the Certificate Signing Request (informations for the certificate) + * @param int $timeout the timeout period + * @param bool $returnAlternateCertificateIfAvailable whether the alternate certificate provided by + * the CA should be returned instead of the main one. + * This is especially useful following + * https://letsencrypt.org/2019/04/15/transitioning-to-isrg-root.html. * * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * (the exception will be more specific if detail is provided) @@ -163,7 +171,7 @@ public function challengeAuthorization(AuthorizationChallenge $challenge, int $t * * @return CertificateResponse the certificate data to save it somewhere you want */ - public function requestCertificate(string $domain, CertificateRequest $csr, int $timeout = 180): CertificateResponse; + public function requestCertificate(string $domain, CertificateRequest $csr, int $timeout = 180, bool $returnAlternateCertificateIfAvailable = false): CertificateResponse; /** * Revoke a given certificate from the Certificate Authority. diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index febef73a..d53f873a 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -22,7 +22,9 @@ use AcmePhp\Ssl\Signer\DataSigner; use GuzzleHttp\ClientInterface; use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Psr7\Header; use GuzzleHttp\Psr7\Request; +use GuzzleHttp\Psr7\Utils; use Lcobucci\JWT\Signer\Hmac\Sha256; use Psr\Http\Message\ResponseInterface; @@ -182,7 +184,8 @@ public function createExternalAccountPayload(ExternalAccount $externalAccount, s } /** - * Send a request encoded in the format defined by the ACME protocol. + * Send a request encoded in the format defined by the ACME protocol + * and its content (optionally parsed as JSON). * * @throws AcmeCoreClientException when an error occured during response parsing * @throws ExpectedJsonException when $returnJson = true and the response is not valid JSON @@ -192,24 +195,8 @@ public function createExternalAccountPayload(ExternalAccount $externalAccount, s */ public function request(string $method, string $endpoint, array $data = [], bool $returnJson = true) { - $call = function () use ($method, $endpoint, $data) { - $request = $this->createRequest($method, $endpoint, $data); - try { - $this->lastResponse = $this->httpClient->send($request); - } catch (\Exception $exception) { - $this->handleClientException($request, $exception); - } - - return $request; - }; - - try { - $request = $call(); - } catch (BadNonceServerException $e) { - $request = $call(); - } - - $body = \GuzzleHttp\Psr7\copy_to_string($this->lastResponse->getBody()); + $response = $this->rawRequest($method, $endpoint, $data); + $body = Utils::copyToString($response->getBody()); if (!$returnJson) { return $body; @@ -222,12 +209,41 @@ public function request(string $method, string $endpoint, array $data = [], bool $data = JsonDecoder::decode($body, true); } catch (\InvalidArgumentException $exception) { - throw new ExpectedJsonException(sprintf('ACME client excepted valid JSON as a response to request "%s %s" (given: "%s")', $request->getMethod(), $request->getUri(), ServerErrorHandler::getResponseBodySummary($this->lastResponse)), $exception); + throw new ExpectedJsonException(sprintf('ACME client excepted valid JSON as a response to request "%s %s" (given: "%s")', $method, $endpoint, ServerErrorHandler::getResponseBodySummary($response)), $exception); } return $data; } + /** + * Send a request encoded in the format defined by the ACME protocol and return the response object. + * + * @throws AcmeCoreClientException when an error occured during response parsing + * @throws ExpectedJsonException when $returnJson = true and the response is not valid JSON + * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code + */ + public function rawRequest(string $method, string $endpoint, array $data = []): ResponseInterface + { + $call = function () use ($method, $endpoint, $data) { + $request = $this->createRequest($method, $endpoint, $data); + try { + $this->lastResponse = $this->httpClient->send($request); + } catch (\Exception $exception) { + $this->handleClientException($request, $exception); + } + + return $request; + }; + + try { + $call(); + } catch (BadNonceServerException $e) { + $call(); + } + + return $this->lastResponse; + } + public function setAccountKeyPair(KeyPair $keyPair) { $this->accountKeyPair = $keyPair; @@ -245,7 +261,7 @@ public function getLastLocation(): string public function getLastLinks(): array { - return \GuzzleHttp\Psr7\parse_header($this->lastResponse->getHeader('Link')); + return Header::parse($this->lastResponse->getHeader('Link')); } public function getAccountKeyPair(): KeyPair @@ -315,7 +331,7 @@ private function createRequest($method, $endpoint, $data) if ('POST' === $method && \is_array($data)) { $request = $request->withHeader('Content-Type', 'application/jose+json'); - $request = $request->withBody(\GuzzleHttp\Psr7\stream_for(json_encode($data))); + $request = $request->withBody(Utils::streamFor(json_encode($data))); } return $request; From 8dc7923803e5fa7078fba92cc0ec6d6d77c16c17 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sun, 13 Dec 2020 21:58:00 +0100 Subject: [PATCH 034/121] Add alternate certificate tests --- src/Core/AcmeClient.php | 2 +- tests/Core/AcmeClientTest.php | 18 ++++++++++-------- tests/setup.sh | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index eefaa8fd..77b8618a 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -177,7 +177,7 @@ public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, if ($returnAlternateCertificateIfAvailable && isset($responseHeaders['Link'][1])) { $matches = []; - preg_match('/<(http.*acme\/cert\/.*\/\d)>;rel="alternate"/', $responseHeaders['Link'][1], $matches); + preg_match('/<(http.*)>;rel="alternate"/', $responseHeaders['Link'][1], $matches); // If response headers include a valid alternate certificate link, return that certificate instead if (isset($matches[1])) { diff --git a/tests/Core/AcmeClientTest.php b/tests/Core/AcmeClientTest.php index 02621bf9..3f8d0c2d 100644 --- a/tests/Core/AcmeClientTest.php +++ b/tests/Core/AcmeClientTest.php @@ -27,24 +27,26 @@ use AcmePhp\Ssl\Generator\KeyOption; use AcmePhp\Ssl\Generator\KeyPairGenerator; use AcmePhp\Ssl\Generator\RsaKey\RsaKeyOption; +use AcmePhp\Ssl\Parser\CertificateParser; use AcmePhp\Ssl\Parser\KeyParser; use AcmePhp\Ssl\Signer\DataSigner; use GuzzleHttp\Client; class AcmeClientTest extends AbstractFunctionnalTest { - public function provideKeyOptions() + public function provideFullProcess() { - yield 'rsa1024' => [new RsaKeyOption(1024)]; - yield 'rsa4098' => [new RsaKeyOption(4098)]; - yield 'ecprime256v1' => [new EcKeyOption('prime256v1')]; - yield 'ecsecp384r1' => [new EcKeyOption('secp384r1')]; + yield 'rsa1024' => [new RsaKeyOption(1024), false]; + yield 'rsa1024-alternate' => [new RsaKeyOption(1024), true]; + yield 'rsa4098' => [new RsaKeyOption(4098), false]; + yield 'ecprime256v1' => [new EcKeyOption('prime256v1'), false]; + yield 'ecsecp384r1' => [new EcKeyOption('secp384r1'), false]; } /** - * @dataProvider provideKeyOptions + * @dataProvider provideFullProcess */ - public function testFullProcess(KeyOption $keyOption) + public function testFullProcess(KeyOption $keyOption, bool $useAlternateCertificate) { $secureHttpClient = new SecureHttpClient( (new KeyPairGenerator())->generateKeyPair($keyOption), @@ -103,7 +105,7 @@ public function testFullProcess(KeyOption $keyOption) * Request certificate */ $csr = new CertificateRequest(new DistinguishedName('acmephp.com'), (new KeyPairGenerator())->generateKeyPair($keyOption)); - $response = $client->finalizeOrder($order, $csr); + $response = $client->finalizeOrder($order, $csr, 180, $useAlternateCertificate); $this->assertInstanceOf(CertificateResponse::class, $response); $this->assertEquals($csr, $response->getCertificateRequest()); diff --git a/tests/setup.sh b/tests/setup.sh index 904148e8..18b3cb56 100755 --- a/tests/setup.sh +++ b/tests/setup.sh @@ -11,7 +11,7 @@ docker run -d --name acme_sftp -p 8022:22 atmoz/sftp acmephp:acmephp:::share MODE=${PEBBLE_MODE:-default} docker run -d --name acme_server --net host letsencrypt/pebble-challtestsrv pebble-challtestsrv -defaultIPv6 "" -defaultIPv4 127.0.0.1 -docker run -d --name acme_pebble --net host -e PEBBLE_VA_NOSLEEP=1 -e PEBBLE_WFE_NONCEREJECT=0 -v $(pwd)/tests/Fixtures/pebble-config-$MODE.json:/test/config/pebble-config.json letsencrypt/pebble pebble -dnsserver 127.0.0.1:8053 +docker run -d --name acme_pebble --net host -e PEBBLE_VA_NOSLEEP=1 -e PEBBLE_WFE_NONCEREJECT=0 -e PEBBLE_ALTERNATE_ROOTS=1 -v $(pwd)/tests/Fixtures/pebble-config-$MODE.json:/test/config/pebble-config.json letsencrypt/pebble pebble -dnsserver 127.0.0.1:8053 # Wait for boot to be completed docker run --rm --net host martin/wait -c localhost:14000,localhost:8022,localhost:8053,localhost:5002 -t 120 From 65a0ac1301dcbe44499654955a846a1cad04b5bd Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sun, 13 Dec 2020 21:58:53 +0100 Subject: [PATCH 035/121] Fix tests and CS --- composer.json | 2 +- tests/Core/AcmeClientTest.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index d4687458..5f355322 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,7 @@ "alibabacloud/cdn": "^1.7", "alibabacloud/wafopenapi": "^1.7", "aws/aws-sdk-php": "^3.38", - "guzzlehttp/guzzle": "^6.0|^7.0", + "guzzlehttp/guzzle": "^7.2", "guzzlehttp/psr7": "^1.0", "lcobucci/jwt": "^3.3", "league/flysystem": "^1.0.19", diff --git a/tests/Core/AcmeClientTest.php b/tests/Core/AcmeClientTest.php index 3f8d0c2d..39383e73 100644 --- a/tests/Core/AcmeClientTest.php +++ b/tests/Core/AcmeClientTest.php @@ -27,7 +27,6 @@ use AcmePhp\Ssl\Generator\KeyOption; use AcmePhp\Ssl\Generator\KeyPairGenerator; use AcmePhp\Ssl\Generator\RsaKey\RsaKeyOption; -use AcmePhp\Ssl\Parser\CertificateParser; use AcmePhp\Ssl\Parser\KeyParser; use AcmePhp\Ssl\Signer\DataSigner; use GuzzleHttp\Client; From 2683496834b647bb48ac57c728e0ea8a68a68c95 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Mon, 14 Dec 2020 20:09:26 +0100 Subject: [PATCH 036/121] Fix Box config and bump version --- box.json.dist | 2 +- src/Cli/Application.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/box.json.dist b/box.json.dist index e26b939a..a3a93ac7 100644 --- a/box.json.dist +++ b/box.json.dist @@ -1,5 +1,5 @@ { - "directories": [ "src", "res" ], + "directories": [ "src" ], "files": [ "LICENSE" ], "finder": [ { diff --git a/src/Cli/Application.php b/src/Cli/Application.php index d14ed45a..caf03a68 100644 --- a/src/Cli/Application.php +++ b/src/Cli/Application.php @@ -35,7 +35,7 @@ class Application extends BaseApplication */ public function __construct() { - parent::__construct('Acme PHP - Let\'s Encrypt/ZeroSSL client', ''); + parent::__construct('Acme PHP - Let\'s Encrypt/ZeroSSL client', '2.0.0'); } /** From 3f3752417beaaed4ed5c62dd75830a58ece02c0b Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Mon, 14 Dec 2020 20:10:14 +0100 Subject: [PATCH 037/121] Release of new version 2.0.0 --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a317c5d..8caa4999 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,29 @@ +14/12/2020 20:10 2.0.0 v2.0 +2683496 Fix Box config and bump version +4f56592 Merge pull request #212 from acmephp/2.0 +65a0ac1 Fix tests and CS +8dc7923 Add alternate certificate tests +258ea5c Add option that allows a client to download the alternate certificate link instead of the default one +7e96853 Fix encoding issue +e3550c5 Allow to configure directly EAB credentials +c2bf09b Finalize EAB support +8f10906 Adapt tests +12533ba Add EAB structure +7fa7a2a Add EAB test +701b864 Bump minimum version of Symfony +5183019 Remove legacy tests +5ef6681 Migrate to Github Actions +3fc8c60 Remove deprecated features +ccbba76 Fix CS +2a473a8 Migrate acmephp/cli to use typehints +7d6b566 Fix tests +ab68cac Bump PHP-CS-Fixer version +c061e3a Migrate acmephp/core to use typehints +b342a86 Migrate acmephp/ssl to use typehints +be0a5b1 Fix subpackages composer.json +e51784a Remove deprecated commands, improve tests on Run command and merge v2 interfaces +e2be9c3 Upgrade dependencies and drop support for PHP <7.2 +2330ef6 Bump version 13/12/2020 23:03 1.3.0 v1.3.0 5d37fb1 Merge pull request #218 from acmephp/do-not-verify-https-http-vaidator fe50cf6 Merge pull request #217 from acmephp/openssl-php8 From f1fbaf15e4f771b45a988006fb9e98301276659a Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Tue, 12 Jan 2021 12:27:18 +0100 Subject: [PATCH 038/121] Require acmephp/ssl 2.0 --- src/Core/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/composer.json b/src/Core/composer.json index 211721a9..ea93820e 100644 --- a/src/Core/composer.json +++ b/src/Core/composer.json @@ -31,7 +31,7 @@ "ext-hash": "*", "ext-json": "*", "ext-openssl": "*", - "acmephp/ssl": "^1.0", + "acmephp/ssl": "^2.0", "guzzlehttp/guzzle": "^6.0|^7.0", "guzzlehttp/psr7": "^1.0", "lcobucci/jwt": "^3.3", From b50c73091a9272ebc15e3ed00a5c06e37fe2fa49 Mon Sep 17 00:00:00 2001 From: jackdpeterson Date: Tue, 12 Jan 2021 10:08:15 -0800 Subject: [PATCH 039/121] expand compatibility range for lcobucci/jwt to include both ^3.3 as well as ^4.0. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5f355322..d9942c84 100644 --- a/composer.json +++ b/composer.json @@ -46,7 +46,7 @@ "aws/aws-sdk-php": "^3.38", "guzzlehttp/guzzle": "^7.2", "guzzlehttp/psr7": "^1.0", - "lcobucci/jwt": "^3.3", + "lcobucci/jwt": "^3.3|^4.0", "league/flysystem": "^1.0.19", "league/flysystem-memory": "^1.0", "league/flysystem-sftp": "^1.0.7", From 8efc61a41cbf19d42c95d87a02dc1037d9e5d766 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sun, 17 Jan 2021 15:22:28 +0100 Subject: [PATCH 040/121] Fix CI config --- .github/workflows/test-build.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 12569935..eb2af741 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -1,6 +1,10 @@ name: Test and build -on: push +on: + pull_request: ~ + push: + branches: + - master jobs: php-cs: From 1aa124cd7fdee1a6984f959e4bd050945a28f067 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Wed, 3 Feb 2021 22:29:16 +0100 Subject: [PATCH 041/121] Release of new version 2.0.1 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8caa4999..bcba8e10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +03/02/2021 22:29 2.0.1 Fix requirements +8efc61a Fix CI config +8b26916 Merge pull request #223 from piotrantosik/patch-1 +f836e0c Merge pull request #224 from jackdpeterson/expand_composer_deps +b50c730 expand compatibility range for lcobucci/jwt to include both ^3.3 as well as ^4.0. +f1fbaf1 Require acmephp/ssl 2.0 14/12/2020 20:10 2.0.0 v2.0 2683496 Fix Box config and bump version 4f56592 Merge pull request #212 from acmephp/2.0 From 192872c085f9865f2cd90db6556acf7d13591eda Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Thu, 11 Feb 2021 11:43:20 +0100 Subject: [PATCH 042/121] Allow lcobucci/jwt ^4.0 in core package --- src/Core/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/composer.json b/src/Core/composer.json index ea93820e..8e72fda8 100644 --- a/src/Core/composer.json +++ b/src/Core/composer.json @@ -34,7 +34,7 @@ "acmephp/ssl": "^2.0", "guzzlehttp/guzzle": "^6.0|^7.0", "guzzlehttp/psr7": "^1.0", - "lcobucci/jwt": "^3.3", + "lcobucci/jwt": "^3.3|^4.0", "psr/http-message": "^1.0", "psr/log": "^1.0", "webmozart/assert": "^1.0" From ee2119ee81b2057a2c817884fecb1243afb19229 Mon Sep 17 00:00:00 2001 From: mjaglewicz Date: Thu, 30 Sep 2021 18:30:06 +0200 Subject: [PATCH 043/121] Cpanel certificate installation action --- src/Cli/Action/InstallCPanelAction.php | 58 ++++++++++++++++++++++++++ src/Cli/Resources/services.xml | 4 ++ 2 files changed, 62 insertions(+) create mode 100644 src/Cli/Action/InstallCPanelAction.php diff --git a/src/Cli/Action/InstallCPanelAction.php b/src/Cli/Action/InstallCPanelAction.php new file mode 100644 index 00000000..6efbdc95 --- /dev/null +++ b/src/Cli/Action/InstallCPanelAction.php @@ -0,0 +1,58 @@ +httpClient = $httpClient; + } + + /** + * {@inheritdoc} + */ + public function handle(array $config, CertificateResponse $response) + { + $this->assertConfiguration($config, ['host', 'username', 'token']); + + $commonName = $response->getCertificateRequest()->getDistinguishedName()->getCommonName(); + $certificate = $response->getCertificate(); + $privateKey = $response->getCertificateRequest()->getKeyPair()->getPrivateKey(); + + $issuerChain = array_map(function (Certificate $certificate) { + return $certificate->getPEM(); + }, $certificate->getIssuerChain()); + + $this->installCertificate( + $config, + $commonName, + $certificate->getPEM(), + implode("\n", $issuerChain), + $privateKey->getPEM() + ); + } + + private function installCertificate($config, $domain, $crt, $caBundle, $key) + { + $this->httpClient->request('POST', $config['host'] . 'json-api/cpanel?' . + 'cpanel_jsonapi_apiversion=2&' . + 'cpanel_jsonapi_module=SSL&' . + 'cpanel_jsonapi_func=installssl&' . + 'domain=' . $domain . '&' . + 'crt=' . urlencode($crt) . '&' . + 'key=' . urlencode($key) . '&' . + 'cabundle=' . urlencode($caBundle), + ['headers' => ['Authorization' => 'cpanel ' . $config['username'] . ':' . $config['token']], + ]); + } +} \ No newline at end of file diff --git a/src/Cli/Resources/services.xml b/src/Cli/Resources/services.xml index 15e3b5b9..d347e0ca 100644 --- a/src/Cli/Resources/services.xml +++ b/src/Cli/Resources/services.xml @@ -96,6 +96,10 @@ + + + + From 2299602e5b619f12cdf4aa5df923a65b81d86b1e Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Wed, 5 Jan 2022 11:28:14 +0100 Subject: [PATCH 044/121] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 75088a6b..389a42d2 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ Acme PHP ======== [![Build Status](https://img.shields.io/travis/acmephp/acmephp/master.svg?style=flat-square)](https://travis-ci.org/acmephp/acmephp) -[![StyleCI](https://styleci.io/repos/59910490/shield)](https://styleci.io/repos/59910490) [![Packagist Version](https://img.shields.io/packagist/v/acmephp/acmephp.svg?style=flat-square)](https://packagist.org/packages/acmephp/acmephp) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) @@ -18,8 +17,7 @@ able to deeply integrate the management of your certificates directly in your ap by these features, have a look at the [acmephp/core](https://github.com/acmephp/core) and [acmephp/ssl](https://github.com/acmephp/ssl) libraries. -> If you want to chat with us or have questions, ping -> @tgalopin or @jderusse on the [Symfony Slack](https://symfony.com/support)! +> Acme PHP is now maintained by https://zerossl.com. ## Why should I use Acme PHP when I have an official client? From 76213560564720e11089feea47aa5ba9d87be83f Mon Sep 17 00:00:00 2001 From: Hans Adema Date: Wed, 1 Dec 2021 11:43:15 +0100 Subject: [PATCH 045/121] Fix tests on PHP 8.0/8.1 --- .github/workflows/test-build.yaml | 17 ++++++++++ src/Ssl/Key.php | 2 +- tests/Ssl/AssertsOpenSslResource.php | 33 ++++++++++++++++++ tests/Ssl/CertificateTest.php | 4 ++- tests/Ssl/Generator/KeyPairGeneratorTest.php | 35 +++++++++++--------- 5 files changed, 73 insertions(+), 18 deletions(-) create mode 100644 tests/Ssl/AssertsOpenSslResource.php diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index eb2af741..4f472c25 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -64,3 +64,20 @@ jobs: run: ./tests/setup.sh - name: Running tests run: ./tests/run.sh + + tests-php80-deps-high-pebble-eab: + runs-on: ubuntu-latest + env: + PEBBLE_MODE: eab + steps: + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' + - uses: actions/checkout@master + - name: Install dependencies + run: composer update --no-interaction --no-progress --ansi --prefer-stable + - name: Preparing tests + run: ./tests/setup.sh + - name: Running tests + run: ./tests/run.sh + diff --git a/src/Ssl/Key.php b/src/Ssl/Key.php index 6614ffb8..6852ce24 100644 --- a/src/Ssl/Key.php +++ b/src/Ssl/Key.php @@ -47,7 +47,7 @@ public function getDER(): string } /** - * @return resource + * @return resource|\OpenSSLAsymmetricKey */ abstract public function getResource(); } diff --git a/tests/Ssl/AssertsOpenSslResource.php b/tests/Ssl/AssertsOpenSslResource.php new file mode 100644 index 00000000..47e19307 --- /dev/null +++ b/tests/Ssl/AssertsOpenSslResource.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\AcmePhp\Ssl; + +use PHPUnit\Framework\Assert; + +trait AssertsOpenSslResource +{ + /** + * Asserts that the provided "resource" is an OpenSSL Assymetric Key. + * + * On PHP 8+, OpenSSL works with objects. On PHP <8 OpenSSL works with resources. + * + * @param resource|\OpenSSLAsymmetricKey $resource + */ + public function assertIsOpenSslAsymmetricKey($resource) + { + if (PHP_MAJOR_VERSION >= 8) { + Assert::assertInstanceOf(\OpenSSLAsymmetricKey::class, $resource); + } else { + Assert::assertIsResource($resource); + } + } +} diff --git a/tests/Ssl/CertificateTest.php b/tests/Ssl/CertificateTest.php index 2202b96d..12fe63c9 100644 --- a/tests/Ssl/CertificateTest.php +++ b/tests/Ssl/CertificateTest.php @@ -17,6 +17,8 @@ class CertificateTest extends TestCase { + use AssertsOpenSslResource; + public function test_getPublicKey_returns_a_PublicKey() { $certificate = new Certificate( @@ -102,6 +104,6 @@ public function test_getPublicKeyResource_returns_a_resource() $resource = $certificate->getPublicKeyResource(); - $this->assertIsResource($resource); + $this->assertIsOpenSslAsymmetricKey($resource); } } diff --git a/tests/Ssl/Generator/KeyPairGeneratorTest.php b/tests/Ssl/Generator/KeyPairGeneratorTest.php index 10ae5812..1c6739d4 100644 --- a/tests/Ssl/Generator/KeyPairGeneratorTest.php +++ b/tests/Ssl/Generator/KeyPairGeneratorTest.php @@ -18,9 +18,12 @@ use AcmePhp\Ssl\Generator\RsaKey\RsaKeyOption; use AcmePhp\Ssl\KeyPair; use PHPUnit\Framework\TestCase; +use Tests\AcmePhp\Ssl\AssertsOpenSslResource; class KeyPairGeneratorTest extends TestCase { + use AssertsOpenSslResource; + /** @var KeyPairGenerator */ private $service; @@ -36,14 +39,14 @@ public function test_generateKeyPair_generate_random_instance_of_KeyPair() $result = $this->service->generateKeyPair(new RsaKeyOption(1024)); $this->assertInstanceOf(KeyPair::class, $result); - $this->assertStringContainsString('-----BEGIN PUBLIC KEY-----', $result->getPublicKey()->getPEM()); - $this->assertStringContainsString('-----BEGIN PRIVATE KEY-----', $result->getPrivateKey()->getPEM()); - $this->assertIsResource($result->getPublicKey()->getResource()); - $this->assertIsResource($result->getPrivateKey()->getResource()); + $this->assertIsOpenSslAsymmetricKey($result->getPublicKey()->getResource()); + $this->assertIsOpenSslAsymmetricKey($result->getPrivateKey()->getResource()); $details = openssl_pkey_get_details($result->getPrivateKey()->getResource()); $this->assertEquals(1024, $details['bits']); $this->assertArrayHasKey('rsa', $details); + + $this->assertEquals($details['key'], $result->getPublicKey()->getPEM()); } public function test_generateKeyPair_generate_random_instance_of_KeyPair_using_DH() @@ -54,13 +57,13 @@ public function test_generateKeyPair_generate_random_instance_of_KeyPair_using_D )); $this->assertInstanceOf(KeyPair::class, $result); - $this->assertStringContainsString('-----BEGIN PUBLIC KEY-----', $result->getPublicKey()->getPEM()); - $this->assertStringContainsString('-----BEGIN PRIVATE KEY-----', $result->getPrivateKey()->getPEM()); - $this->assertIsResource($result->getPublicKey()->getResource()); - $this->assertIsResource($result->getPrivateKey()->getResource()); + $this->assertIsOpenSslAsymmetricKey($result->getPublicKey()->getResource()); + $this->assertIsOpenSslAsymmetricKey($result->getPrivateKey()->getResource()); $details = openssl_pkey_get_details($result->getPrivateKey()->getResource()); $this->assertArrayHasKey('dh', $details); + + $this->assertEquals($details['key'], $result->getPublicKey()->getPEM()); } public function test_generateKeyPair_generate_random_instance_of_KeyPair_using_DSA() @@ -68,14 +71,14 @@ public function test_generateKeyPair_generate_random_instance_of_KeyPair_using_D $result = $this->service->generateKeyPair(new DsaKeyOption(1024)); $this->assertInstanceOf(KeyPair::class, $result); - $this->assertStringContainsString('-----BEGIN PUBLIC KEY-----', $result->getPublicKey()->getPEM()); - $this->assertStringContainsString('-----BEGIN PRIVATE KEY-----', $result->getPrivateKey()->getPEM()); - $this->assertIsResource($result->getPublicKey()->getResource()); - $this->assertIsResource($result->getPrivateKey()->getResource()); + $this->assertIsOpenSslAsymmetricKey($result->getPublicKey()->getResource()); + $this->assertIsOpenSslAsymmetricKey($result->getPrivateKey()->getResource()); $details = openssl_pkey_get_details($result->getPrivateKey()->getResource()); $this->assertEquals(1024, $details['bits']); $this->assertArrayHasKey('dsa', $details); + + $this->assertEquals($details['key'], $result->getPublicKey()->getPEM()); } /** @@ -86,14 +89,14 @@ public function test_generateKeyPair_generate_random_instance_of_KeyPair_using_E $result = $this->service->generateKeyPair(new EcKeyOption('secp112r1')); $this->assertInstanceOf(KeyPair::class, $result); - $this->assertStringContainsString('-----BEGIN PUBLIC KEY-----', $result->getPublicKey()->getPEM()); - $this->assertStringContainsString('-----BEGIN EC PRIVATE KEY-----', $result->getPrivateKey()->getPEM()); - $this->assertIsResource($result->getPublicKey()->getResource()); - $this->assertIsResource($result->getPrivateKey()->getResource()); + $this->assertIsOpenSslAsymmetricKey($result->getPublicKey()->getResource()); + $this->assertIsOpenSslAsymmetricKey($result->getPrivateKey()->getResource()); $details = openssl_pkey_get_details($result->getPrivateKey()->getResource()); $this->assertEquals(112, $details['bits']); $this->assertArrayHasKey('ec', $details); $this->assertEquals('secp112r1', $details['ec']['curve_name']); + + $this->assertEquals($details['key'], $result->getPublicKey()->getPEM()); } } From 2755d98e75c73706f180a5641adc9bac1f8bcff5 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Tue, 31 May 2022 18:46:13 +0200 Subject: [PATCH 046/121] Add PHP 8.1 tests and fix deprecations --- .github/workflows/test-build.yaml | 15 +++++++++++++++ src/Core/Http/SecureHttpClient.php | 8 +++++--- tests/Core/Challenge/Http/HttpValidatorTest.php | 5 ++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 4f472c25..116c935a 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -81,3 +81,18 @@ jobs: - name: Running tests run: ./tests/run.sh + tests-php81-deps-high-pebble-eab: + runs-on: ubuntu-latest + env: + PEBBLE_MODE: eab + steps: + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + - uses: actions/checkout@master + - name: Install dependencies + run: composer update --no-interaction --no-progress --ansi --prefer-stable + - name: Preparing tests + run: ./tests/setup.sh + - name: Running tests + run: ./tests/run.sh diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index d53f873a..7a2a0ed7 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -26,6 +26,7 @@ use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Utils; use Lcobucci\JWT\Signer\Hmac\Sha256; +use Lcobucci\JWT\Signer\Key\InMemory; use Psr\Http\Message\ResponseInterface; /** @@ -172,9 +173,10 @@ public function createExternalAccountPayload(ExternalAccount $externalAccount, s $encodedProtected = $this->base64Encoder->encode(json_encode($protected, JSON_UNESCAPED_SLASHES)); $encodedPayload = $this->base64Encoder->encode(json_encode($this->getJWK(), JSON_UNESCAPED_SLASHES)); - $signature = $this->base64Encoder->encode( - (string) $signer->sign($encodedProtected.'.'.$encodedPayload, $this->base64Encoder->decode($externalAccount->getHmacKey())) - ); + $hmacKey = $this->base64Encoder->decode($externalAccount->getHmacKey()); + $hmacKey = class_exists(InMemory::class) ? InMemory::plainText($hmacKey) : $hmacKey; + + $signature = $this->base64Encoder->encode((string) $signer->sign($encodedProtected.'.'.$encodedPayload, $hmacKey)); return [ 'protected' => $encodedProtected, diff --git a/tests/Core/Challenge/Http/HttpValidatorTest.php b/tests/Core/Challenge/Http/HttpValidatorTest.php index 98cf691f..116fbe45 100644 --- a/tests/Core/Challenge/Http/HttpValidatorTest.php +++ b/tests/Core/Challenge/Http/HttpValidatorTest.php @@ -79,10 +79,13 @@ public function testIsValidCatchExceptions() $mockExtractor->getCheckUrl($stubChallenge->reveal())->willReturn($checkUrl); $mockExtractor->getCheckContent($stubChallenge->reveal())->willReturn($checkContent); + $mockResponse = $this->prophesize(ResponseInterface::class); + $mockResponse->getStatusCode()->willReturn(400); + $mockHttpClient->get($checkUrl, ['verify' => false])->willThrow(new ClientException( 'boom', $this->prophesize(RequestInterface::class)->reveal(), - $this->prophesize(ResponseInterface::class)->reveal() + $mockResponse->reveal() )); $this->assertFalse($validator->isValid($stubChallenge->reveal(), $this->prophesize(SolverInterface::class)->reveal())); From 2cfc866bafa9395cf38ad684ef40868713eafb0a Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Tue, 31 May 2022 19:09:48 +0200 Subject: [PATCH 047/121] Allow Symfony 6 --- .github/workflows/test-build.yaml | 69 +++++++++++++++++++++++++----- composer.json | 23 +++++----- src/Cli/Application.php | 5 ++- src/Cli/Serializer/PemEncoder.php | 4 +- src/Core/Http/SecureHttpClient.php | 4 +- 5 files changed, 76 insertions(+), 29 deletions(-) diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 116c935a..4269520e 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -13,86 +13,135 @@ jobs: - uses: shivammathur/setup-php@v2 with: php-version: '7.2' + coverage: none + - uses: actions/checkout@master + - name: php-cs-fixer run: | wget https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v2.16.4/php-cs-fixer.phar -q php php-cs-fixer.phar fix --dry-run --diff - tests-php72-deps-low-pebble-default: + tests-php72-sf50-low: runs-on: ubuntu-latest + env: + SYMFONY_VERSION: 5.0.* steps: - uses: shivammathur/setup-php@v2 with: php-version: '7.2' + coverage: none + - uses: actions/checkout@master + - name: Install dependencies run: | composer require --dev "sebastian/comparator:^2.0" + composer require --no-update symfony/config=$SYMFONY_VERSION symfony/console=$SYMFONY_VERSION symfony/dependency-injection=$SYMFONY_VERSION symfony/filesystem=$SYMFONY_VERSION symfony/serializer=$SYMFONY_VERSION symfony/yaml=$SYMFONY_VERSION + composer require --no-update --dev symfony/finder=$SYMFONY_VERSION composer update --no-interaction --no-progress --ansi --prefer-lowest --prefer-stable + - name: Preparing tests run: ./tests/setup.sh + - name: Running tests run: ./tests/run.sh - tests-php73-deps-lock-pebble-default: + tests-php73-sf54-low: runs-on: ubuntu-latest + env: + SYMFONY_VERSION: 5.4.* steps: - uses: shivammathur/setup-php@v2 with: php-version: '7.3' + coverage: none + - uses: actions/checkout@master + - name: Install dependencies - run: composer install + run: | + composer require --dev "sebastian/comparator:^2.0" + composer require --no-update symfony/config=$SYMFONY_VERSION symfony/console=$SYMFONY_VERSION symfony/dependency-injection=$SYMFONY_VERSION symfony/filesystem=$SYMFONY_VERSION symfony/serializer=$SYMFONY_VERSION symfony/yaml=$SYMFONY_VERSION + composer require --no-update --dev symfony/finder=$SYMFONY_VERSION + composer update --no-interaction --no-progress --ansi --prefer-lowest --prefer-stable + - name: Preparing tests run: ./tests/setup.sh + - name: Running tests run: ./tests/run.sh - tests-php74-deps-high-pebble-eab: + tests-php74-sf54-high-eab: runs-on: ubuntu-latest env: + SYMFONY_VERSION: 5.4.* PEBBLE_MODE: eab steps: - uses: shivammathur/setup-php@v2 with: php-version: '7.4' + coverage: none + - uses: actions/checkout@master + - name: Install dependencies - run: composer update --no-interaction --no-progress --ansi --prefer-stable + run: | + composer require --no-update symfony/config=$SYMFONY_VERSION symfony/console=$SYMFONY_VERSION symfony/dependency-injection=$SYMFONY_VERSION symfony/filesystem=$SYMFONY_VERSION symfony/serializer=$SYMFONY_VERSION symfony/yaml=$SYMFONY_VERSION + composer require --no-update --dev symfony/finder=$SYMFONY_VERSION + composer update --no-interaction --no-progress --ansi --prefer-stable + - name: Preparing tests run: ./tests/setup.sh + - name: Running tests run: ./tests/run.sh - tests-php80-deps-high-pebble-eab: + tests-php80-sf60-high: runs-on: ubuntu-latest env: - PEBBLE_MODE: eab + SYMFONY_VERSION: 6.0.* steps: - uses: shivammathur/setup-php@v2 with: php-version: '8.0' + coverage: none + - uses: actions/checkout@master + - name: Install dependencies - run: composer update --no-interaction --no-progress --ansi --prefer-stable + run: | + composer require --no-update symfony/config=$SYMFONY_VERSION symfony/console=$SYMFONY_VERSION symfony/dependency-injection=$SYMFONY_VERSION symfony/filesystem=$SYMFONY_VERSION symfony/serializer=$SYMFONY_VERSION symfony/yaml=$SYMFONY_VERSION + composer require --no-update --dev symfony/finder=$SYMFONY_VERSION + composer update --no-interaction --no-progress --ansi --prefer-stable + - name: Preparing tests run: ./tests/setup.sh + - name: Running tests run: ./tests/run.sh - tests-php81-deps-high-pebble-eab: + tests-php81-sf61-high-eab: runs-on: ubuntu-latest env: + SYMFONY_VERSION: 6.1.* PEBBLE_MODE: eab steps: - uses: shivammathur/setup-php@v2 with: php-version: '8.1' + coverage: none + - uses: actions/checkout@master + - name: Install dependencies - run: composer update --no-interaction --no-progress --ansi --prefer-stable + run: | + composer require --no-update symfony/config=$SYMFONY_VERSION symfony/console=$SYMFONY_VERSION symfony/dependency-injection=$SYMFONY_VERSION symfony/filesystem=$SYMFONY_VERSION symfony/serializer=$SYMFONY_VERSION symfony/yaml=$SYMFONY_VERSION + composer require --no-update --dev symfony/finder=$SYMFONY_VERSION + composer update --no-interaction --no-progress --ansi --prefer-stable + - name: Preparing tests run: ./tests/setup.sh + - name: Running tests run: ./tests/run.sh diff --git a/composer.json b/composer.json index d9942c84..73f9f258 100644 --- a/composer.json +++ b/composer.json @@ -56,12 +56,12 @@ "psr/http-message": "^1.0", "psr/log": "^1.0", "swiftmailer/swiftmailer": "^5.4|^6.0", - "symfony/config": "^5.0", - "symfony/console": "^5.0", - "symfony/dependency-injection": "^5.0", - "symfony/filesystem": "^5.0", - "symfony/serializer": "^5.0", - "symfony/yaml": "^5.0", + "symfony/config": "^5.0|^6.0", + "symfony/console": "^5.0|^6.0", + "symfony/dependency-injection": "^5.0|^6.0", + "symfony/filesystem": "^5.0|^6.0", + "symfony/serializer": "^5.0|^6.0", + "symfony/yaml": "^5.0|^6.0", "webmozart/assert": "^1.0", "webmozart/path-util": "^2.3" }, @@ -70,9 +70,9 @@ }, "require-dev": { "phpspec/prophecy": "^1.9", - "symfony/finder": "^5.0", - "symfony/phpunit-bridge": "^5.0", - "symfony/var-dumper": "^5.0" + "symfony/finder": "^5.0|^6.0", + "symfony/phpunit-bridge": "^5.0|^6.0", + "symfony/var-dumper": "^5.0|^6.0" }, "autoload": { "psr-4": { @@ -85,9 +85,6 @@ } }, "config": { - "sort-packages": true, - "platform": { - "php": "7.2.34" - } + "sort-packages": true } } diff --git a/src/Cli/Application.php b/src/Cli/Application.php index caf03a68..19bb14db 100644 --- a/src/Cli/Application.php +++ b/src/Cli/Application.php @@ -17,6 +17,7 @@ use AcmePhp\Cli\Command\SelfUpdateCommand; use AcmePhp\Cli\Command\StatusCommand; use Symfony\Component\Console\Application as BaseApplication; +use Symfony\Component\Console\Helper\HelperSet; use Webmozart\PathUtil\Path; /** @@ -41,7 +42,7 @@ public function __construct() /** * {@inheritdoc} */ - protected function getDefaultCommands() + protected function getDefaultCommands(): array { return array_merge(parent::getDefaultCommands(), [ new RunCommand(), @@ -54,7 +55,7 @@ protected function getDefaultCommands() /** * {@inheritdoc} */ - protected function getDefaultHelperSet() + protected function getDefaultHelperSet(): HelperSet { $set = parent::getDefaultHelperSet(); $set->set(new DistinguishedNameHelper()); diff --git a/src/Cli/Serializer/PemEncoder.php b/src/Cli/Serializer/PemEncoder.php index d5f8d117..d9f15c35 100644 --- a/src/Cli/Serializer/PemEncoder.php +++ b/src/Cli/Serializer/PemEncoder.php @@ -24,7 +24,7 @@ class PemEncoder implements EncoderInterface, DecoderInterface /** * {@inheritdoc} */ - public function encode($data, $format, array $context = []) + public function encode($data, $format, array $context = []): string { return trim($data)."\n"; } @@ -40,7 +40,7 @@ public function decode($data, $format, array $context = []) /** * {@inheritdoc} */ - public function supportsEncoding($format) + public function supportsEncoding($format): bool { return self::FORMAT === $format; } diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index 7a2a0ed7..acb05b59 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -165,7 +165,7 @@ public function createExternalAccountPayload(ExternalAccount $externalAccount, s $signer = new Sha256(); $protected = [ - 'alg' => $signer->getAlgorithmId(), + 'alg' => method_exists($signer, 'algorithmId') ? $signer->algorithmId() : $signer->getAlgorithmId(), 'kid' => $externalAccount->getId(), 'url' => $url, ]; @@ -176,7 +176,7 @@ public function createExternalAccountPayload(ExternalAccount $externalAccount, s $hmacKey = $this->base64Encoder->decode($externalAccount->getHmacKey()); $hmacKey = class_exists(InMemory::class) ? InMemory::plainText($hmacKey) : $hmacKey; - $signature = $this->base64Encoder->encode((string) $signer->sign($encodedProtected.'.'.$encodedPayload, $hmacKey)); + $signature = $this->base64Encoder->encode($signer->sign($encodedProtected.'.'.$encodedPayload, $hmacKey)); return [ 'protected' => $encodedProtected, From 043fd6cd896597d2c05a9565ab620a045b57f8ef Mon Sep 17 00:00:00 2001 From: Thomas Bickley Date: Wed, 10 Mar 2021 16:04:52 +0000 Subject: [PATCH 048/121] Allow Monolog version 2 --- composer.json | 2 +- src/Cli/Monolog/ConsoleFormatter.php | 2 +- src/Cli/Monolog/ConsoleHandler.php | 11 ++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 73f9f258..9692dad7 100644 --- a/composer.json +++ b/composer.json @@ -50,7 +50,7 @@ "league/flysystem": "^1.0.19", "league/flysystem-memory": "^1.0", "league/flysystem-sftp": "^1.0.7", - "monolog/monolog": "^1.19", + "monolog/monolog": "^1.19|^2.0", "padraic/phar-updater": "^1.0", "psr/container": "^1.0", "psr/http-message": "^1.0", diff --git a/src/Cli/Monolog/ConsoleFormatter.php b/src/Cli/Monolog/ConsoleFormatter.php index 21fda4ca..8cb29c2b 100644 --- a/src/Cli/Monolog/ConsoleFormatter.php +++ b/src/Cli/Monolog/ConsoleFormatter.php @@ -38,7 +38,7 @@ public function __construct($format = null, $dateFormat = null, $allowInlineLine /** * {@inheritdoc} */ - public function format(array $record) + public function format(array $record): string { if ($record['level'] >= Logger::ERROR) { $record['start_tag'] = ''; diff --git a/src/Cli/Monolog/ConsoleHandler.php b/src/Cli/Monolog/ConsoleHandler.php index 94692863..45cc8bde 100644 --- a/src/Cli/Monolog/ConsoleHandler.php +++ b/src/Cli/Monolog/ConsoleHandler.php @@ -11,6 +11,7 @@ namespace AcmePhp\Cli\Monolog; +use Monolog\Formatter\FormatterInterface; use Monolog\Handler\AbstractProcessingHandler; use Monolog\Logger; use Symfony\Component\Console\Output\OutputInterface; @@ -65,7 +66,7 @@ public function __construct(OutputInterface $output = null, bool $bubble = true, /** * {@inheritdoc} */ - public function isHandling(array $record) + public function isHandling(array $record): bool { return $this->updateLevel() && parent::isHandling($record); } @@ -73,7 +74,7 @@ public function isHandling(array $record) /** * {@inheritdoc} */ - public function handle(array $record) + public function handle(array $record): bool { // we have to update the logging level each time because the verbosity of the // console output might have changed in the meantime (it is not immutable) @@ -93,7 +94,7 @@ public function setOutput(OutputInterface $output) /** * Disables the output. */ - public function close() + public function close(): void { $this->output = null; @@ -103,7 +104,7 @@ public function close() /** * {@inheritdoc} */ - protected function write(array $record) + protected function write(array $record): void { $this->output->write((string) $record['formatted']); } @@ -111,7 +112,7 @@ protected function write(array $record) /** * {@inheritdoc} */ - protected function getDefaultFormatter() + protected function getDefaultFormatter(): FormatterInterface { $formatter = new ConsoleFormatter(); $formatter->allowInlineLineBreaks(); From d685b932967c9b992791d7f54e98161c6d9ef229 Mon Sep 17 00:00:00 2001 From: W0rma Date: Sat, 1 Jan 2022 23:37:09 +0100 Subject: [PATCH 049/121] Fix badges in README --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 389a42d2..7ec35b55 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,10 @@ Acme PHP ======== -[![Build Status](https://img.shields.io/travis/acmephp/acmephp/master.svg?style=flat-square)](https://travis-ci.org/acmephp/acmephp) +[![Build Status](https://img.shields.io/github/workflow/status/acmephp/acmephp/Test%20and%20build?style=flat-square)](https://github.com/acmephp/acmephp/actions?query=branch%3Amaster+workflow%3A%22Test+and+build%22) [![Packagist Version](https://img.shields.io/packagist/v/acmephp/acmephp.svg?style=flat-square)](https://packagist.org/packages/acmephp/acmephp) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) -[![SymfonyInsight](https://insight.symfony.com/projects/4eb121bf-9f9d-4d16-813b-f98f07003eaf/big.svg)](https://insight.symfony.com/projects/4eb121bf-9f9d-4d16-813b-f98f07003eaf) - Acme PHP is a simple yet very extensible CLI client for Let's Encrypt that will help you get and renew free HTTPS certificates. From 2ae3edf26314a1d626f83cac97608cee553f3523 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Tue, 31 May 2022 19:28:37 +0200 Subject: [PATCH 050/121] Upgrade PHP-CS-Fixer --- .github/workflows/test-build.yaml | 11 +++++---- .gitignore | 2 +- .php-cs-fixer.dist.php | 26 ++++++++++++++++++++ .php_cs | 40 ------------------------------- 4 files changed, 33 insertions(+), 46 deletions(-) create mode 100644 .php-cs-fixer.dist.php delete mode 100644 .php_cs diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 4269520e..a3af626f 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -12,15 +12,16 @@ jobs: steps: - uses: shivammathur/setup-php@v2 with: - php-version: '7.2' + php-version: '8.1' coverage: none - uses: actions/checkout@master - - name: php-cs-fixer - run: | - wget https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v2.16.4/php-cs-fixer.phar -q - php php-cs-fixer.phar fix --dry-run --diff + - name: Install php-cs-fixer + run: wget https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v3.4.0/php-cs-fixer.phar -q + + - name: Check coding style + run: php php-cs-fixer.phar fix --dry-run --diff tests-php72-sf50-low: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index afc37e8d..a3c77191 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ /box.json composer.lock vendor/ -.php_cs.cache +.php-cs-fixer.cache doc/.couscous .subsplit .phpunit.result.cache diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 00000000..be3821f3 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,26 @@ + + +For the full copyright and license information, please view the LICENSE +file that was distributed with this source code. +EOF; + +$finder = PhpCsFixer\Finder::create() + ->in(__DIR__.'/bin') + ->in(__DIR__.'/src') + ->in(__DIR__.'/tests') +; + +return (new PhpCsFixer\Config()) + ->setFinder($finder) + ->setRules([ + '@Symfony' => true, + 'array_syntax' => ['syntax' => 'short'], + 'phpdoc_annotation_without_dot' => false, + 'header_comment' => ['header' => $header], + ]) +; diff --git a/.php_cs b/.php_cs deleted file mode 100644 index acd8b667..00000000 --- a/.php_cs +++ /dev/null @@ -1,40 +0,0 @@ - - -For the full copyright and license information, please view the LICENSE -file that was distributed with this source code. -EOF; - -$config = PhpCsFixer\Config::create() - ->setRiskyAllowed(true) - ->setRules([ - '@Symfony' => true, - '@Symfony:risky' => true, - 'array_syntax' => ['syntax' => 'short'], - 'ordered_imports' => true, - 'header_comment' => ['header' => $header], - 'linebreak_after_opening_tag' => true, - 'modernize_types_casting' => true, - 'native_function_invocation' => ['include' => ['@compiler_optimized'], 'scope' => 'namespaced'], - 'no_superfluous_elseif' => true, - 'no_useless_else' => true, - 'phpdoc_order' => true, - 'psr4' => true, - 'simplified_null_return' => true, - 'no_useless_return' => true, - 'strict_comparison' => true, - 'yoda_style' => true, - ]) - ->setFinder( - PhpCsFixer\Finder::create() - ->in(__DIR__.'/') - ->exclude('vendor') - ->name('*.php') - ) -; - -return $config; From 182d539c113d86de07050e19ce6fc2bae44e1784 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Tue, 31 May 2022 19:29:05 +0200 Subject: [PATCH 051/121] Fix CS --- src/Cli/Command/SelfUpdateCommand.php | 8 ++++---- src/Cli/Monolog/ConsoleFormatter.php | 2 +- tests/Ssl/CertificateTest.php | 4 ++-- tests/Ssl/Generator/KeyPairGeneratorTest.php | 8 ++++---- tests/Ssl/Parser/CertificateParserTest.php | 6 +++--- tests/Ssl/Parser/KeyParserTest.php | 12 ++++++------ tests/Ssl/PrivateKeyTest.php | 6 +++--- tests/Ssl/PublicKeyTest.php | 6 +++--- tests/Ssl/Signer/CertificateRequestSignerTest.php | 6 +++--- tests/Ssl/Signer/DataSignerTest.php | 4 ++-- 10 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/Cli/Command/SelfUpdateCommand.php b/src/Cli/Command/SelfUpdateCommand.php index bb11a37f..b2f03b33 100644 --- a/src/Cli/Command/SelfUpdateCommand.php +++ b/src/Cli/Command/SelfUpdateCommand.php @@ -29,10 +29,10 @@ */ class SelfUpdateCommand extends Command { - const PACKAGE_NAME = 'acmephp/acmephp'; - const FILE_NAME = 'acmephp.phar'; - const VERSION_URL = 'https://acmephp.github.io/downloads/acmephp.version'; - const PHAR_URL = 'https://acmephp.github.io/downloads/acmephp.phar'; + public const PACKAGE_NAME = 'acmephp/acmephp'; + public const FILE_NAME = 'acmephp.phar'; + public const VERSION_URL = 'https://acmephp.github.io/downloads/acmephp.version'; + public const PHAR_URL = 'https://acmephp.github.io/downloads/acmephp.phar'; /** * @var string diff --git a/src/Cli/Monolog/ConsoleFormatter.php b/src/Cli/Monolog/ConsoleFormatter.php index 8cb29c2b..e9ad5281 100644 --- a/src/Cli/Monolog/ConsoleFormatter.php +++ b/src/Cli/Monolog/ConsoleFormatter.php @@ -25,7 +25,7 @@ */ class ConsoleFormatter extends LineFormatter { - const SIMPLE_FORMAT = "%start_tag%%message% %context% %extra%%end_tag%\n"; + public const SIMPLE_FORMAT = "%start_tag%%message% %context% %extra%%end_tag%\n"; /** * {@inheritdoc} diff --git a/tests/Ssl/CertificateTest.php b/tests/Ssl/CertificateTest.php index 12fe63c9..24b0a4c2 100644 --- a/tests/Ssl/CertificateTest.php +++ b/tests/Ssl/CertificateTest.php @@ -19,7 +19,7 @@ class CertificateTest extends TestCase { use AssertsOpenSslResource; - public function test_getPublicKey_returns_a_PublicKey() + public function testGetPublicKeyReturnsAPublicKey() { $certificate = new Certificate( ' @@ -63,7 +63,7 @@ public function test_getPublicKey_returns_a_PublicKey() $this->assertEquals('58b94e38ce0088f0ec5a0c38f04bd76c', md5($publicKey->getPEM())); } - public function test_getPublicKeyResource_returns_a_resource() + public function testGetPublicKeyResourceReturnsAResource() { $certificate = new Certificate( ' diff --git a/tests/Ssl/Generator/KeyPairGeneratorTest.php b/tests/Ssl/Generator/KeyPairGeneratorTest.php index 1c6739d4..49df2602 100644 --- a/tests/Ssl/Generator/KeyPairGeneratorTest.php +++ b/tests/Ssl/Generator/KeyPairGeneratorTest.php @@ -34,7 +34,7 @@ public function setUp(): void $this->service = new KeyPairGenerator(); } - public function test_generateKeyPair_generate_random_instance_of_KeyPair() + public function testGenerateKeyPairGenerateRandomInstanceOfKeyPair() { $result = $this->service->generateKeyPair(new RsaKeyOption(1024)); @@ -49,7 +49,7 @@ public function test_generateKeyPair_generate_random_instance_of_KeyPair() $this->assertEquals($details['key'], $result->getPublicKey()->getPEM()); } - public function test_generateKeyPair_generate_random_instance_of_KeyPair_using_DH() + public function testGenerateKeyPairGenerateRandomInstanceOfKeyPairUsingDH() { $result = $this->service->generateKeyPair(new DhKeyOption( 'dcf93a0b883972ec0e19989ac5a2ce310e1d37717e8d9571bb7623731866e61ef75a2e27898b057f9891c2e27a639c3f29b60814581cd3b2ca3986d2683705577d45c2e7e52dc81c7a171876e5cea74b1448bfdfaf18828efd2519f14e45e3826634af1949e5b535cc829a483b8a76223e5d490a257f05bdff16f2fb22c583ab', @@ -66,7 +66,7 @@ public function test_generateKeyPair_generate_random_instance_of_KeyPair_using_D $this->assertEquals($details['key'], $result->getPublicKey()->getPEM()); } - public function test_generateKeyPair_generate_random_instance_of_KeyPair_using_DSA() + public function testGenerateKeyPairGenerateRandomInstanceOfKeyPairUsingDSA() { $result = $this->service->generateKeyPair(new DsaKeyOption(1024)); @@ -84,7 +84,7 @@ public function test_generateKeyPair_generate_random_instance_of_KeyPair_using_D /** * @requires PHP 7.1 */ - public function test_generateKeyPair_generate_random_instance_of_KeyPair_using_EC() + public function testGenerateKeyPairGenerateRandomInstanceOfKeyPairUsingEC() { $result = $this->service->generateKeyPair(new EcKeyOption('secp112r1')); diff --git a/tests/Ssl/Parser/CertificateParserTest.php b/tests/Ssl/Parser/CertificateParserTest.php index 06fe6a37..7b82373e 100644 --- a/tests/Ssl/Parser/CertificateParserTest.php +++ b/tests/Ssl/Parser/CertificateParserTest.php @@ -28,13 +28,13 @@ public function setUp(): void $this->service = new CertificateParser(); } - public function test_parse_raise_proper_exception() + public function testParseRaiseProperException() { $this->expectException('AcmePhp\Ssl\Exception\CertificateParsingException'); $this->service->parse(new Certificate('Not a cert')); } - public function test_parse_returns_instance_of_ParsedCertificate() + public function testParseReturnsInstanceOfParsedCertificate() { $result = $this->service->parse( new Certificate( @@ -88,7 +88,7 @@ public function test_parse_returns_instance_of_ParsedCertificate() $this->assertFalse($result->isSelfSigned()); } - public function test_parse_without_issuer_CN_returns_instance_of_ParsedCertificate() + public function testParseWithoutIssuerCNReturnsInstanceOfParsedCertificate() { $result = $this->service->parse( new Certificate( diff --git a/tests/Ssl/Parser/KeyParserTest.php b/tests/Ssl/Parser/KeyParserTest.php index 991ede29..93e8a4e4 100644 --- a/tests/Ssl/Parser/KeyParserTest.php +++ b/tests/Ssl/Parser/KeyParserTest.php @@ -30,30 +30,30 @@ public function setUp(): void $this->service = new KeyParser(); } - public function test_parse_PublicKey_raise_proper_exception() + public function testParsePublicKeyRaiseProperException() { $this->expectException('AcmePhp\Ssl\Exception\KeyParsingException'); $this->service->parse(new PublicKey('Not a key')); } - public function test_parse_PrivateKey_raise_proper_exception() + public function testParsePrivateKeyRaiseProperException() { $this->expectException('AcmePhp\Ssl\Exception\KeyParsingException'); $this->service->parse(new PrivateKey('Not a key')); } - public function test_get_PrivateKey_has_invalid_detail() + public function testGetPrivateKeyHasInvalidDetail() { $this->assertFalse($this->service->parse($this->getPrivateKey())->hasDetail('invalid')); } - public function test_get_PrivateKey_get_invalid_detail_raise_proper_exception() + public function testGetPrivateKeyGetInvalidDetailRaiseProperException() { $this->expectException('InvalidArgumentException'); $this->service->parse($this->getPrivateKey())->getDetail('invalid'); } - public function test_parse_PrivateKey_returns_instance_of_ParsedKey() + public function testParsePrivateKeyReturnsInstanceOfParsedKey() { $result = $this->service->parse($this->getPrivateKey()); @@ -67,7 +67,7 @@ public function test_parse_PrivateKey_returns_instance_of_ParsedKey() $this->assertEquals(trim($this->getPublicKey()->getPEM()), trim($result->getKey())); } - public function test_parse_PublicKey_returns_instance_of_ParsedKey() + public function testParsePublicKeyReturnsInstanceOfParsedKey() { $result = $this->service->parse($this->getPublicKey()); diff --git a/tests/Ssl/PrivateKeyTest.php b/tests/Ssl/PrivateKeyTest.php index bd3cd155..e59cbe50 100644 --- a/tests/Ssl/PrivateKeyTest.php +++ b/tests/Ssl/PrivateKeyTest.php @@ -17,7 +17,7 @@ class PrivateKeyTest extends TestCase { - public function test_getPublicKey_returns_a_PublicKey() + public function testGetPublicKeyReturnsAPublicKey() { $privateKey = new PrivateKey('-----BEGIN PRIVATE KEY----- MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDH3IKV8sJZZHGd @@ -78,7 +78,7 @@ public function test_getPublicKey_returns_a_PublicKey() $this->assertEquals('80969771cf03d0331d1911810feff5fc', md5($publicKey->getPEM())); } - public function test_fromDER_returns_a_PrivateKey() + public function testFromDERReturnsAPrivateKey() { $derb64 = 'MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDH3IKV8sJZZHGd Q0vUN9GHJACixg8N1wFpUe763HmnWwiyCFHK9YjOfxkDSRK+2lP72Ns+RTBwtM8s @@ -137,7 +137,7 @@ public function test_fromDER_returns_a_PrivateKey() $this->assertEquals('a6dcb8eaae257961d2ee888899f087ef', md5($privateKey->getPEM())); } - public function test_getDER_returns_a_string() + public function testGetDERReturnsAString() { $privateKey = new PrivateKey('-----BEGIN PRIVATE KEY----- MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDH3IKV8sJZZHGd diff --git a/tests/Ssl/PublicKeyTest.php b/tests/Ssl/PublicKeyTest.php index bc4034fe..ac44e63a 100644 --- a/tests/Ssl/PublicKeyTest.php +++ b/tests/Ssl/PublicKeyTest.php @@ -16,7 +16,7 @@ class PublicKeyTest extends TestCase { - public function test_fromDER_returns_a_PublicKey() + public function testFromDERReturnsAPublicKey() { $derb64 = 'MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAx9yClfLCWWRxnUNL1DfR hyQAosYPDdcBaVHu+tx5p1sIsghRyvWIzn8ZA0kSvtpT+9jbPkUwcLTPLGW0SAC8 @@ -37,7 +37,7 @@ public function test_fromDER_returns_a_PublicKey() $this->assertEquals('48fa4235a71c704c815363702d7effbb', md5($publicKey->getPEM())); } - public function test_getDER_returns_a_string() + public function testGetDERReturnsAString() { $publicKey = new PublicKey('-----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAx9yClfLCWWRxnUNL1DfR @@ -60,7 +60,7 @@ public function test_getDER_returns_a_string() $this->assertEquals('d2ea173bab74794037c74653b65433af', md5($der)); } - public function test_getHPKP_returns_a_string() + public function testGetHPKPReturnsAString() { $publicKey = new PublicKey('-----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAx9yClfLCWWRxnUNL1DfR diff --git a/tests/Ssl/Signer/CertificateRequestSignerTest.php b/tests/Ssl/Signer/CertificateRequestSignerTest.php index bb9a660b..473d6e6b 100644 --- a/tests/Ssl/Signer/CertificateRequestSignerTest.php +++ b/tests/Ssl/Signer/CertificateRequestSignerTest.php @@ -30,7 +30,7 @@ public function setUp(): void $this->service = new CertificateRequestSigner(); } - public function test_signCertificateRequest_returns_a_certificate() + public function testSignCertificateRequestReturnsACertificate() { $dummyDistinguishedName = new DistinguishedName( 'acmephp.com', @@ -59,7 +59,7 @@ public function test_signCertificateRequest_returns_a_certificate() ); } - public function test_signCertificateRequest_use_default_values() + public function testSignCertificateRequestUseDefaultValues() { $dummyDistinguishedName = new DistinguishedName( 'acmephp.com' @@ -80,7 +80,7 @@ public function test_signCertificateRequest_use_default_values() ); } - public function test_signCertificateRequest_with_subject_alternative_names() + public function testSignCertificateRequestWithSubjectAlternativeNames() { $dummyDistinguishedName = new DistinguishedName( 'acmephp.com', diff --git a/tests/Ssl/Signer/DataSignerTest.php b/tests/Ssl/Signer/DataSignerTest.php index fce80d15..cf54d1a6 100644 --- a/tests/Ssl/Signer/DataSignerTest.php +++ b/tests/Ssl/Signer/DataSignerTest.php @@ -30,7 +30,7 @@ public function setUp(): void $this->service = new DataSigner(); } - public function test_signData_returns_a_signature() + public function testSignDataReturnsASignature() { $privateRsaKey = (new RsaKeyGenerator())->generatePrivateKey(new RsaKeyOption()); @@ -51,7 +51,7 @@ public function test_signData_returns_a_signature() /** * @requires PHP 7.1 */ - public function test_signData_returns_a_signature_for_ec_keys() + public function testSignDataReturnsASignatureForEcKeys() { $this->assertEquals(64, \strlen($this->service->signData('foo', (new EcKeyGenerator())->generatePrivateKey(new EcKeyOption('prime256v1')), OPENSSL_ALGO_SHA256, DataSigner::FORMAT_ECDSA))); $this->assertEquals(96, \strlen($this->service->signData('foo', (new EcKeyGenerator())->generatePrivateKey(new EcKeyOption('secp384r1')), OPENSSL_ALGO_SHA384, DataSigner::FORMAT_ECDSA))); From 03d14b353ef16c9af40d7de0803ac52dc31319d1 Mon Sep 17 00:00:00 2001 From: W0rma Date: Sat, 1 Jan 2022 22:49:53 +0100 Subject: [PATCH 052/121] Remove unused swiftmailer dependency --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 9692dad7..a358b3c8 100644 --- a/composer.json +++ b/composer.json @@ -55,7 +55,6 @@ "psr/container": "^1.0", "psr/http-message": "^1.0", "psr/log": "^1.0", - "swiftmailer/swiftmailer": "^5.4|^6.0", "symfony/config": "^5.0|^6.0", "symfony/console": "^5.0|^6.0", "symfony/dependency-injection": "^5.0|^6.0", From d345ffe46edaaa64f5d8f99f3ca7729342101ac4 Mon Sep 17 00:00:00 2001 From: Mike Griego Date: Thu, 24 Feb 2022 12:15:02 -0600 Subject: [PATCH 053/121] Allow for newer dependencies and fix a couple of issues in AcmePhp\Core. --- src/Core/AcmeClient.php | 8 ++++++-- src/Core/Http/SecureHttpClient.php | 2 +- src/Core/Http/ServerErrorHandler.php | 5 +++-- src/Core/composer.json | 4 ++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 77b8618a..1d2c897d 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -327,9 +327,13 @@ protected function requestResource(string $method, string $resource, array $payl private function createCertificateResponse(CertificateRequest $csr, string $certificate): CertificateResponse { + $certificateHeader = '-----BEGIN CERTIFICATE-----'; $certificatesChain = null; - foreach (array_reverse(explode("\n\n", $certificate)) as $pem) { - $certificatesChain = new Certificate($pem, $certificatesChain); + + foreach (array_reverse(explode($certificateHeader, $certificate)) as $pem) { + if (\trim($pem) !== '') { + $certificatesChain = new Certificate($certificateHeader . $pem, $certificatesChain); + } } return new CertificateResponse($csr, $certificatesChain); diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index acb05b59..efcd7cd0 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -211,7 +211,7 @@ public function request(string $method, string $endpoint, array $data = [], bool $data = JsonDecoder::decode($body, true); } catch (\InvalidArgumentException $exception) { - throw new ExpectedJsonException(sprintf('ACME client excepted valid JSON as a response to request "%s %s" (given: "%s")', $method, $endpoint, ServerErrorHandler::getResponseBodySummary($response)), $exception); + throw new ExpectedJsonException(sprintf('ACME client expected valid JSON as a response to request "%s %s" (given: "%s")', $method, $endpoint, ServerErrorHandler::getResponseBodySummary($response)), $exception); } return $data; diff --git a/src/Core/Http/ServerErrorHandler.php b/src/Core/Http/ServerErrorHandler.php index 9b40c768..b161fa70 100644 --- a/src/Core/Http/ServerErrorHandler.php +++ b/src/Core/Http/ServerErrorHandler.php @@ -33,6 +33,7 @@ use AcmePhp\Core\Exception\Server\UserActionRequiredServerException; use AcmePhp\Core\Util\JsonDecoder; use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Psr7\Utils; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; @@ -80,7 +81,7 @@ public static function getResponseBodySummary(ResponseInterface $response): stri return RequestException::getResponseBodySummary($response); } - $body = \GuzzleHttp\Psr7\copy_to_string($response->getBody()); + $body = Utils::copyToString($response->getBody()); if (\strlen($body) > 120) { return substr($body, 0, 120).' (truncated...)'; @@ -94,7 +95,7 @@ public function createAcmeExceptionForResponse( ResponseInterface $response, \Exception $previous = null ): AcmeCoreServerException { - $body = \GuzzleHttp\Psr7\copy_to_string($response->getBody()); + $body = Utils::copyToString($response->getBody()); try { $data = JsonDecoder::decode($body, true); diff --git a/src/Core/composer.json b/src/Core/composer.json index 8e72fda8..2e82021a 100644 --- a/src/Core/composer.json +++ b/src/Core/composer.json @@ -33,10 +33,10 @@ "ext-openssl": "*", "acmephp/ssl": "^2.0", "guzzlehttp/guzzle": "^6.0|^7.0", - "guzzlehttp/psr7": "^1.0", + "guzzlehttp/psr7": "^1.7|^2.1", "lcobucci/jwt": "^3.3|^4.0", "psr/http-message": "^1.0", - "psr/log": "^1.0", + "psr/log": "^1.0|^2.0|^3.0", "webmozart/assert": "^1.0" }, "autoload": { From 09e4bead6167f601b2060da36a0c36ab1e73d0e8 Mon Sep 17 00:00:00 2001 From: Mike Griego Date: Thu, 10 Mar 2022 09:57:04 -0600 Subject: [PATCH 054/121] Serialize embedded authorization challenges when serializing order objects. --- src/Core/AcmeClient.php | 4 ++-- src/Core/Protocol/CertificateOrder.php | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 1d2c897d..709ceebf 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -331,8 +331,8 @@ private function createCertificateResponse(CertificateRequest $csr, string $cert $certificatesChain = null; foreach (array_reverse(explode($certificateHeader, $certificate)) as $pem) { - if (\trim($pem) !== '') { - $certificatesChain = new Certificate($certificateHeader . $pem, $certificatesChain); + if ('' !== \trim($pem)) { + $certificatesChain = new Certificate($certificateHeader.$pem, $certificatesChain); } } diff --git a/src/Core/Protocol/CertificateOrder.php b/src/Core/Protocol/CertificateOrder.php index a18236c3..8f343e2c 100644 --- a/src/Core/Protocol/CertificateOrder.php +++ b/src/Core/Protocol/CertificateOrder.php @@ -42,8 +42,20 @@ public function __construct(array $authorizationsChallenges, string $orderEndpoi public function toArray(): array { + $authorizationsChallenges = array_map( + function ($challenges): array { + return array_map( + function ($challenge): array { + return $challenge->toArray(); + }, + $challenges + ); + }, + $this->getAuthorizationsChallenges() + ); + return [ - 'authorizationsChallenges' => $this->getAuthorizationsChallenges(), + 'authorizationsChallenges' => $authorizationsChallenges, 'orderEndpoint' => $this->getOrderEndpoint(), ]; } From 95c17a4156843e2ddb404fc0cd83dbd6a342a81b Mon Sep 17 00:00:00 2001 From: Hans Adema Date: Wed, 1 Dec 2021 11:31:53 +0100 Subject: [PATCH 055/121] Add Core\AcmeClient::reloadOrder method The reloadOrder method refreshes CertificateOrder information from the CA. This is useful if the CA returned a server error, and you need to retrieve what has been done. Also add the order status property to the CertificateOrder object to check this. --- src/Core/AcmeClient.php | 27 +++++++++++++++++++++++++- src/Core/AcmeClientInterface.php | 5 +++++ src/Core/Protocol/CertificateOrder.php | 12 +++++++++++- tests/Core/AcmeClientTest.php | 10 ++++++++++ 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 709ceebf..24a9fb31 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -140,7 +140,32 @@ static function ($domain) { } } - return new CertificateOrder($authorizationsChallenges, $orderEndpoint); + return new CertificateOrder($authorizationsChallenges, $orderEndpoint, $response['status']); + } + + /** + * {@inheritdoc} + */ + public function reloadOrder(CertificateOrder $order): CertificateOrder + { + $client = $this->getHttpClient(); + $orderEndpoint = $order->getOrderEndpoint(); + $response = $client->request('POST', $orderEndpoint, $client->signKidPayload($orderEndpoint, $this->getResourceAccount(), null)); + + if (!isset($response['authorizations']) || !$response['authorizations']) { + throw new ChallengeNotSupportedException(); + } + + $authorizationsChallenges = []; + foreach ($response['authorizations'] as $authorizationEndpoint) { + $authorizationsResponse = $client->request('POST', $authorizationEndpoint, $client->signKidPayload($authorizationEndpoint, $this->getResourceAccount(), null)); + $domain = (empty($authorizationsResponse['wildcard']) ? '' : '*.').$authorizationsResponse['identifier']['value']; + foreach ($authorizationsResponse['challenges'] as $challenge) { + $authorizationsChallenges[$domain][] = $this->createAuthorizationChallenge($authorizationsResponse['identifier']['value'], $challenge); + } + } + + return new CertificateOrder($authorizationsChallenges, $orderEndpoint, $response['status']); } /** diff --git a/src/Core/AcmeClientInterface.php b/src/Core/AcmeClientInterface.php index f307fb7b..e035bbd7 100644 --- a/src/Core/AcmeClientInterface.php +++ b/src/Core/AcmeClientInterface.php @@ -66,6 +66,11 @@ public function registerAccount(string $email = null, ExternalAccount $externalA */ public function requestOrder(array $domains): CertificateOrder; + /** + * Request the current status of a certificate order. + */ + public function reloadOrder(CertificateOrder $order): CertificateOrder; + /** * Request a certificate for the given domain. * diff --git a/src/Core/Protocol/CertificateOrder.php b/src/Core/Protocol/CertificateOrder.php index 8f343e2c..a78bd830 100644 --- a/src/Core/Protocol/CertificateOrder.php +++ b/src/Core/Protocol/CertificateOrder.php @@ -26,7 +26,10 @@ class CertificateOrder /** @var string */ private $orderEndpoint; - public function __construct(array $authorizationsChallenges, string $orderEndpoint = null) + /** @var string */ + private $status; + + public function __construct(array $authorizationsChallenges, string $orderEndpoint = null, string $status = null) { foreach ($authorizationsChallenges as &$authorizationChallenges) { foreach ($authorizationChallenges as &$authorizationChallenge) { @@ -38,6 +41,7 @@ public function __construct(array $authorizationsChallenges, string $orderEndpoi $this->authorizationsChallenges = $authorizationsChallenges; $this->orderEndpoint = $orderEndpoint; + $this->status = $status; } public function toArray(): array @@ -57,6 +61,7 @@ function ($challenge): array { return [ 'authorizationsChallenges' => $authorizationsChallenges, 'orderEndpoint' => $this->getOrderEndpoint(), + 'status' => $this->getStatus(), ]; } @@ -89,4 +94,9 @@ public function getOrderEndpoint(): string { return $this->orderEndpoint; } + + public function getStatus(): string + { + return $this->status; + } } diff --git a/tests/Core/AcmeClientTest.php b/tests/Core/AcmeClientTest.php index 39383e73..1ba447c5 100644 --- a/tests/Core/AcmeClientTest.php +++ b/tests/Core/AcmeClientTest.php @@ -76,6 +76,7 @@ public function testFullProcess(KeyOption $keyOption, bool $useAlternateCertific * Ask for domain challenge */ $order = $client->requestOrder(['acmephp.com']); + $this->assertEquals('pending', $order->getStatus()); $challenges = $order->getAuthorizationChallenges('acmephp.com'); foreach ($challenges as $challenge) { if ('http-01' === $challenge->getType()) { @@ -100,6 +101,15 @@ public function testFullProcess(KeyOption $keyOption, bool $useAlternateCertific $this->cleanChallenge($challenge->getToken()); } + /** + * Reload order, check if challenge was completed. + */ + $updatedOrder = $client->reloadOrder($order); + $this->assertEquals('ready', $updatedOrder->getStatus()); + $this->assertCount(1, $updatedOrder->getAuthorizationChallenges('acmephp.com')); + $validatedChallenge = $updatedOrder->getAuthorizationChallenges('acmephp.com')[0]; + $this->assertEquals('valid', $validatedChallenge->getStatus()); + /* * Request certificate */ From 192a15cbc5be12572dcbdf435ddae74ffb96ded2 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Tue, 7 Jun 2022 22:41:57 +0200 Subject: [PATCH 056/121] Release of new version 2.1.0 --- CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcba8e10..213c3d73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +07/06/2022 22:41 2.1.0 Add compatibility for PHP 8.0/8.1, Symfony 6 and other improvements +8a8a975 Merge pull request #263 from acmephp/core-get-order +95c17a4 Add Core\AcmeClient::reloadOrder method +7bac887 Merge pull request #257 from mgriego/core-deps-and-bug-fixes +371c905 Merge pull request #251 from W0rma/remove-swiftmailer +09e4bea Serialize embedded authorization challenges when serializing order objects. +d345ffe Allow for newer dependencies and fix a couple of issues in AcmePhp\Core. +8be2586 Merge pull request #262 from acmephp/upgrade-phpcs +03d14b3 Remove unused swiftmailer dependency +74b71f5 Merge pull request #254 from W0rma/fix-badges +182d539 Fix CS +2ae3edf Upgrade PHP-CS-Fixer +d685b93 Fix badges in README +c7fb74e Merge pull request #233 from tbickley-mediabowl/issue/197_Monolog2 +043fd6c Allow Monolog version 2 +e23b888 Merge pull request #261 from acmephp/php81 +2cfc866 Allow Symfony 6 +2755d98 Add PHP 8.1 tests and fix deprecations +22df0d1 feature #250 Fix tests on PHP 8.0/8.1 (HansAdema) +7621356 Fix tests on PHP 8.0/8.1 +2299602 Update README.md +2e43e83 Merge pull request #231 from piotrantosik/feature/core-deps +192872c Allow lcobucci/jwt ^4.0 in core package 03/02/2021 22:29 2.0.1 Fix requirements 8efc61a Fix CI config 8b26916 Merge pull request #223 from piotrantosik/patch-1 From 112f660d762e9c5c8694e9f3293e6ab0c13afdbe Mon Sep 17 00:00:00 2001 From: duobradovic <153935011+duobradovic@users.noreply.github.com> Date: Fri, 15 Dec 2023 17:45:35 +0100 Subject: [PATCH 057/121] Accept Json when expected --- src/Core/Http/SecureHttpClient.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index efcd7cd0..6fac9553 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -197,7 +197,7 @@ public function createExternalAccountPayload(ExternalAccount $externalAccount, s */ public function request(string $method, string $endpoint, array $data = [], bool $returnJson = true) { - $response = $this->rawRequest($method, $endpoint, $data); + $response = $this->rawRequest($method, $endpoint, $data, $returnJson); $body = Utils::copyToString($response->getBody()); if (!$returnJson) { @@ -224,10 +224,10 @@ public function request(string $method, string $endpoint, array $data = [], bool * @throws ExpectedJsonException when $returnJson = true and the response is not valid JSON * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code */ - public function rawRequest(string $method, string $endpoint, array $data = []): ResponseInterface + public function rawRequest(string $method, string $endpoint, array $data = [], bool $acceptJson = false): ResponseInterface { - $call = function () use ($method, $endpoint, $data) { - $request = $this->createRequest($method, $endpoint, $data); + $call = function () use ($method, $endpoint, $data, $acceptJson) { + $request = $this->createRequest($method, $endpoint, $data, $acceptJson); try { $this->lastResponse = $this->httpClient->send($request); } catch (\Exception $exception) { @@ -326,10 +326,15 @@ private function signPayload(array $protected, array $payload = null): array ]; } - private function createRequest($method, $endpoint, $data) + private function createRequest($method, $endpoint, $data, $acceptJson) { $request = new Request($method, $endpoint); - $request = $request->withHeader('Accept', 'application/json,application/jose+json,'); + + if ($acceptJson) { + $request = $request->withHeader('Accept', 'application/json,application/jose+json,'); + } else { + $request = $request->withHeader('Accept', '*/*'); + } if ('POST' === $method && \is_array($data)) { $request = $request->withHeader('Content-Type', 'application/jose+json'); From cb1a237821a6fd49c6ae090cf954f420433141ac Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Fri, 15 Mar 2024 16:11:21 +0100 Subject: [PATCH 058/121] fix: allow psr/http-message v2 --- src/Core/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/composer.json b/src/Core/composer.json index 2e82021a..7cb1e11f 100644 --- a/src/Core/composer.json +++ b/src/Core/composer.json @@ -35,7 +35,7 @@ "guzzlehttp/guzzle": "^6.0|^7.0", "guzzlehttp/psr7": "^1.7|^2.1", "lcobucci/jwt": "^3.3|^4.0", - "psr/http-message": "^1.0", + "psr/http-message": "^1 || ^2", "psr/log": "^1.0|^2.0|^3.0", "webmozart/assert": "^1.0" }, From 16c2e03e1ea8ab192cb06d820dc5af3d150d2288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20St=C3=B6ckl?= Date: Fri, 15 Mar 2024 17:46:42 +0100 Subject: [PATCH 059/121] Revert "fix: allow psr/http-message v2" --- src/Core/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/composer.json b/src/Core/composer.json index 7cb1e11f..2e82021a 100644 --- a/src/Core/composer.json +++ b/src/Core/composer.json @@ -35,7 +35,7 @@ "guzzlehttp/guzzle": "^6.0|^7.0", "guzzlehttp/psr7": "^1.7|^2.1", "lcobucci/jwt": "^3.3|^4.0", - "psr/http-message": "^1 || ^2", + "psr/http-message": "^1.0", "psr/log": "^1.0|^2.0|^3.0", "webmozart/assert": "^1.0" }, From aa4e614ec3833884c61c95c81fd58d3d76e9e6ea Mon Sep 17 00:00:00 2001 From: Harry Lewis Date: Fri, 12 Apr 2024 18:57:06 +0100 Subject: [PATCH 060/121] Add Tests\AcmePhp\Core\AcmeClientTest::testRequestAuthorizationAllowsCapitalisation() --- tests/Core/AcmeClientTest.php | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/Core/AcmeClientTest.php b/tests/Core/AcmeClientTest.php index 1ba447c5..0838684c 100644 --- a/tests/Core/AcmeClientTest.php +++ b/tests/Core/AcmeClientTest.php @@ -131,4 +131,37 @@ public function testFullProcess(KeyOption $keyOption, bool $useAlternateCertific $this->assertStringContainsString('Unable to find specified certificate', $e->getPrevious()->getPrevious()->getMessage()); } } + + /** + * @dataProvider provideFullProcess + */ + public function testRequestAuthorizationAllowsCapitalisation(KeyOption $keyOption, bool $useAlternateCertificate) + { + $secureHttpClient = new SecureHttpClient( + (new KeyPairGenerator())->generateKeyPair($keyOption), + new Client(), + new Base64SafeEncoder(), + new KeyParser(), + new DataSigner(), + new ServerErrorHandler() + ); + + $client = new AcmeClient($secureHttpClient, 'https://localhost:14000/dir'); + + /* + * Register account + */ + if ('eab' === getenv('PEBBLE_MODE')) { + $client->registerAccount('titouan.galopin@acmephp.com', new ExternalAccount('kid1', 'dGVzdGluZw')); + } else { + $client->registerAccount('titouan.galopin@acmephp.com'); + } + + /* + * Request authorization challenges using a domain with capital letters + */ + $challenges = $client->requestAuthorization('ACMEPHP.com'); + + $this->assertNotEmpty($challenges); + } } From 9cd8f00d5ed6085b459c3bf1c74b5b49d88a9322 Mon Sep 17 00:00:00 2001 From: Harry Lewis Date: Fri, 12 Apr 2024 19:04:48 +0100 Subject: [PATCH 061/121] #280 Set domain name strings to lowercase in AcmeClient::requestOrder() and CertificateOrder::getAuthorizationChallenges() --- src/Core/AcmeClient.php | 2 +- src/Core/Protocol/CertificateOrder.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 24a9fb31..8f7531aa 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -116,7 +116,7 @@ public function requestOrder(array $domains): CertificateOrder static function ($domain) { return [ 'type' => 'dns', - 'value' => $domain, + 'value' => strtolower($domain), ]; }, array_values($domains) diff --git a/src/Core/Protocol/CertificateOrder.php b/src/Core/Protocol/CertificateOrder.php index a78bd830..2f7e11ec 100644 --- a/src/Core/Protocol/CertificateOrder.php +++ b/src/Core/Protocol/CertificateOrder.php @@ -83,6 +83,8 @@ public function getAuthorizationsChallenges() */ public function getAuthorizationChallenges(string $domain): array { + $domain = strtolower($domain); + if (!isset($this->authorizationsChallenges[$domain])) { throw new AcmeCoreClientException('The order does not contains any authorization challenge for the domain '.$domain); } From c47222ebcacc0586f363b171d61d6686df3ac439 Mon Sep 17 00:00:00 2001 From: Harry Lewis Date: Mon, 15 Apr 2024 12:06:00 +0100 Subject: [PATCH 062/121] Chain previous into re-thrown exceptions --- src/Core/AcmeClient.php | 2 +- src/Core/Challenge/Dns/LibDnsResolver.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 8f7531aa..cae90bc6 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -226,7 +226,7 @@ public function requestAuthorization(string $domain): array try { return $order->getAuthorizationChallenges($domain); } catch (AcmeCoreClientException $e) { - throw new ChallengeNotSupportedException(); + throw new ChallengeNotSupportedException($e); } } diff --git a/src/Core/Challenge/Dns/LibDnsResolver.php b/src/Core/Challenge/Dns/LibDnsResolver.php index 5cce509b..3030da2e 100644 --- a/src/Core/Challenge/Dns/LibDnsResolver.php +++ b/src/Core/Challenge/Dns/LibDnsResolver.php @@ -97,7 +97,7 @@ public function getTxtEntries($domain): array try { $response = $this->request($domain, ResourceTypes::TXT, $ipNameServer[0]); } catch (\Exception $e) { - throw new AcmeDnsResolutionException(sprintf('Unable to find domain %s on nameserver %s', $domain, $nameServer)); + throw new AcmeDnsResolutionException(sprintf('Unable to find domain %s on nameserver %s', $domain, $nameServer), $e); } $entries = []; foreach ($response->getAnswerRecords() as $record) { From 54df0f8ba0d30b66aa352a27a5a4520588a3231d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 14 Aug 2024 10:53:04 +0200 Subject: [PATCH 063/121] Fix CHANGELOG.md --- CHANGELOG.md | 537 +++++++++++++++++++++++++++------------------------ 1 file changed, 282 insertions(+), 255 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 213c3d73..3c875679 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,245 +1,269 @@ -07/06/2022 22:41 2.1.0 Add compatibility for PHP 8.0/8.1, Symfony 6 and other improvements -8a8a975 Merge pull request #263 from acmephp/core-get-order -95c17a4 Add Core\AcmeClient::reloadOrder method -7bac887 Merge pull request #257 from mgriego/core-deps-and-bug-fixes -371c905 Merge pull request #251 from W0rma/remove-swiftmailer -09e4bea Serialize embedded authorization challenges when serializing order objects. -d345ffe Allow for newer dependencies and fix a couple of issues in AcmePhp\Core. -8be2586 Merge pull request #262 from acmephp/upgrade-phpcs -03d14b3 Remove unused swiftmailer dependency -74b71f5 Merge pull request #254 from W0rma/fix-badges -182d539 Fix CS -2ae3edf Upgrade PHP-CS-Fixer -d685b93 Fix badges in README -c7fb74e Merge pull request #233 from tbickley-mediabowl/issue/197_Monolog2 -043fd6c Allow Monolog version 2 -e23b888 Merge pull request #261 from acmephp/php81 -2cfc866 Allow Symfony 6 -2755d98 Add PHP 8.1 tests and fix deprecations -22df0d1 feature #250 Fix tests on PHP 8.0/8.1 (HansAdema) -7621356 Fix tests on PHP 8.0/8.1 -2299602 Update README.md -2e43e83 Merge pull request #231 from piotrantosik/feature/core-deps -192872c Allow lcobucci/jwt ^4.0 in core package -03/02/2021 22:29 2.0.1 Fix requirements -8efc61a Fix CI config -8b26916 Merge pull request #223 from piotrantosik/patch-1 -f836e0c Merge pull request #224 from jackdpeterson/expand_composer_deps -b50c730 expand compatibility range for lcobucci/jwt to include both ^3.3 as well as ^4.0. -f1fbaf1 Require acmephp/ssl 2.0 -14/12/2020 20:10 2.0.0 v2.0 -2683496 Fix Box config and bump version -4f56592 Merge pull request #212 from acmephp/2.0 -65a0ac1 Fix tests and CS -8dc7923 Add alternate certificate tests -258ea5c Add option that allows a client to download the alternate certificate link instead of the default one -7e96853 Fix encoding issue -e3550c5 Allow to configure directly EAB credentials -c2bf09b Finalize EAB support -8f10906 Adapt tests -12533ba Add EAB structure -7fa7a2a Add EAB test -701b864 Bump minimum version of Symfony -5183019 Remove legacy tests -5ef6681 Migrate to Github Actions -3fc8c60 Remove deprecated features -ccbba76 Fix CS -2a473a8 Migrate acmephp/cli to use typehints -7d6b566 Fix tests -ab68cac Bump PHP-CS-Fixer version -c061e3a Migrate acmephp/core to use typehints -b342a86 Migrate acmephp/ssl to use typehints -be0a5b1 Fix subpackages composer.json -e51784a Remove deprecated commands, improve tests on Run command and merge v2 interfaces -e2be9c3 Upgrade dependencies and drop support for PHP <7.2 -2330ef6 Bump version -13/12/2020 23:03 1.3.0 v1.3.0 -5d37fb1 Merge pull request #218 from acmephp/do-not-verify-https-http-vaidator -fe50cf6 Merge pull request #217 from acmephp/openssl-php8 -e849f30 Do not check HTTPS certificate validity in HttpValidator -38a9a5f Fix openssl_free_key deprecation notice in PHP 8 -cb1eae4 Merge pull request #219 from acmephp/fix-tests -5514e91 Fix 1.0 CI -db4d497 Merge pull request #192 from acmephp/handle-processing -965c6b6 Fix Box config for latest Box version -6968927 Merge pull request #208 from InfinityFreeHosting/guzzle-7 -6ec9c47 Support both Guzzle 6.x and 7.x -9565469 Merge pull request #204 from p-seven-v/add-rejected-identifier-exception -a9ed8ac Fixed quotes -c0d3f0d Added more exceptions -3749f96 Reordered alphabetically -47c2139 Added class comment -765dd86 Added RejectedIdentifierServerException -852d90c Handle processing status case -5b07014 Merge pull request #193 from acmephp/update-ci -6133be4 Fix coding-style -252306a Update CI configuration -312ef14 Merge pull request #190 from philipsharp/response-body-summary -187ce72 Merge pull request #188 from miranovy/master -edcb011 Rewind response body before generating summary for server errors -a156f98 Distinguished name assert update -15/01/2020 22:42 1.2.0 Release version 1.2.0 -d031223 Merge pull request #185 from miranovy/master -24b8575 Fix getIssuerCertificate return type -df8f156 Merge pull request #183 from jderusse/split-request -b86d2d6 Merge pull request #184 from miranovy/master -ec1bbba fix the wrong return type -028470a fix the wrong return type -25a12e3 Split Sign and Request -937a2f7 Merge pull request #182 from pauladams8/master -c592b85 added orderNotReady error type -0d09084 Merge pull request #180 from jderusse/fix-test -f0e9422 Add php 7.3 and 7.4 -6b65f1a Merge pull request #164 from trustocean/feat-install-aliyun-cdn -d060f0c style ci, no spacing near dot -222f184 重名时阿里云会报错 -51da67e Merge branch 'master' of https://github.com/acmephp/acmephp into feat-install-aliyun-cdn -9203dec Merge pull request #163 from trustocean/feat-install-aliyun-waf -ce4bb92 style ci -45037e1 Merge branch 'master' of https://github.com/acmephp/acmephp into feat-install-aliyun-cdn -dcd1ad7 fixbug -bce7829 fixbug -3292b41 fix -2400302 remove wrong conf -0501e56 register in services -505d4c2 stash -0d522f7 Update InstallAliyunWafAction.php -91049e3 Remove try...catch wrapper -e9fefc3 stash -2d88030 Merge pull request #161 from jderusse/deprecate-commands -723de09 style ci -1a17f6e fix style ci -40558ea close #17 -05a2f39 fix -8541342 statsh -d865632 Merge pull request #162 from aik099/gandi-dns-solver-feat -83e47d3 Removed unused code -62d9b1b CS fixes -9eeb20a Adding Gandi.Net DNS solver class -8bae348 Deprecate commands in favor of run -9ef2916 Merge pull request #153 from elliotfehr/missed-memleak -c1271c0 free openssl resource after reading -b276743 Merge pull request #151 from elliotfehr/openssl-mem-leak -1effe3e Merge branch 'master' into openssl-mem-leak -58ee1e6 Merge pull request #152 from jderusse/fix-cs -2071f9d Fix CS -bb55db6 free the key resource after reading +# CHANGELOG + +## 3.0.0 (not released yet) + +> [!NOTE] +> From now on, a particular attention will be given to provide a nice changelog. + +## 07/06/2022 22:41 2.1.0 Add compatibility for PHP 8.0/8.1, Symfony 6 and other improvements + +* 8a8a975 Merge pull request #263 from acmephp/core-get-order +* 95c17a4 Add Core\AcmeClient::reloadOrder method +* 7bac887 Merge pull request #257 from mgriego/core-deps-and-bug-fixes +* 371c905 Merge pull request #251 from W0rma/remove-swiftmailer +* 09e4bea Serialize embedded authorization challenges when serializing order objects. +* d345ffe Allow for newer dependencies and fix a couple of issues in AcmePhp\Core. +* 8be2586 Merge pull request #262 from acmephp/upgrade-phpcs +* 03d14b3 Remove unused swiftmailer dependency +* 74b71f5 Merge pull request #254 from W0rma/fix-badges +* 182d539 Fix CS +* 2ae3edf Upgrade PHP-CS-Fixer +* d685b93 Fix badges in README +* c7fb74e Merge pull request #233 from tbickley-mediabowl/issue/197_Monolog2 +* 043fd6c Allow Monolog version 2 +* e23b888 Merge pull request #261 from acmephp/php81 +* 2cfc866 Allow Symfony 6 +* 2755d98 Add PHP 8.1 tests and fix deprecations +* 22df0d1 feature #250 Fix tests on PHP 8.0/8.1 (HansAdema) +* 7621356 Fix tests on PHP 8.0/8.1 +* 2299602 Update README.md +* 2e43e83 Merge pull request #231 from piotrantosik/feature/core-deps +* 192872c Allow lcobucci/jwt ^4.0 in core package + +## 03/02/2021 22:29 2.0.1 Fix requirements + +* 8efc61a Fix CI config +* 8b26916 Merge pull request #223 from piotrantosik/patch-1 +* f836e0c Merge pull request #224 from jackdpeterson/expand_composer_deps +* b50c730 expand compatibility range for lcobucci/jwt to include both ^3.3 as well as ^4.0. +* f1fbaf1 Require acmephp/ssl 2.0 + +## 14/12/2020 20:10 2.0.0 v2.0 + +* 2683496 Fix Box config and bump version +* 4f56592 Merge pull request #212 from acmephp/2.0 +* 65a0ac1 Fix tests and CS +* 8dc7923 Add alternate certificate tests +258ea5c Add option that allows a client to download the alternate certificate link instead * of the default one +* 7e96853 Fix encoding issue +* e3550c5 Allow to configure directly EAB credentials +* c2bf09b Finalize EAB support +* 8f10906 Adapt tests +* 12533ba Add EAB structure +* 7fa7a2a Add EAB test +* 701b864 Bump minimum version of Symfony +* 5183019 Remove legacy tests +* 5ef6681 Migrate to Github Actions +* 3fc8c60 Remove deprecated features +* ccbba76 Fix CS +* 2a473a8 Migrate acmephp/cli to use typehints +* 7d6b566 Fix tests +* ab68cac Bump PHP-CS-Fixer version +* c061e3a Migrate acmephp/core to use typehints +* b342a86 Migrate acmephp/ssl to use typehints +* be0a5b1 Fix subpackages composer.json +* e51784a Remove deprecated commands, improve tests on Run command and merge v2 interfaces +* e2be9c3 Upgrade dependencies and drop support for PHP <7.2 +* 2330ef6 Bump version + +## 13/12/2020 23:03 1.3.0 v1.3.0 + +* 5d37fb1 Merge pull request #218 from acmephp/do-not-verify-https-http-vaidator +* fe50cf6 Merge pull request #217 from acmephp/openssl-php8 +* e849f30 Do not check HTTPS certificate validity in HttpValidator +* 38a9a5f Fix openssl_free_key deprecation notice in PHP 8 +* cb1eae4 Merge pull request #219 from acmephp/fix-tests +* 5514e91 Fix 1.0 CI +* db4d497 Merge pull request #192 from acmephp/handle-processing +* 965c6b6 Fix Box config for latest Box version +* 6968927 Merge pull request #208 from InfinityFreeHosting/guzzle-7 +* 6ec9c47 Support both Guzzle 6.x and 7.x +* 9565469 Merge pull request #204 from p-seven-v/add-rejected-identifier-exception +* a9ed8ac Fixed quotes +* c0d3f0d Added more exceptions +* 3749f96 Reordered alphabetically +* 47c2139 Added class comment +* 765dd86 Added RejectedIdentifierServerException +* 852d90c Handle processing status case +* 5b07014 Merge pull request #193 from acmephp/update-ci +* 6133be4 Fix coding-style +* 252306a Update CI configuration +* 312ef14 Merge pull request #190 from philipsharp/response-body-summary +* 187ce72 Merge pull request #188 from miranovy/master +* edcb011 Rewind response body before generating summary for server errors +* a156f98 Distinguished name assert update + +## 15/01/2020 22:42 1.2.0 Release version 1.2.0 + +* d031223 Merge pull request #185 from miranovy/master +* 24b8575 Fix getIssuerCertificate return type +* df8f156 Merge pull request #183 from jderusse/split-request +* b86d2d6 Merge pull request #184 from miranovy/master +* ec1bbba fix the wrong return type +* 028470a fix the wrong return type +* 25a12e3 Split Sign and Request +* 937a2f7 Merge pull request #182 from pauladams8/master +* c592b85 added orderNotReady error type +* 0d09084 Merge pull request #180 from jderusse/fix-test +* f0e9422 Add php 7.3 and 7.4 +* 6b65f1a Merge pull request #164 from trustocean/feat-install-aliyun-cdn +* d060f0c style ci, no spacing near dot +* 222f184 重名时阿里云会报错 +51da67e Merge branch 'master' of https://github.com/acmephp/acmephp into * feat-install-aliyun-cdn +* 9203dec Merge pull request #163 from trustocean/feat-install-aliyun-waf +* ce4bb92 style ci +45037e1 Merge branch 'master' of https://github.com/acmephp/acmephp into * feat-install-aliyun-cdn +* dcd1ad7 fixbug +* bce7829 fixbug +* 3292b41 fix +* 2400302 remove wrong conf +* 0501e56 register in services +* 505d4c2 stash +* 0d522f7 Update InstallAliyunWafAction.php +* 91049e3 Remove try...catch wrapper +* e9fefc3 stash +* 2d88030 Merge pull request #161 from jderusse/deprecate-commands +* 723de09 style ci +* 1a17f6e fix style ci +* 40558ea close #17 +* 05a2f39 fix +* 8541342 statsh +* d865632 Merge pull request #162 from aik099/gandi-dns-solver-feat +* 83e47d3 Removed unused code +* 62d9b1b CS fixes +* 9eeb20a Adding Gandi.Net DNS solver class +* 8bae348 Deprecate commands in favor of run +* 9ef2916 Merge pull request #153 from elliotfehr/missed-memleak +* c1271c0 free openssl resource after reading +* b276743 Merge pull request #151 from elliotfehr/openssl-mem-leak +* 1effe3e Merge branch 'master' into openssl-mem-leak +* 58ee1e6 Merge pull request #152 from jderusse/fix-cs +* 2071f9d Fix CS +* bb55db6 free the key resource after reading 8c9d313 Build 1.1.1 PHAR -18/01/2019 15:17 1.1.1 Several bug fixes -952b1a6 Merge pull request #148 from rokclimb15/patch-1 -56df417 Correctly throw ChallengeTimedOutException -c7f523f Merge pull request #145 from jderusse/fix-exceptionx -034b6a2 Fix exception constructor -ff10617 Merge pull request #146 from jderusse/fix-deprec -9e754ad Fix 4.2 deprecations -10/11/2018 12:55 1.1.0 Add support for certificate revocation and ECDSA certificates -6933ffb Merge pull request #141 from jderusse/ecdsa -f7bac9e Fix undefined const OPENSSL_KEYTYPE_EC -5cf1a8d Add DH and DSA generators -c22cd9e Add support for ECDSA -3881f18 Merge pull request #143 from jderusse/update-phpunit -2cf3baf Use simple-phpunit to run tests -5194897 Merge pull request #142 from jderusse/fix-deps -4f13320 Add missing dependencies in composer.json files -da7c0e1 Merge pull request #139 from acmephp/revoke-certificate -39cf7fa Fix certificate revocation -27d4355 Update test to pass with Pebble implementation -94e2f20 Remove Certificate::__toString(), Command validation failure warning -> error -ef00e5f Add revocation reason and more helpful doc -c61019c Fix cs issues -7418bd9 Add api for certificate revocation -6d3888e Merge pull request #140 from acmephp/remove-scrutinizer -29258e6 Remove Scrutinizer -61df472 Merge pull request #138 from acmephp/remove-config-platform -af56ed7 Remove config.platform.php in Composer -e8029c2 Bump to dev -27/10/2018 12:07 1.0.1 Fix PHP version issue -6079833 Merge pull request #137 from acmephp/fix-php-version -6d6e2d2 Fix tests configuration -12ed5fc Fix PHP version in composer.json -958d497 Remove Gitter -a4effb8 Add link to core and ssl libraries in README -b17236e Bump to dev -14/10/2018 12:05 1.0.0 First stable release -1e4ba50 Merge pull request #135 from acmephp/prepare-release -13866eb Update README -9d773bb Remove beta messages -25e986e Prepare stable release, use only PrettyCI and fix CS -6267095 Merge pull request #134 from ScullWM/patch-1 -542ed28 Fix markdown error -c90e0a8 Merge pull request #132 from jderusse/optimize-route53 -c6b767a Optimize Route53 resolution -d4d2fa6 Merge pull request #131 from jderusse/catch-unresolvabled-nameserver -d57be04 Merge pull request #130 from alexmckinnon/csr-payload -1fd6427 Add common name to CSR payload -95f30c4 atch case where NameServer is not resolvable -421a4ab Merge pull request #128 from jderusse/update-dependencies -cdde24e Update dependencies -9b8ae4c Merge pull request #127 from jderusse/improve-libdns-fetching -d553270 Improve DNS checking -93bf725 Merge pull request #126 from jderusse/catch-libdns-exception-2 -c40f93b Catch exception on external calls -9bdd3ed Merge pull request #125 from jderusse/catch-libdns-exception -1930de5 Wrap external call in try/catch block -03b5532 Merge pull request #123 from jderusse/fix-status -c2b6b8d Allow "ready" status for orders with valid challenges -f039226 Merge pull request #119 from acmephp/auto-split -9ea70bc Automate split -32ccf3f Merge pull request #120 from jderusse/refactor-travis -2b5b296 Add comments -9b7ea2b Switch to travis pipeline -bbe4b9b Merge pull request #116 from jderusse/fix-sftp-config -db6c434 Merge pull request #115 from jderusse/fix-missing-lib -db391fc Add lib-xml used by SFTP adapter -922bd70 Fix typo in config -4a08ccc Merge pull request #113 from jderusse/fix-route53-domain-lookupx -bce7338 Fix Route53 zone search -c08891c Merge pull request #109 from jderusse/optimize-resolve -5dddf23 Optimize Route53 solvin -166dde3 Merge pull request #112 from kirtangajjar/fix-nginxproxy-wildcard -a848329 Merge pull request #111 from kirtangajjar/fix-nginxproxy-crt-generation -01f00c8 Merge pull request #107 from jderusse/feature-improve-error-messages -34bd808 Merge pull request #108 from jderusse/fix-payload -ca6531f Fix nginxproxy wildcard certificates -bac8140 Fixed in a bit better way -d081953 Fix nginxproxy crt generation -09ff221 Remove non linear index on array -26e672a Improve error messages -74dd66d Merge pull request #104 from benjilevens/laravel-5.5 -55d7b9b Merge pull request #106 from benjilevens/jose-json -2dd3303 Merge pull request #102 from jderusse/feature-skip-challenge -d4dcd9f Merge pull request #105 from benjilevens/request-certificate-calling-finalize-order -503c4df Use more appropriate Accept and Content-Type headers -475f359 Support multiple versions of swift mailer -4b9dac7 Make requestCertificate call finalizeOrder with required parameters -005081d Bump Swiftmailer version to prevent composer conflicts when installing into a Laravel 5.5 project -82e5e5b Skip challenge when no renewal -fd54a08 Merge pull request #101 from jderusse/feature-multiple-challenges -dd88adc Optimize challenge solving by solving several challenges at once -0e618e3 Merge pull request #100 from jderusse/feature-file-solver -898c939 Use service locator -04c9814 Add a filesystem solver (to upload http challenge) -9818d39 Merge pull request #98 from jderusse/fix-tree -208913f Refactor file tree -4966d60 Merge pull request #96 from jderusse/fix-combined-public -1671a5b Merge pull request #97 from jderusse/fix-status-expired -e327321 Add an option to hide/show expired certificates -51ebab0 Move combined certificate in private folder -bd1478e Merge pull request #95 from jderusse/fix-ci -a08cb2e Fix CS -1d590bb Switch from styleCi to travis -477929d Merge pull request #94 from jderusse/fix-debug -75552fd Remove debug -c4b8c66 Merge pull request #87 from jderusse/feature/run -38e49cb Add a run command -15c1809 Merge pull request #93 from jderusse/feature/docker -9907669 Add a dockerfile -a7b8581 Merge pull request #92 from jderusse/v2 -1550972 Implement v2 protocol -6d15380 Implement ELB installation -3c8b06a Add Route53 solver -f306733 Bump to dev -21/01/2018 18:31 1.0.0-beta5 Fix deprecations and allow setting KeyPair from Client object + +## 18/01/2019 15:17 1.1.1 Several bug fixes + +* 952b1a6 Merge pull request #148 from rokclimb15/patch-1 +* 56df417 Correctly throw ChallengeTimedOutException +* c7f523f Merge pull request #145 from jderusse/fix-exceptionx +* 034b6a2 Fix exception constructor +* ff10617 Merge pull request #146 from jderusse/fix-deprec +* 9e754ad Fix 4.2 deprecations +* 10/11/2018 12:55 1.1.0 Add support for certificate revocation and ECDSA certificates +* 6933ffb Merge pull request #141 from jderusse/ecdsa +* f7bac9e Fix undefined const OPENSSL_KEYTYPE_EC +* 5cf1a8d Add DH and DSA generators +* c22cd9e Add support for ECDSA +* 3881f18 Merge pull request #143 from jderusse/update-phpunit +* 2cf3baf Use simple-phpunit to run tests +* 5194897 Merge pull request #142 from jderusse/fix-deps +* 4f13320 Add missing dependencies in composer.json files +* da7c0e1 Merge pull request #139 from acmephp/revoke-certificate +* 39cf7fa Fix certificate revocation +* 27d4355 Update test to pass with Pebble implementation +* 94e2f20 Remove Certificate::__toString(), Command validation failure warning -> error +* ef00e5f Add revocation reason and more helpful doc +* c61019c Fix cs issues +* 7418bd9 Add api for certificate revocation +* 6d3888e Merge pull request #140 from acmephp/remove-scrutinizer +* 29258e6 Remove Scrutinizer +* 61df472 Merge pull request #138 from acmephp/remove-config-platform +* af56ed7 Remove config.platform.php in Composer +* e8029c2 Bump to dev + +## 27/10/2018 12:07 1.0.1 Fix PHP version issue + +* 6079833 Merge pull request #137 from acmephp/fix-php-version +* 6d6e2d2 Fix tests configuration +* 12ed5fc Fix PHP version in composer.json +* 958d497 Remove Gitter +* a4effb8 Add link to core and ssl libraries in README +* b17236e Bump to dev + +## 14/10/2018 12:05 1.0.0 First stable release + +* 1e4ba50 Merge pull request #135 from acmephp/prepare-release +* 13866eb Update README +* 9d773bb Remove beta messages +* 25e986e Prepare stable release, use only PrettyCI and fix CS +* 6267095 Merge pull request #134 from ScullWM/patch-1 +* 542ed28 Fix markdown error +* c90e0a8 Merge pull request #132 from jderusse/optimize-route53 +* c6b767a Optimize Route53 resolution +* d4d2fa6 Merge pull request #131 from jderusse/catch-unresolvabled-nameserver +* d57be04 Merge pull request #130 from alexmckinnon/csr-payload +* 1fd6427 Add common name to CSR payload +* 95f30c4 atch case where NameServer is not resolvable +* 421a4ab Merge pull request #128 from jderusse/update-dependencies +* cdde24e Update dependencies +* 9b8ae4c Merge pull request #127 from jderusse/improve-libdns-fetching +* d553270 Improve DNS checking +* 93bf725 Merge pull request #126 from jderusse/catch-libdns-exception-2 +* c40f93b Catch exception on external calls +* 9bdd3ed Merge pull request #125 from jderusse/catch-libdns-exception +* 1930de5 Wrap external call in try/catch block +* 03b5532 Merge pull request #123 from jderusse/fix-status +* c2b6b8d Allow "ready" status for orders with valid challenges +* f039226 Merge pull request #119 from acmephp/auto-split +* 9ea70bc Automate split +* 32ccf3f Merge pull request #120 from jderusse/refactor-travis +* 2b5b296 Add comments +* 9b7ea2b Switch to travis pipeline +* bbe4b9b Merge pull request #116 from jderusse/fix-sftp-config +* db6c434 Merge pull request #115 from jderusse/fix-missing-lib +* db391fc Add lib-xml used by SFTP adapter +* 922bd70 Fix typo in config +* 4a08ccc Merge pull request #113 from jderusse/fix-route53-domain-lookupx +* bce7338 Fix Route53 zone search +* c08891c Merge pull request #109 from jderusse/optimize-resolve +* 5dddf23 Optimize Route53 solvin +* 166dde3 Merge pull request #112 from kirtangajjar/fix-nginxproxy-wildcard +* a848329 Merge pull request #111 from kirtangajjar/fix-nginxproxy-crt-generation +* 01f00c8 Merge pull request #107 from jderusse/feature-improve-error-messages +* 34bd808 Merge pull request #108 from jderusse/fix-payload +* ca6531f Fix nginxproxy wildcard certificates +* bac8140 Fixed in a bit better way +* d081953 Fix nginxproxy crt generation +* 09ff221 Remove non linear index on array +* 26e672a Improve error messages +* 74dd66d Merge pull request #104 from benjilevens/laravel-5.5 +* 55d7b9b Merge pull request #106 from benjilevens/jose-json +* 2dd3303 Merge pull request #102 from jderusse/feature-skip-challenge +* d4dcd9f Merge pull request #105 from benjilevens/request-certificate-calling-finalize-order +* 503c4df Use more appropriate Accept and Content-Type headers +* 475f359 Support multiple versions of swift mailer +* 4b9dac7 Make requestCertificate call finalizeOrder with required parameters +005081d Bump Swiftmailer version to prevent composer conflicts when installing into a * Laravel 5.5 project +* 82e5e5b Skip challenge when no renewal +* fd54a08 Merge pull request #101 from jderusse/feature-multiple-challenges +* dd88adc Optimize challenge solving by solving several challenges at once +* 0e618e3 Merge pull request #100 from jderusse/feature-file-solver +* 898c939 Use service locator +* 04c9814 Add a filesystem solver (to upload http challenge) +* 9818d39 Merge pull request #98 from jderusse/fix-tree +* 208913f Refactor file tree +* 4966d60 Merge pull request #96 from jderusse/fix-combined-public +* 1671a5b Merge pull request #97 from jderusse/fix-status-expired +* e327321 Add an option to hide/show expired certificates +* 51ebab0 Move combined certificate in private folder +* bd1478e Merge pull request #95 from jderusse/fix-ci +* a08cb2e Fix CS +* 1d590bb Switch from styleCi to travis +* 477929d Merge pull request #94 from jderusse/fix-debug +* 75552fd Remove debug +* c4b8c66 Merge pull request #87 from jderusse/feature/run +* 38e49cb Add a run command +* 15c1809 Merge pull request #93 from jderusse/feature/docker +* 9907669 Add a dockerfile +* a7b8581 Merge pull request #92 from jderusse/v2 +* 1550972 Implement v2 protocol +* 6d15380 Implement ELB installation +* 3c8b06a Add Route53 solver +* f306733 Bump to dev + +## 21/01/2018 18:31 1.0.0-beta5 Fix deprecations and allow setting KeyPair from Client object + 466e009 Release of new version 1.0.0-beta5 0a94b15 Fix mailer handler 8fde026 Allow setting KeyPair from Client object (#72) @@ -247,14 +271,17 @@ e2ac2bf Fix json_decode error handling in SecureHttpClient and ServerErrorHandle 4c7d10d Fix deprecations and improve tests f559a93 [doc] Fix typo on README 0b4ea8b Bump to dev -21/01/2018 18:23 1.0.0-beta5 Fix deprecations and allow setting KeyPair from Client object -0a94b15 Fix mailer handler -8fde026 Allow setting KeyPair from Client object (#72) -e2ac2bf Fix json_decode error handling in SecureHttpClient and ServerErrorHandler -4c7d10d Fix deprecations and improve tests -f559a93 [doc] Fix typo on README -0b4ea8b Bump to dev -* 1.0.0-beta4 (2017-01-29) + +## 21/01/2018 18:23 1.0.0-beta5 Fix deprecations and allow setting KeyPair from Client object + +* 0a94b15 Fix mailer handler +* 8fde026 Allow setting KeyPair from Client object (#72) +* e2ac2bf Fix json_decode error handling in SecureHttpClient and ServerErrorHandler +* 4c7d10d Fix deprecations and improve tests +* f559a93 [doc] Fix typo on README +* 0b4ea8b Bump to dev + +## 1.0.0-beta4 (2017-01-29) * 0b18a86 Redone nameing of ::getResource with new tests (#61) * 78d0e2d Accept empty issuer CN field (#59) @@ -272,7 +299,7 @@ f559a93 [doc] Fix typo on README * a7f194a Fix vendor/bin/acme autoload path * 5e4a887 Bump to dev -* 1.0.0-beta3 (2016-12-09) +## 1.0.0-beta3 (2016-12-09) * daa6d1c Merge pull request #40 from acmephp/fix-39 * 0b8629e Remove RECOVER_REGISTRATION obsolete unused resource @@ -284,7 +311,7 @@ f559a93 [doc] Fix typo on README * 794f8d5 Introduce CLI logger to handle different verbosities properly * e055695 Bump to dev -* 1.0.0-beta2 (2016-10-19) +## 1.0.0-beta2 (2016-10-19) * ed2cc9e Update main and components README files * 1adff0d Improve some commands descriptions @@ -306,7 +333,7 @@ f559a93 [doc] Fix typo on README * 1d1bf00 Rename challenger into SOlver * e1289df Allow custom challenger extension -* 1.0.0-beta1 (2016-09-24) +## 1.0.0-beta1 (2016-09-24) * f1585a4 Fix type in README * 54d68c3 Merge pull request #24 from jderusse/multi-domains @@ -317,12 +344,12 @@ f559a93 [doc] Fix typo on README * f7de82d Automatically agreed with agrement (#26) * 7accf30 Fix tests (#27) -* 1.0.0-alpha10 (2016-08-16) +## 1.0.0-alpha10 (2016-08-16) * 3bfa96c Update RegisterCommand.php (#21) * d3d779f Bump version -* 1.0.0-alpha9 (2016-07-27) +## 1.0.0-alpha9 (2016-07-27) * 3e89b38 Remove unsupported actions from the dist file for the moment * 07857e6 Add PHP 7.1 in Travis and improve CI configuration (#19) @@ -330,4 +357,4 @@ f559a93 [doc] Fix typo on README * adf4dc8 Update version as DEV * f61df6f Fix Guzzle URI test (#17) * 846bbce Fix assertions messages - * 113b2d8 Fix 404 on documentation link (#15) \ No newline at end of file + * 113b2d8 Fix 404 on documentation link (#15) From 3281eb12b84a1837ce45a4ddf628c94b79720d8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 14 Aug 2024 10:55:31 +0200 Subject: [PATCH 064/121] Remove '.rmt.yml' This tools generates totally useless changelog, and urgly! --- .rmt.yml | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 .rmt.yml diff --git a/.rmt.yml b/.rmt.yml deleted file mode 100644 index 6a18a5f0..00000000 --- a/.rmt.yml +++ /dev/null @@ -1,23 +0,0 @@ -vcs: - name: git - sign-tag: true - -version-generator: - name: semantic - allow-label: true - -version-persister: vcs-tag - -prerequisites: - display-last-changes: ~ - -pre-release-actions: - changelog-update: - format: simple - file: CHANGELOG.md - dump-commits: true - vcs-commit: ~ - -post-release-actions: - vcs-publish: - ask-confirmation: true From 9ec2ae2a954397ba9ceb0adeab8da5f0ea2bac1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 14 Aug 2024 11:00:07 +0200 Subject: [PATCH 065/121] Make more robust setup.sh --- tests/setup.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/setup.sh b/tests/setup.sh index 18b3cb56..7165af64 100755 --- a/tests/setup.sh +++ b/tests/setup.sh @@ -1,17 +1,19 @@ #!/usr/bin/env bash +set -e +set -o pipefail + # Root directory -cd $( dirname "${BASH_SOURCE[0]}" ) -cd .. +cd $( dirname "${BASH_SOURCE[0]}" )/.. # SFTP -docker run -d --name acme_sftp -p 8022:22 atmoz/sftp acmephp:acmephp:::share +docker run -d --rm --name acme_sftp -p 8022:22 atmoz/sftp acmephp:acmephp:::share # pebble MODE=${PEBBLE_MODE:-default} -docker run -d --name acme_server --net host letsencrypt/pebble-challtestsrv pebble-challtestsrv -defaultIPv6 "" -defaultIPv4 127.0.0.1 -docker run -d --name acme_pebble --net host -e PEBBLE_VA_NOSLEEP=1 -e PEBBLE_WFE_NONCEREJECT=0 -e PEBBLE_ALTERNATE_ROOTS=1 -v $(pwd)/tests/Fixtures/pebble-config-$MODE.json:/test/config/pebble-config.json letsencrypt/pebble pebble -dnsserver 127.0.0.1:8053 +docker run -d --rm --name acme_server --net host letsencrypt/pebble-challtestsrv pebble-challtestsrv -defaultIPv6 "" -defaultIPv4 127.0.0.1 +docker run -d --rm --name acme_pebble --net host -e PEBBLE_VA_NOSLEEP=1 -e PEBBLE_WFE_NONCEREJECT=0 -e PEBBLE_ALTERNATE_ROOTS=1 -v $(pwd)/tests/Fixtures/pebble-config-$MODE.json:/test/config/pebble-config.json letsencrypt/pebble pebble -dnsserver 127.0.0.1:8053 # Wait for boot to be completed docker run --rm --net host martin/wait -c localhost:14000,localhost:8022,localhost:8053,localhost:5002 -t 120 From c1bc9e492df2c8998608fe6405863a6c4ffe92d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 14 Aug 2024 11:02:58 +0200 Subject: [PATCH 066/121] Make more robust test.sh --- tests/run.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/run.sh b/tests/run.sh index d2c3ed8a..a5f3a4b5 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -1,8 +1,10 @@ #!/usr/bin/env bash +set -e +set -o pipefail + # Root directory -cd $( dirname "${BASH_SOURCE[0]}" ) -cd .. +cd $( dirname "${BASH_SOURCE[0]}" )/.. tempfile=$(mktemp) cert='-----BEGIN CERTIFICATE----- From f8b329365e804ec9b578f9f54fd6742947574f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 14 Aug 2024 11:28:33 +0200 Subject: [PATCH 067/121] Drop support for outdated version (SF, PHP) and fix tests --- .github/workflows/test-build.yaml | 125 +++--------------- CHANGELOG.md | 3 + composer.json | 25 ++-- phpunit.xml.dist | 1 + src/Cli/Serializer/PemNormalizer.php | 8 ++ tests/Cli/Action/InstallAwsElbActionTest.php | 3 + .../Cli/Action/InstallAwsElbv2ActionTest.php | 3 + tests/Cli/Repository/RepositoryTest.php | 3 +- tests/Core/Challenge/ChainValidatorTest.php | 3 + .../Challenge/Dns/DnsDataExtractorTest.php | 3 + tests/Core/Challenge/Dns/DnsValidatorTest.php | 3 + tests/Core/Challenge/Dns/GandiSolverTest.php | 3 + .../Core/Challenge/Dns/Route53SolverTest.php | 3 + .../Challenge/Dns/SimpleDnsSolverTest.php | 3 + .../Challenge/Http/FilesystemSolverTest.php | 3 + .../Challenge/Http/HttpDataExtractorTest.php | 3 + .../Core/Challenge/Http/HttpValidatorTest.php | 3 + .../Challenge/Http/SimpleHttpSolverTest.php | 3 + tests/Core/Challenge/WaitingValidatorTest.php | 3 + 19 files changed, 80 insertions(+), 124 deletions(-) diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index a3af626f..9adfb417 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -23,123 +23,30 @@ jobs: - name: Check coding style run: php php-cs-fixer.phar fix --dry-run --diff - tests-php72-sf50-low: + ci: + name: Test PHP ${{ matrix.php-version }} ${{ matrix.name }} runs-on: ubuntu-latest - env: - SYMFONY_VERSION: 5.0.* - steps: - - uses: shivammathur/setup-php@v2 - with: - php-version: '7.2' - coverage: none - - - uses: actions/checkout@master - - - name: Install dependencies - run: | - composer require --dev "sebastian/comparator:^2.0" - composer require --no-update symfony/config=$SYMFONY_VERSION symfony/console=$SYMFONY_VERSION symfony/dependency-injection=$SYMFONY_VERSION symfony/filesystem=$SYMFONY_VERSION symfony/serializer=$SYMFONY_VERSION symfony/yaml=$SYMFONY_VERSION - composer require --no-update --dev symfony/finder=$SYMFONY_VERSION - composer update --no-interaction --no-progress --ansi --prefer-lowest --prefer-stable - - - name: Preparing tests - run: ./tests/setup.sh - - - name: Running tests - run: ./tests/run.sh - - tests-php73-sf54-low: - runs-on: ubuntu-latest - env: - SYMFONY_VERSION: 5.4.* - steps: - - uses: shivammathur/setup-php@v2 - with: - php-version: '7.3' - coverage: none - - - uses: actions/checkout@master - - - name: Install dependencies - run: | - composer require --dev "sebastian/comparator:^2.0" - composer require --no-update symfony/config=$SYMFONY_VERSION symfony/console=$SYMFONY_VERSION symfony/dependency-injection=$SYMFONY_VERSION symfony/filesystem=$SYMFONY_VERSION symfony/serializer=$SYMFONY_VERSION symfony/yaml=$SYMFONY_VERSION - composer require --no-update --dev symfony/finder=$SYMFONY_VERSION - composer update --no-interaction --no-progress --ansi --prefer-lowest --prefer-stable - - - name: Preparing tests - run: ./tests/setup.sh - - - name: Running tests - run: ./tests/run.sh - - tests-php74-sf54-high-eab: - runs-on: ubuntu-latest - env: - SYMFONY_VERSION: 5.4.* - PEBBLE_MODE: eab - steps: - - uses: shivammathur/setup-php@v2 - with: - php-version: '7.4' - coverage: none - - - uses: actions/checkout@master + strategy: + fail-fast: false + matrix: + php-version: ["8.2", "8.3"] + composer-flags: [""] + name: [""] + include: + - php-version: 8.1 + composer-flags: "--prefer-lowest" + name: "(prefer lowest dependencies)" - - name: Install dependencies - run: | - composer require --no-update symfony/config=$SYMFONY_VERSION symfony/console=$SYMFONY_VERSION symfony/dependency-injection=$SYMFONY_VERSION symfony/filesystem=$SYMFONY_VERSION symfony/serializer=$SYMFONY_VERSION symfony/yaml=$SYMFONY_VERSION - composer require --no-update --dev symfony/finder=$SYMFONY_VERSION - composer update --no-interaction --no-progress --ansi --prefer-stable - - - name: Preparing tests - run: ./tests/setup.sh - - - name: Running tests - run: ./tests/run.sh - - tests-php80-sf60-high: - runs-on: ubuntu-latest - env: - SYMFONY_VERSION: 6.0.* steps: - uses: shivammathur/setup-php@v2 with: - php-version: '8.0' - coverage: none - - - uses: actions/checkout@master + php-version: ${{ matrix.php-version }} - - name: Install dependencies - run: | - composer require --no-update symfony/config=$SYMFONY_VERSION symfony/console=$SYMFONY_VERSION symfony/dependency-injection=$SYMFONY_VERSION symfony/filesystem=$SYMFONY_VERSION symfony/serializer=$SYMFONY_VERSION symfony/yaml=$SYMFONY_VERSION - composer require --no-update --dev symfony/finder=$SYMFONY_VERSION - composer update --no-interaction --no-progress --ansi --prefer-stable - - - name: Preparing tests - run: ./tests/setup.sh - - - name: Running tests - run: ./tests/run.sh - - tests-php81-sf61-high-eab: - runs-on: ubuntu-latest - env: - SYMFONY_VERSION: 6.1.* - PEBBLE_MODE: eab - steps: - - uses: shivammathur/setup-php@v2 - with: - php-version: '8.1' - coverage: none - - - uses: actions/checkout@master + - uses: actions/checkout@v4 - - name: Install dependencies + - name: Install Composer dependencies run: | - composer require --no-update symfony/config=$SYMFONY_VERSION symfony/console=$SYMFONY_VERSION symfony/dependency-injection=$SYMFONY_VERSION symfony/filesystem=$SYMFONY_VERSION symfony/serializer=$SYMFONY_VERSION symfony/yaml=$SYMFONY_VERSION - composer require --no-update --dev symfony/finder=$SYMFONY_VERSION - composer update --no-interaction --no-progress --ansi --prefer-stable + composer update --prefer-dist --no-interaction ${{ matrix.composer-flags }} - name: Preparing tests run: ./tests/setup.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c875679..ed010da4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ > [!NOTE] > From now on, a particular attention will be given to provide a nice changelog. +* Drop support for PHP <8.1 +* Drop support for Symfony <5.4, and 6.0, 6.1, 6.2, 6.3 + ## 07/06/2022 22:41 2.1.0 Add compatibility for PHP 8.0/8.1, Symfony 6 and other improvements * 8a8a975 Merge pull request #263 from acmephp/core-get-order diff --git a/composer.json b/composer.json index a358b3c8..6bb0b415 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ } ], "require": { - "php": ">=7.2.5", + "php": ">=8.1", "ext-filter": "*", "ext-hash": "*", "ext-json": "*", @@ -55,23 +55,20 @@ "psr/container": "^1.0", "psr/http-message": "^1.0", "psr/log": "^1.0", - "symfony/config": "^5.0|^6.0", - "symfony/console": "^5.0|^6.0", - "symfony/dependency-injection": "^5.0|^6.0", - "symfony/filesystem": "^5.0|^6.0", - "symfony/serializer": "^5.0|^6.0", - "symfony/yaml": "^5.0|^6.0", + "symfony/config": "^5.4 || ^6.4", + "symfony/console": "^5.4 || ^6.4", + "symfony/dependency-injection": "^5.4 || ^6.4", + "symfony/filesystem": "^5.4 || ^6.4", + "symfony/serializer": "^5.4 || ^6.4", + "symfony/yaml": "^5.4 || ^6.4", "webmozart/assert": "^1.0", "webmozart/path-util": "^2.3" }, - "suggest": { - "daverandom/libdns": "^2.0" - }, "require-dev": { - "phpspec/prophecy": "^1.9", - "symfony/finder": "^5.0|^6.0", - "symfony/phpunit-bridge": "^5.0|^6.0", - "symfony/var-dumper": "^5.0|^6.0" + "symfony/finder": "^5.4 || ^6.4", + "symfony/phpunit-bridge": "^5.4 || ^6.4", + "symfony/property-access": "^5.4 || ^6.4", + "symfony/var-dumper": "^5.4 || ^6.4" }, "autoload": { "psr-4": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index e94cde70..46e21548 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -14,6 +14,7 @@ + diff --git a/src/Cli/Serializer/PemNormalizer.php b/src/Cli/Serializer/PemNormalizer.php index 8053fbb1..2a59ac7e 100644 --- a/src/Cli/Serializer/PemNormalizer.php +++ b/src/Cli/Serializer/PemNormalizer.php @@ -52,4 +52,12 @@ public function supportsDenormalization($data, $type, string $format = null) { return \is_string($data); } + + public function getSupportedTypes(?string $format): array + { + return [ + Certificate::class => true, + Key::class => true, + ]; + } } diff --git a/tests/Cli/Action/InstallAwsElbActionTest.php b/tests/Cli/Action/InstallAwsElbActionTest.php index c507d9b1..2e551f9d 100644 --- a/tests/Cli/Action/InstallAwsElbActionTest.php +++ b/tests/Cli/Action/InstallAwsElbActionTest.php @@ -24,9 +24,12 @@ use Aws\Iam\IamClient; use PHPUnit\Framework\TestCase; use Prophecy\Argument; +use Prophecy\PhpUnit\ProphecyTrait; class InstallAwsElbActionTest extends TestCase { + use ProphecyTrait; + public function testHandle() { $domain = 'foo.bar'; diff --git a/tests/Cli/Action/InstallAwsElbv2ActionTest.php b/tests/Cli/Action/InstallAwsElbv2ActionTest.php index 1a4d6928..38fa7eb9 100644 --- a/tests/Cli/Action/InstallAwsElbv2ActionTest.php +++ b/tests/Cli/Action/InstallAwsElbv2ActionTest.php @@ -24,9 +24,12 @@ use Aws\Iam\IamClient; use PHPUnit\Framework\TestCase; use Prophecy\Argument; +use Prophecy\PhpUnit\ProphecyTrait; class InstallAwsElbv2ActionTest extends TestCase { + use ProphecyTrait; + public function testHandle() { $domain = 'foo.bar'; diff --git a/tests/Cli/Repository/RepositoryTest.php b/tests/Cli/Repository/RepositoryTest.php index 4a1d87a6..8fc69de8 100644 --- a/tests/Cli/Repository/RepositoryTest.php +++ b/tests/Cli/Repository/RepositoryTest.php @@ -25,6 +25,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; +use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Serializer; class RepositoryTest extends TestCase @@ -47,7 +48,7 @@ class RepositoryTest extends TestCase public function setUp(): void { $this->serializer = new Serializer( - [new PemNormalizer(), new GetSetMethodNormalizer()], + [new PemNormalizer(), new ObjectNormalizer()], [new PemEncoder(), new JsonEncoder()] ); diff --git a/tests/Core/Challenge/ChainValidatorTest.php b/tests/Core/Challenge/ChainValidatorTest.php index 900aaaf6..f78e6650 100644 --- a/tests/Core/Challenge/ChainValidatorTest.php +++ b/tests/Core/Challenge/ChainValidatorTest.php @@ -16,9 +16,12 @@ use AcmePhp\Core\Challenge\ValidatorInterface; use AcmePhp\Core\Protocol\AuthorizationChallenge; use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; class ChainValidatorTest extends TestCase { + use ProphecyTrait; + public function testSupports() { $mockValidator1 = $this->prophesize(ValidatorInterface::class); diff --git a/tests/Core/Challenge/Dns/DnsDataExtractorTest.php b/tests/Core/Challenge/Dns/DnsDataExtractorTest.php index 9d232fe5..2ed8ab13 100644 --- a/tests/Core/Challenge/Dns/DnsDataExtractorTest.php +++ b/tests/Core/Challenge/Dns/DnsDataExtractorTest.php @@ -15,9 +15,12 @@ use AcmePhp\Core\Http\Base64SafeEncoder; use AcmePhp\Core\Protocol\AuthorizationChallenge; use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; class DnsDataExtractorTest extends TestCase { + use ProphecyTrait; + public function testGetRecordName() { $domain = 'foo.com'; diff --git a/tests/Core/Challenge/Dns/DnsValidatorTest.php b/tests/Core/Challenge/Dns/DnsValidatorTest.php index 9b964111..5102380b 100644 --- a/tests/Core/Challenge/Dns/DnsValidatorTest.php +++ b/tests/Core/Challenge/Dns/DnsValidatorTest.php @@ -17,9 +17,12 @@ use AcmePhp\Core\Challenge\SolverInterface; use AcmePhp\Core\Protocol\AuthorizationChallenge; use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; class DnsValidatorTest extends TestCase { + use ProphecyTrait; + public function testSupports() { $typeDns = 'dns-01'; diff --git a/tests/Core/Challenge/Dns/GandiSolverTest.php b/tests/Core/Challenge/Dns/GandiSolverTest.php index 19915113..0ed9adc6 100644 --- a/tests/Core/Challenge/Dns/GandiSolverTest.php +++ b/tests/Core/Challenge/Dns/GandiSolverTest.php @@ -16,9 +16,12 @@ use AcmePhp\Core\Protocol\AuthorizationChallenge; use GuzzleHttp\ClientInterface; use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; class GandiSolverTest extends TestCase { + use ProphecyTrait; + public function testSupports() { $typeDns = 'dns-01'; diff --git a/tests/Core/Challenge/Dns/Route53SolverTest.php b/tests/Core/Challenge/Dns/Route53SolverTest.php index bd9581af..cdbaca5c 100644 --- a/tests/Core/Challenge/Dns/Route53SolverTest.php +++ b/tests/Core/Challenge/Dns/Route53SolverTest.php @@ -17,9 +17,12 @@ use Aws\Route53\Route53Client; use PHPUnit\Framework\TestCase; use Prophecy\Argument; +use Prophecy\PhpUnit\ProphecyTrait; class Route53SolverTest extends TestCase { + use ProphecyTrait; + public function testSupports() { $typeDns = 'dns-01'; diff --git a/tests/Core/Challenge/Dns/SimpleDnsSolverTest.php b/tests/Core/Challenge/Dns/SimpleDnsSolverTest.php index 7ef38e5d..09be9d71 100644 --- a/tests/Core/Challenge/Dns/SimpleDnsSolverTest.php +++ b/tests/Core/Challenge/Dns/SimpleDnsSolverTest.php @@ -16,10 +16,13 @@ use AcmePhp\Core\Protocol\AuthorizationChallenge; use PHPUnit\Framework\TestCase; use Prophecy\Argument; +use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Component\Console\Output\OutputInterface; class SimpleDnsSolverTest extends TestCase { + use ProphecyTrait; + public function testSupports() { $typeDns = 'dns-01'; diff --git a/tests/Core/Challenge/Http/FilesystemSolverTest.php b/tests/Core/Challenge/Http/FilesystemSolverTest.php index 3a66986a..75e8e4d1 100644 --- a/tests/Core/Challenge/Http/FilesystemSolverTest.php +++ b/tests/Core/Challenge/Http/FilesystemSolverTest.php @@ -18,10 +18,13 @@ use AcmePhp\Core\Protocol\AuthorizationChallenge; use PHPUnit\Framework\TestCase; use Prophecy\Argument; +use Prophecy\PhpUnit\ProphecyTrait; use Psr\Container\ContainerInterface; class FilesystemSolverTest extends TestCase { + use ProphecyTrait; + public function testSupports() { $typeDns = 'dns-01'; diff --git a/tests/Core/Challenge/Http/HttpDataExtractorTest.php b/tests/Core/Challenge/Http/HttpDataExtractorTest.php index f8a4b75e..5b5a8a3c 100644 --- a/tests/Core/Challenge/Http/HttpDataExtractorTest.php +++ b/tests/Core/Challenge/Http/HttpDataExtractorTest.php @@ -14,9 +14,12 @@ use AcmePhp\Core\Challenge\Http\HttpDataExtractor; use AcmePhp\Core\Protocol\AuthorizationChallenge; use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; class HttpDataExtractorTest extends TestCase { + use ProphecyTrait; + public function testGetCheckUrl() { $domain = 'foo.com'; diff --git a/tests/Core/Challenge/Http/HttpValidatorTest.php b/tests/Core/Challenge/Http/HttpValidatorTest.php index 116fbe45..1dd1c7e0 100644 --- a/tests/Core/Challenge/Http/HttpValidatorTest.php +++ b/tests/Core/Challenge/Http/HttpValidatorTest.php @@ -18,12 +18,15 @@ use GuzzleHttp\Client; use GuzzleHttp\Exception\ClientException; use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; class HttpValidatorTest extends TestCase { + use ProphecyTrait; + public function testSupports() { $typeDns = 'dns-01'; diff --git a/tests/Core/Challenge/Http/SimpleHttpSolverTest.php b/tests/Core/Challenge/Http/SimpleHttpSolverTest.php index 3c6df104..7b4feb6f 100644 --- a/tests/Core/Challenge/Http/SimpleHttpSolverTest.php +++ b/tests/Core/Challenge/Http/SimpleHttpSolverTest.php @@ -16,10 +16,13 @@ use AcmePhp\Core\Protocol\AuthorizationChallenge; use PHPUnit\Framework\TestCase; use Prophecy\Argument; +use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Component\Console\Output\OutputInterface; class SimpleHttpSolverTest extends TestCase { + use ProphecyTrait; + public function testSupports() { $typeDns = 'dns-01'; diff --git a/tests/Core/Challenge/WaitingValidatorTest.php b/tests/Core/Challenge/WaitingValidatorTest.php index db230f03..c16ea383 100644 --- a/tests/Core/Challenge/WaitingValidatorTest.php +++ b/tests/Core/Challenge/WaitingValidatorTest.php @@ -16,10 +16,13 @@ use AcmePhp\Core\Challenge\WaitingValidator; use AcmePhp\Core\Protocol\AuthorizationChallenge; use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Bridge\PhpUnit\ClockMock; class WaitingValidatorTest extends TestCase { + use ProphecyTrait; + public function setUp(): void { parent::setUp(); From c791c3ca80c7fb59ed01c795b0617ee4fd774332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 14 Aug 2024 14:45:06 +0200 Subject: [PATCH 068/121] Update PHP-CS-Fixer to 3.62.0 and run it --- .github/workflows/test-build.yaml | 2 +- src/Cli/Action/AbstractAwsAction.php | 5 +- src/Cli/Action/BuildNginxProxyAction.php | 3 - src/Cli/Action/FilesystemAction.php | 5 +- src/Cli/Action/InstallAliyunCdnAction.php | 3 - src/Cli/Action/InstallAliyunWafAction.php | 3 - src/Cli/Action/InstallCPanelAction.php | 34 ++++++----- src/Cli/Action/PushRancherAction.php | 3 - src/Cli/Application.php | 9 --- src/Cli/Command/AbstractCommand.php | 30 ---------- .../Helper/DistinguishedNameHelper.php | 3 - src/Cli/Command/RevokeCommand.php | 6 -- src/Cli/Command/RunCommand.php | 6 -- src/Cli/Command/SelfUpdateCommand.php | 3 - src/Cli/Command/StatusCommand.php | 6 -- src/Cli/Configuration/DomainConfiguration.php | 3 - src/Cli/Exception/AcmeCliActionException.php | 2 +- src/Cli/Exception/AcmeCliException.php | 2 +- src/Cli/Exception/CommandFlowException.php | 2 +- src/Cli/Monolog/ConsoleFormatter.php | 6 -- src/Cli/Monolog/ConsoleHandler.php | 14 +---- src/Cli/Repository/Repository.php | 60 ------------------- src/Cli/Serializer/PemEncoder.php | 12 ---- src/Cli/Serializer/PemNormalizer.php | 20 ++----- src/Core/AcmeClient.php | 37 ++---------- src/Core/AcmeClientInterface.php | 28 ++++----- src/Core/Challenge/ChainValidator.php | 6 -- src/Core/Challenge/Dns/DnsDataExtractor.php | 2 +- src/Core/Challenge/Dns/DnsValidator.php | 8 +-- src/Core/Challenge/Dns/GandiSolver.php | 17 +----- src/Core/Challenge/Dns/LibDnsResolver.php | 8 +-- src/Core/Challenge/Dns/Route53Solver.php | 19 +----- src/Core/Challenge/Dns/SimpleDnsSolver.php | 15 +---- src/Core/Challenge/Http/FilesystemSolver.php | 11 +--- src/Core/Challenge/Http/HttpValidator.php | 8 +-- src/Core/Challenge/Http/MockHttpValidator.php | 6 -- .../Challenge/Http/MockServerHttpSolver.php | 9 --- src/Core/Challenge/Http/SimpleHttpSolver.php | 11 +--- src/Core/Challenge/WaitingValidator.php | 6 -- .../Exception/AcmeCoreClientException.php | 2 +- .../Exception/AcmeCoreServerException.php | 2 +- .../Exception/AcmeDnsResolutionException.php | 2 +- .../Protocol/ChallengeFailedException.php | 2 +- .../ChallengeNotSupportedException.php | 2 +- .../Protocol/ChallengeTimedOutException.php | 2 +- .../Server/BadCsrServerException.php | 2 +- .../Server/BadNonceServerException.php | 2 +- .../Exception/Server/CaaServerException.php | 2 +- .../Server/ConnectionServerException.php | 2 +- .../Exception/Server/DnsServerException.php | 2 +- .../IncorrectResponseServerException.php | 2 +- .../Server/InternalServerException.php | 2 +- .../Server/InvalidContactServerException.php | 2 +- .../Server/InvalidEmailServerException.php | 2 +- .../Server/MalformedServerException.php | 2 +- .../Server/OrderNotReadyServerException.php | 2 +- .../Server/RateLimitedServerException.php | 2 +- .../RejectedIdentifierServerException.php | 2 +- .../Exception/Server/TlsServerException.php | 2 +- .../Server/UnauthorizedServerException.php | 2 +- .../Server/UnknownHostServerException.php | 2 +- .../UnsupportedContactServerException.php | 2 +- .../UnsupportedIdentifierServerException.php | 2 +- .../UserActionRequiredServerException.php | 2 +- .../Adapter/FlysystemFtpFactory.php | 3 - .../Adapter/FlysystemLocalFactory.php | 3 - .../Adapter/FlysystemSftpFactory.php | 3 - src/Core/Http/SecureHttpClient.php | 12 ++-- src/Core/Http/ServerErrorHandler.php | 4 +- src/Core/Protocol/CertificateOrder.php | 2 +- src/Core/Util/JsonDecoder.php | 2 - src/Ssl/Certificate.php | 2 +- src/Ssl/DistinguishedName.php | 12 ++-- src/Ssl/Generator/KeyPairGenerator.php | 4 +- src/Ssl/ParsedCertificate.php | 14 ++--- src/Ssl/PrivateKey.php | 3 - src/Ssl/PublicKey.php | 3 - tests/Cli/Repository/RepositoryTest.php | 1 - tests/Ssl/CertificateTest.php | 2 +- 79 files changed, 110 insertions(+), 443 deletions(-) diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 9adfb417..38b8644b 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -18,7 +18,7 @@ jobs: - uses: actions/checkout@master - name: Install php-cs-fixer - run: wget https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v3.4.0/php-cs-fixer.phar -q + run: wget https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v3.62.0/php-cs-fixer.phar -q - name: Check coding style run: php php-cs-fixer.phar fix --dry-run --diff diff --git a/src/Cli/Action/AbstractAwsAction.php b/src/Cli/Action/AbstractAwsAction.php index baea6df9..24d2ddcc 100644 --- a/src/Cli/Action/AbstractAwsAction.php +++ b/src/Cli/Action/AbstractAwsAction.php @@ -32,9 +32,6 @@ public function __construct(ClientFactory $clientFactory) $this->clientFactory = $clientFactory; } - /** - * {@inheritdoc} - */ public function handle(array $config, CertificateResponse $response) { $this->assertConfiguration($config, ['loadbalancer', 'region']); @@ -87,7 +84,7 @@ private function cleanupOldCertificates($region, $certificatePrefix, $certificat ) { try { $this->retryCall( - // Try several time to delete certificate given AWS takes time to uninstall previous one + // Try several time to delete certificate given AWS takes time to uninstall previous one function () use ($iamClient, $certificate) { $iamClient->deleteServerCertificate( ['ServerCertificateName' => $certificate['ServerCertificateName']] diff --git a/src/Cli/Action/BuildNginxProxyAction.php b/src/Cli/Action/BuildNginxProxyAction.php index dac28a9b..6377f27a 100644 --- a/src/Cli/Action/BuildNginxProxyAction.php +++ b/src/Cli/Action/BuildNginxProxyAction.php @@ -34,9 +34,6 @@ public function __construct(RepositoryInterface $repository) $this->repository = $repository; } - /** - * {@inheritdoc} - */ public function handle(array $config, CertificateResponse $response) { $domain = $response->getCertificateRequest()->getDistinguishedName()->getCommonName(); diff --git a/src/Cli/Action/FilesystemAction.php b/src/Cli/Action/FilesystemAction.php index cede6e8d..258b7030 100644 --- a/src/Cli/Action/FilesystemAction.php +++ b/src/Cli/Action/FilesystemAction.php @@ -33,15 +33,12 @@ class FilesystemAction extends AbstractAction */ protected $filesystemFactoryLocator; - public function __construct(FlysystemFilesystemInterface $storage, ContainerInterface $locator = null) + public function __construct(FlysystemFilesystemInterface $storage, ?ContainerInterface $locator = null) { $this->storage = $storage; $this->filesystemFactoryLocator = $locator ?: new ServiceLocator([]); } - /** - * {@inheritdoc} - */ public function handle(array $config, CertificateResponse $response) { $this->assertConfiguration($config, ['adapter']); diff --git a/src/Cli/Action/InstallAliyunCdnAction.php b/src/Cli/Action/InstallAliyunCdnAction.php index a30018e8..dcdea45d 100644 --- a/src/Cli/Action/InstallAliyunCdnAction.php +++ b/src/Cli/Action/InstallAliyunCdnAction.php @@ -22,9 +22,6 @@ */ class InstallAliyunCdnAction extends AbstractAction { - /** - * {@inheritdoc} - */ public function handle(array $config, CertificateResponse $response) { $issuerChain = []; diff --git a/src/Cli/Action/InstallAliyunWafAction.php b/src/Cli/Action/InstallAliyunWafAction.php index 86bcb01e..8ccff2ec 100644 --- a/src/Cli/Action/InstallAliyunWafAction.php +++ b/src/Cli/Action/InstallAliyunWafAction.php @@ -22,9 +22,6 @@ */ class InstallAliyunWafAction extends AbstractAction { - /** - * {@inheritdoc} - */ public function handle(array $config, CertificateResponse $response) { $issuerChain = []; diff --git a/src/Cli/Action/InstallCPanelAction.php b/src/Cli/Action/InstallCPanelAction.php index 6efbdc95..cb7fef8d 100644 --- a/src/Cli/Action/InstallCPanelAction.php +++ b/src/Cli/Action/InstallCPanelAction.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace AcmePhp\Cli\Action; use AcmePhp\Ssl\Certificate; @@ -18,9 +27,6 @@ public function __construct(Client $httpClient) $this->httpClient = $httpClient; } - /** - * {@inheritdoc} - */ public function handle(array $config, CertificateResponse $response) { $this->assertConfiguration($config, ['host', 'username', 'token']); @@ -44,15 +50,15 @@ public function handle(array $config, CertificateResponse $response) private function installCertificate($config, $domain, $crt, $caBundle, $key) { - $this->httpClient->request('POST', $config['host'] . 'json-api/cpanel?' . - 'cpanel_jsonapi_apiversion=2&' . - 'cpanel_jsonapi_module=SSL&' . - 'cpanel_jsonapi_func=installssl&' . - 'domain=' . $domain . '&' . - 'crt=' . urlencode($crt) . '&' . - 'key=' . urlencode($key) . '&' . - 'cabundle=' . urlencode($caBundle), - ['headers' => ['Authorization' => 'cpanel ' . $config['username'] . ':' . $config['token']], - ]); + $this->httpClient->request('POST', $config['host'].'json-api/cpanel?'. + 'cpanel_jsonapi_apiversion=2&'. + 'cpanel_jsonapi_module=SSL&'. + 'cpanel_jsonapi_func=installssl&'. + 'domain='.$domain.'&'. + 'crt='.urlencode($crt).'&'. + 'key='.urlencode($key).'&'. + 'cabundle='.urlencode($caBundle), + ['headers' => ['Authorization' => 'cpanel '.$config['username'].':'.$config['token']], + ]); } -} \ No newline at end of file +} diff --git a/src/Cli/Action/PushRancherAction.php b/src/Cli/Action/PushRancherAction.php index c1785a42..015804ea 100644 --- a/src/Cli/Action/PushRancherAction.php +++ b/src/Cli/Action/PushRancherAction.php @@ -35,9 +35,6 @@ public function __construct(Client $httpClient) $this->httpClient = $httpClient; } - /** - * {@inheritdoc} - */ public function handle(array $config, CertificateResponse $response) { $payload = $this->createRancherPayloadFromResponse($response); diff --git a/src/Cli/Application.php b/src/Cli/Application.php index 19bb14db..ae5a98cb 100644 --- a/src/Cli/Application.php +++ b/src/Cli/Application.php @@ -31,17 +31,11 @@ class Application extends BaseApplication 'localhost' => 'https://localhost:14000/dir', ]; - /** - * {@inheritdoc} - */ public function __construct() { parent::__construct('Acme PHP - Let\'s Encrypt/ZeroSSL client', '2.0.0'); } - /** - * {@inheritdoc} - */ protected function getDefaultCommands(): array { return array_merge(parent::getDefaultCommands(), [ @@ -52,9 +46,6 @@ protected function getDefaultCommands(): array ]); } - /** - * {@inheritdoc} - */ protected function getDefaultHelperSet(): HelperSet { $set = parent::getDefaultHelperSet(); diff --git a/src/Cli/Command/AbstractCommand.php b/src/Cli/Command/AbstractCommand.php index 35162886..408bd164 100644 --- a/src/Cli/Command/AbstractCommand.php +++ b/src/Cli/Command/AbstractCommand.php @@ -47,9 +47,6 @@ abstract class AbstractCommand extends Command implements LoggerInterface */ private $container; - /** - * {@inheritdoc} - */ protected function initialize(InputInterface $input, OutputInterface $output) { $this->input = $input; @@ -134,73 +131,46 @@ private function initializeContainer() $this->container->set('output', $this->output); } - /** - * {@inheritdoc} - */ public function emergency($message, array $context = []) { return $this->getCliLogger()->emergency($message, $context); } - /** - * {@inheritdoc} - */ public function alert($message, array $context = []) { return $this->getCliLogger()->alert($message, $context); } - /** - * {@inheritdoc} - */ public function critical($message, array $context = []) { return $this->getCliLogger()->critical($message, $context); } - /** - * {@inheritdoc} - */ public function error($message, array $context = []) { return $this->getCliLogger()->error($message, $context); } - /** - * {@inheritdoc} - */ public function warning($message, array $context = []) { return $this->getCliLogger()->warning($message, $context); } - /** - * {@inheritdoc} - */ public function notice($message, array $context = []) { return $this->getCliLogger()->notice($message, $context); } - /** - * {@inheritdoc} - */ public function info($message, array $context = []) { return $this->getCliLogger()->info($message, $context); } - /** - * {@inheritdoc} - */ public function debug($message, array $context = []) { return $this->getCliLogger()->debug($message, $context); } - /** - * {@inheritdoc} - */ public function log($level, $message, array $context = []) { return $this->getCliLogger()->log($level, $message, $context); diff --git a/src/Cli/Command/Helper/DistinguishedNameHelper.php b/src/Cli/Command/Helper/DistinguishedNameHelper.php index cb34c4bf..5b0452b7 100644 --- a/src/Cli/Command/Helper/DistinguishedNameHelper.php +++ b/src/Cli/Command/Helper/DistinguishedNameHelper.php @@ -23,9 +23,6 @@ */ class DistinguishedNameHelper extends Helper { - /** - * {@inheritdoc} - */ public function getName() { return 'distinguished_name'; diff --git a/src/Cli/Command/RevokeCommand.php b/src/Cli/Command/RevokeCommand.php index 29f85def..18e4e227 100644 --- a/src/Cli/Command/RevokeCommand.php +++ b/src/Cli/Command/RevokeCommand.php @@ -21,9 +21,6 @@ class RevokeCommand extends AbstractCommand { - /** - * {@inheritdoc} - */ protected function configure() { $reasons = implode(PHP_EOL, RevocationReason::getFormattedReasons()); @@ -44,9 +41,6 @@ protected function configure() ->setHelp('The %command.name% command revoke a previously obtained certificate for a given domain'); } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output) { if (!isset(Application::PROVIDERS[$this->input->getOption('provider')])) { diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index 1a70880c..cca39a3c 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -50,9 +50,6 @@ class RunCommand extends AbstractCommand private $config; - /** - * {@inheritdoc} - */ protected function configure() { $this->setName('run') @@ -72,9 +69,6 @@ protected function configure() ->setHelp('The %command.name% challenge the domains, request the certificates and install them following a given configuration.'); } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output) { $this->config = $this->getConfig(Path::makeAbsolute($input->getArgument('config'), getcwd())); diff --git a/src/Cli/Command/SelfUpdateCommand.php b/src/Cli/Command/SelfUpdateCommand.php index b2f03b33..8e6024d9 100644 --- a/src/Cli/Command/SelfUpdateCommand.php +++ b/src/Cli/Command/SelfUpdateCommand.php @@ -87,9 +87,6 @@ protected function configure() ); } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output) { $this->output = $output; diff --git a/src/Cli/Command/StatusCommand.php b/src/Cli/Command/StatusCommand.php index 524b9b56..a54997b3 100644 --- a/src/Cli/Command/StatusCommand.php +++ b/src/Cli/Command/StatusCommand.php @@ -23,9 +23,6 @@ */ class StatusCommand extends AbstractCommand { - /** - * {@inheritdoc} - */ protected function configure() { $this->setName('status') @@ -38,9 +35,6 @@ protected function configure() ); } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output) { $repository = $this->getRepository(); diff --git a/src/Cli/Configuration/DomainConfiguration.php b/src/Cli/Configuration/DomainConfiguration.php index 99040495..251bde9f 100644 --- a/src/Cli/Configuration/DomainConfiguration.php +++ b/src/Cli/Configuration/DomainConfiguration.php @@ -20,9 +20,6 @@ */ class DomainConfiguration implements ConfigurationInterface { - /** - * {@inheritdoc} - */ public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder('acmephp'); diff --git a/src/Cli/Exception/AcmeCliActionException.php b/src/Cli/Exception/AcmeCliActionException.php index fa785b61..2275e275 100644 --- a/src/Cli/Exception/AcmeCliActionException.php +++ b/src/Cli/Exception/AcmeCliActionException.php @@ -16,7 +16,7 @@ */ class AcmeCliActionException extends AcmeCliException { - public function __construct(string $actionName, \Exception $previous = null) + public function __construct(string $actionName, ?\Exception $previous = null) { parent::__construct(sprintf('An exception was thrown during action "%s"', $actionName), $previous); } diff --git a/src/Cli/Exception/AcmeCliException.php b/src/Cli/Exception/AcmeCliException.php index b5f4c0c7..321021a9 100644 --- a/src/Cli/Exception/AcmeCliException.php +++ b/src/Cli/Exception/AcmeCliException.php @@ -16,7 +16,7 @@ */ class AcmeCliException extends \RuntimeException { - public function __construct($message, \Exception $previous = null) + public function __construct($message, ?\Exception $previous = null) { parent::__construct($message, 0, $previous); } diff --git a/src/Cli/Exception/CommandFlowException.php b/src/Cli/Exception/CommandFlowException.php index fcaafe96..387577e8 100644 --- a/src/Cli/Exception/CommandFlowException.php +++ b/src/Cli/Exception/CommandFlowException.php @@ -25,7 +25,7 @@ class CommandFlowException extends AcmeCliException * @param string $command Name of the command to run in order to fix the flow * @param array $arguments Optional list of missing arguments */ - public function __construct(string $missing, string $command, array $arguments = [], \Exception $previous = null) + public function __construct(string $missing, string $command, array $arguments = [], ?\Exception $previous = null) { $this->missing = $missing; $this->command = $command; diff --git a/src/Cli/Monolog/ConsoleFormatter.php b/src/Cli/Monolog/ConsoleFormatter.php index e9ad5281..36346f2f 100644 --- a/src/Cli/Monolog/ConsoleFormatter.php +++ b/src/Cli/Monolog/ConsoleFormatter.php @@ -27,17 +27,11 @@ class ConsoleFormatter extends LineFormatter { public const SIMPLE_FORMAT = "%start_tag%%message% %context% %extra%%end_tag%\n"; - /** - * {@inheritdoc} - */ public function __construct($format = null, $dateFormat = null, $allowInlineLineBreaks = false, $ignoreEmptyContextAndExtra = true) { parent::__construct($format, $dateFormat, $allowInlineLineBreaks, $ignoreEmptyContextAndExtra); } - /** - * {@inheritdoc} - */ public function format(array $record): string { if ($record['level'] >= Logger::ERROR) { diff --git a/src/Cli/Monolog/ConsoleHandler.php b/src/Cli/Monolog/ConsoleHandler.php index 45cc8bde..f509b638 100644 --- a/src/Cli/Monolog/ConsoleHandler.php +++ b/src/Cli/Monolog/ConsoleHandler.php @@ -52,7 +52,7 @@ class ConsoleHandler extends AbstractProcessingHandler * @param array $verbosityLevelMap Array that maps the OutputInterface verbosity to a minimum logging * level (leave empty to use the default mapping) */ - public function __construct(OutputInterface $output = null, bool $bubble = true, array $verbosityLevelMap = []) + public function __construct(?OutputInterface $output = null, bool $bubble = true, array $verbosityLevelMap = []) { parent::__construct(Logger::DEBUG, $bubble); @@ -63,17 +63,11 @@ public function __construct(OutputInterface $output = null, bool $bubble = true, } } - /** - * {@inheritdoc} - */ public function isHandling(array $record): bool { return $this->updateLevel() && parent::isHandling($record); } - /** - * {@inheritdoc} - */ public function handle(array $record): bool { // we have to update the logging level each time because the verbosity of the @@ -101,17 +95,11 @@ public function close(): void parent::close(); } - /** - * {@inheritdoc} - */ protected function write(array $record): void { $this->output->write((string) $record['formatted']); } - /** - * {@inheritdoc} - */ protected function getDefaultFormatter(): FormatterInterface { $formatter = new ConsoleFormatter(); diff --git a/src/Cli/Repository/Repository.php b/src/Cli/Repository/Repository.php index 0b475b16..fd80e8d2 100644 --- a/src/Cli/Repository/Repository.php +++ b/src/Cli/Repository/Repository.php @@ -60,9 +60,6 @@ public function __construct(SerializerInterface $serializer, FilesystemInterface $this->storage = $storage; } - /** - * {@inheritdoc} - */ public function storeCertificateResponse(CertificateResponse $certificateResponse) { $distinguishedName = $certificateResponse->getCertificateRequest()->getDistinguishedName(); @@ -73,9 +70,6 @@ public function storeCertificateResponse(CertificateResponse $certificateRespons $this->storeDomainCertificate($domain, $certificateResponse->getCertificate()); } - /** - * {@inheritdoc} - */ public function storeAccountKeyPair(KeyPair $keyPair) { try { @@ -93,17 +87,11 @@ public function storeAccountKeyPair(KeyPair $keyPair) } } - /** - * {@inheritdoc} - */ public function hasAccountKeyPair(): bool { return $this->storage->has(self::PATH_ACCOUNT_KEY_PRIVATE); } - /** - * {@inheritdoc} - */ public function loadAccountKeyPair(): KeyPair { try { @@ -119,9 +107,6 @@ public function loadAccountKeyPair(): KeyPair } } - /** - * {@inheritdoc} - */ public function storeDomainKeyPair(string $domain, KeyPair $keyPair) { try { @@ -139,17 +124,11 @@ public function storeDomainKeyPair(string $domain, KeyPair $keyPair) } } - /** - * {@inheritdoc} - */ public function hasDomainKeyPair(string $domain): bool { return $this->storage->has($this->getPathForDomain(self::PATH_DOMAIN_KEY_PRIVATE, $domain)); } - /** - * {@inheritdoc} - */ public function loadDomainKeyPair(string $domain): KeyPair { try { @@ -165,9 +144,6 @@ public function loadDomainKeyPair(string $domain): KeyPair } } - /** - * {@inheritdoc} - */ public function storeDomainAuthorizationChallenge(string $domain, AuthorizationChallenge $authorizationChallenge) { try { @@ -180,17 +156,11 @@ public function storeDomainAuthorizationChallenge(string $domain, AuthorizationC } } - /** - * {@inheritdoc} - */ public function hasDomainAuthorizationChallenge(string $domain): bool { return $this->storage->has($this->getPathForDomain(self::PATH_CACHE_AUTHORIZATION_CHALLENGE, $domain)); } - /** - * {@inheritdoc} - */ public function loadDomainAuthorizationChallenge(string $domain): AuthorizationChallenge { try { @@ -202,9 +172,6 @@ public function loadDomainAuthorizationChallenge(string $domain): AuthorizationC } } - /** - * {@inheritdoc} - */ public function storeDomainDistinguishedName(string $domain, DistinguishedName $distinguishedName) { try { @@ -217,17 +184,11 @@ public function storeDomainDistinguishedName(string $domain, DistinguishedName $ } } - /** - * {@inheritdoc} - */ public function hasDomainDistinguishedName(string $domain): bool { return $this->storage->has($this->getPathForDomain(self::PATH_CACHE_DISTINGUISHED_NAME, $domain)); } - /** - * {@inheritdoc} - */ public function loadDomainDistinguishedName(string $domain): DistinguishedName { try { @@ -239,9 +200,6 @@ public function loadDomainDistinguishedName(string $domain): DistinguishedName } } - /** - * {@inheritdoc} - */ public function storeDomainCertificate(string $domain, Certificate $certificate) { // Simple certificate @@ -272,17 +230,11 @@ public function storeDomainCertificate(string $domain, Certificate $certificate) $this->save($this->getPathForDomain(self::PATH_DOMAIN_CERT_COMBINED, $domain), $combinedPem); } - /** - * {@inheritdoc} - */ public function hasDomainCertificate(string $domain): bool { return $this->storage->has($this->getPathForDomain(self::PATH_DOMAIN_CERT_FULLCHAIN, $domain)); } - /** - * {@inheritdoc} - */ public function loadDomainCertificate(string $domain): Certificate { try { @@ -309,9 +261,6 @@ public function loadDomainCertificate(string $domain): Certificate return $certificate; } - /** - * {@inheritdoc} - */ public function storeCertificateOrder(array $domains, CertificateOrder $order) { try { @@ -324,17 +273,11 @@ public function storeCertificateOrder(array $domains, CertificateOrder $order) } } - /** - * {@inheritdoc} - */ public function hasCertificateOrder(array $domains): bool { return $this->storage->has($this->getPathForDomainList(self::PATH_CACHE_CERTIFICATE_ORDER, $domains)); } - /** - * {@inheritdoc} - */ public function loadCertificateOrder(array $domains): CertificateOrder { try { @@ -346,9 +289,6 @@ public function loadCertificateOrder(array $domains): CertificateOrder } } - /** - * {@inheritdoc} - */ public function save(string $path, string $content, string $visibility = self::VISIBILITY_PRIVATE) { if (!$this->storage->has($path)) { diff --git a/src/Cli/Serializer/PemEncoder.php b/src/Cli/Serializer/PemEncoder.php index d9f15c35..e1f0a459 100644 --- a/src/Cli/Serializer/PemEncoder.php +++ b/src/Cli/Serializer/PemEncoder.php @@ -21,33 +21,21 @@ class PemEncoder implements EncoderInterface, DecoderInterface { public const FORMAT = 'pem'; - /** - * {@inheritdoc} - */ public function encode($data, $format, array $context = []): string { return trim($data)."\n"; } - /** - * {@inheritdoc} - */ public function decode($data, $format, array $context = []) { return trim($data)."\n"; } - /** - * {@inheritdoc} - */ public function supportsEncoding($format): bool { return self::FORMAT === $format; } - /** - * {@inheritdoc} - */ public function supportsDecoding($format) { return self::FORMAT === $format; diff --git a/src/Cli/Serializer/PemNormalizer.php b/src/Cli/Serializer/PemNormalizer.php index 2a59ac7e..0b9f8f57 100644 --- a/src/Cli/Serializer/PemNormalizer.php +++ b/src/Cli/Serializer/PemNormalizer.php @@ -21,34 +21,22 @@ */ class PemNormalizer implements NormalizerInterface, DenormalizerInterface { - /** - * {@inheritdoc} - */ - public function normalize($object, string $format = null, array $context = []) + public function normalize($object, ?string $format = null, array $context = []) { return $object->getPEM(); } - /** - * {@inheritdoc} - */ - public function denormalize($data, $class, string $format = null, array $context = []) + public function denormalize($data, $class, ?string $format = null, array $context = []) { return new $class($data); } - /** - * {@inheritdoc} - */ - public function supportsNormalization($data, string $format = null) + public function supportsNormalization($data, ?string $format = null) { return \is_object($data) && ($data instanceof Certificate || $data instanceof Key); } - /** - * {@inheritdoc} - */ - public function supportsDenormalization($data, $type, string $format = null) + public function supportsDenormalization($data, $type, ?string $format = null) { return \is_string($data); } diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index cae90bc6..50ab20ac 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -68,17 +68,14 @@ class AcmeClient implements AcmeClientInterface */ private $account; - public function __construct(SecureHttpClient $httpClient, string $directoryUrl, CertificateRequestSigner $csrSigner = null) + public function __construct(SecureHttpClient $httpClient, string $directoryUrl, ?CertificateRequestSigner $csrSigner = null) { $this->uninitializedHttpClient = $httpClient; $this->directoryUrl = $directoryUrl; $this->csrSigner = $csrSigner ?: new CertificateRequestSigner(); } - /** - * {@inheritdoc} - */ - public function registerAccount(string $email = null, ExternalAccount $externalAccount = null): array + public function registerAccount(?string $email = null, ?ExternalAccount $externalAccount = null): array { $client = $this->getHttpClient(); @@ -104,9 +101,6 @@ public function registerAccount(string $email = null, ExternalAccount $externalA return $client->request('POST', $account, $client->signKidPayload($account, $account, null)); } - /** - * {@inheritdoc} - */ public function requestOrder(array $domains): CertificateOrder { Assert::allStringNotEmpty($domains, 'requestOrder::$domains expected a list of strings. Got: %s'); @@ -143,9 +137,6 @@ static function ($domain) { return new CertificateOrder($authorizationsChallenges, $orderEndpoint, $response['status']); } - /** - * {@inheritdoc} - */ public function reloadOrder(CertificateOrder $order): CertificateOrder { $client = $this->getHttpClient(); @@ -168,9 +159,6 @@ public function reloadOrder(CertificateOrder $order): CertificateOrder return new CertificateOrder($authorizationsChallenges, $orderEndpoint, $response['status']); } - /** - * {@inheritdoc} - */ public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, int $timeout = 180, bool $returnAlternateCertificateIfAvailable = false): CertificateResponse { $endTime = time() + $timeout; @@ -216,9 +204,6 @@ public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, return $this->createCertificateResponse($csr, Utils::copyToString($response->getBody())); } - /** - * {@inheritdoc} - */ public function requestAuthorization(string $domain): array { $order = $this->requestOrder([$domain]); @@ -230,9 +215,6 @@ public function requestAuthorization(string $domain): array } } - /** - * {@inheritdoc} - */ public function reloadAuthorization(AuthorizationChallenge $challenge): AuthorizationChallenge { $client = $this->getHttpClient(); @@ -242,9 +224,6 @@ public function reloadAuthorization(AuthorizationChallenge $challenge): Authoriz return $this->createAuthorizationChallenge($challenge->getDomain(), $response); } - /** - * {@inheritdoc} - */ public function challengeAuthorization(AuthorizationChallenge $challenge, int $timeout = 180): array { $endTime = time() + $timeout; @@ -271,9 +250,6 @@ public function challengeAuthorization(AuthorizationChallenge $challenge, int $t return $response; } - /** - * {@inheritdoc} - */ public function requestCertificate(string $domain, CertificateRequest $csr, int $timeout = 180, bool $returnAlternateCertificateIfAvailable = false): CertificateResponse { $order = $this->requestOrder(array_unique(array_merge([$domain], $csr->getDistinguishedName()->getSubjectAlternativeNames()))); @@ -281,10 +257,7 @@ public function requestCertificate(string $domain, CertificateRequest $csr, int return $this->finalizeOrder($order, $csr, $timeout, $returnAlternateCertificateIfAvailable); } - /** - * {@inheritdoc} - */ - public function revokeCertificate(Certificate $certificate, RevocationReason $revocationReason = null) + public function revokeCertificate(Certificate $certificate, ?RevocationReason $revocationReason = null) { if (!$endpoint = $this->getResourceUrl(ResourcesDirectory::REVOKE_CERT)) { throw new CertificateRevocationException('This ACME server does not support certificate revocation.'); @@ -332,10 +305,10 @@ public function getResourceUrl(string $resource): string /** * Request a resource (URL is found using ACME server directory). * + * @return array|string + * * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * @throws AcmeCoreClientException when an error occured during response parsing - * - * @return array|string */ protected function requestResource(string $method, string $resource, array $payload, bool $returnJson = true) { diff --git a/src/Core/AcmeClientInterface.php b/src/Core/AcmeClientInterface.php index e035bbd7..3ac4440d 100644 --- a/src/Core/AcmeClientInterface.php +++ b/src/Core/AcmeClientInterface.php @@ -40,13 +40,13 @@ interface AcmeClientInterface * @param string|null $email an optionnal e-mail to associate with the account * @param ExternalAccount|null $externalAccount an optionnal External Account to use for External Account Binding * + * @return array the Certificate Authority response decoded from JSON into an array + * * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * (the exception will be more specific if detail is provided) * @throws AcmeCoreClientException when an error occured during response parsing - * - * @return array the Certificate Authority response decoded from JSON into an array */ - public function registerAccount(string $email = null, ExternalAccount $externalAccount = null): array; + public function registerAccount(?string $email = null, ?ExternalAccount $externalAccount = null): array; /** * Request authorization challenge data for a list of domains. @@ -57,12 +57,12 @@ public function registerAccount(string $email = null, ExternalAccount $externalA * * @param string[] $domains the domains to challenge * + * @return CertificateOrder the Order returned by the Certificate Authority + * * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * (the exception will be more specific if detail is provided) * @throws AcmeCoreClientException when an error occured during response parsing * @throws ChallengeNotSupportedException when the HTTP challenge is not supported by the server - * - * @return CertificateOrder the Order returned by the Certificate Authority */ public function requestOrder(array $domains): CertificateOrder; @@ -89,13 +89,13 @@ public function reloadOrder(CertificateOrder $order): CertificateOrder; * This is especially useful following * https://letsencrypt.org/2019/04/15/transitioning-to-isrg-root.html. * + * @return CertificateResponse the certificate data to save it somewhere you want + * * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * (the exception will be more specific if detail is provided) * @throws AcmeCoreClientException when an error occured during response parsing * @throws CertificateRequestFailedException when the certificate request failed * @throws CertificateRequestTimedOutException when the certificate request timed out - * - * @return CertificateResponse the certificate data to save it somewhere you want */ public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, int $timeout = 180, bool $returnAlternateCertificateIfAvailable = false): CertificateResponse; @@ -108,12 +108,12 @@ public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, * * @param string $domain the domain to challenge * + * @return AuthorizationChallenge[] the list of challenges data returned by the Certificate Authority + * * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * (the exception will be more specific if detail is provided) * @throws AcmeCoreClientException when an error occured during response parsing * @throws ChallengeNotSupportedException when the HTTP challenge is not supported by the server - * - * @return AuthorizationChallenge[] the list of challenges data returned by the Certificate Authority */ public function requestAuthorization(string $domain): array; @@ -140,13 +140,13 @@ public function reloadAuthorization(AuthorizationChallenge $challenge): Authoriz * @param AuthorizationChallenge $challenge the challenge data to check * @param int $timeout the timeout period * + * @return array the validate challenge response + * * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * (the exception will be more specific if detail is provided) * @throws AcmeCoreClientException when an error occured during response parsing * @throws ChallengeTimedOutException when the challenge timed out * @throws ChallengeFailedException when the challenge failed - * - * @return array the validate challenge response */ public function challengeAuthorization(AuthorizationChallenge $challenge, int $timeout = 180): array; @@ -168,13 +168,13 @@ public function challengeAuthorization(AuthorizationChallenge $challenge, int $t * This is especially useful following * https://letsencrypt.org/2019/04/15/transitioning-to-isrg-root.html. * + * @return CertificateResponse the certificate data to save it somewhere you want + * * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * (the exception will be more specific if detail is provided) * @throws AcmeCoreClientException when an error occured during response parsing * @throws CertificateRequestFailedException when the certificate request failed * @throws CertificateRequestTimedOutException when the certificate request timed out - * - * @return CertificateResponse the certificate data to save it somewhere you want */ public function requestCertificate(string $domain, CertificateRequest $csr, int $timeout = 180, bool $returnAlternateCertificateIfAvailable = false): CertificateResponse; @@ -183,5 +183,5 @@ public function requestCertificate(string $domain, CertificateRequest $csr, int * * @throws CertificateRevocationException */ - public function revokeCertificate(Certificate $certificate, RevocationReason $revocationReason = null); + public function revokeCertificate(Certificate $certificate, ?RevocationReason $revocationReason = null); } diff --git a/src/Core/Challenge/ChainValidator.php b/src/Core/Challenge/ChainValidator.php index 30552372..c9ec5481 100644 --- a/src/Core/Challenge/ChainValidator.php +++ b/src/Core/Challenge/ChainValidator.php @@ -32,9 +32,6 @@ public function __construct(array $validators) $this->validators = $validators; } - /** - * {@inheritdoc} - */ public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { foreach ($this->validators as $validator) { @@ -46,9 +43,6 @@ public function supports(AuthorizationChallenge $authorizationChallenge, SolverI return false; } - /** - * {@inheritdoc} - */ public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { foreach ($this->validators as $validator) { diff --git a/src/Core/Challenge/Dns/DnsDataExtractor.php b/src/Core/Challenge/Dns/DnsDataExtractor.php index b64ea733..135b774d 100644 --- a/src/Core/Challenge/Dns/DnsDataExtractor.php +++ b/src/Core/Challenge/Dns/DnsDataExtractor.php @@ -24,7 +24,7 @@ class DnsDataExtractor /** @var Base64SafeEncoder */ private $encoder; - public function __construct(Base64SafeEncoder $encoder = null) + public function __construct(?Base64SafeEncoder $encoder = null) { $this->encoder = $encoder ?: new Base64SafeEncoder(); } diff --git a/src/Core/Challenge/Dns/DnsValidator.php b/src/Core/Challenge/Dns/DnsValidator.php index 998da9d9..bb6d9333 100644 --- a/src/Core/Challenge/Dns/DnsValidator.php +++ b/src/Core/Challenge/Dns/DnsValidator.php @@ -33,7 +33,7 @@ class DnsValidator implements ValidatorInterface */ private $dnsResolver; - public function __construct(DnsDataExtractor $extractor = null, DnsResolverInterface $dnsResolver = null) + public function __construct(?DnsDataExtractor $extractor = null, ?DnsResolverInterface $dnsResolver = null) { $this->extractor = $extractor ?: new DnsDataExtractor(); @@ -43,17 +43,11 @@ public function __construct(DnsDataExtractor $extractor = null, DnsResolverInter } } - /** - * {@inheritdoc} - */ public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { return 'dns-01' === $authorizationChallenge->getType(); } - /** - * {@inheritdoc} - */ public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { $recordName = $this->extractor->getRecordName($authorizationChallenge); diff --git a/src/Core/Challenge/Dns/GandiSolver.php b/src/Core/Challenge/Dns/GandiSolver.php index a4ad28b5..eb52c0a0 100644 --- a/src/Core/Challenge/Dns/GandiSolver.php +++ b/src/Core/Challenge/Dns/GandiSolver.php @@ -49,7 +49,7 @@ class GandiSolver implements MultipleChallengesSolverInterface, ConfigurableServ */ private $apiKey; - public function __construct(DnsDataExtractor $extractor = null, ClientInterface $client = null) + public function __construct(?DnsDataExtractor $extractor = null, ?ClientInterface $client = null) { $this->extractor = $extractor ?: new DnsDataExtractor(); $this->client = $client ?: new Client(); @@ -64,25 +64,16 @@ public function configure(array $config) $this->apiKey = $config['api_key']; } - /** - * {@inheritdoc} - */ public function supports(AuthorizationChallenge $authorizationChallenge): bool { return 'dns-01' === $authorizationChallenge->getType(); } - /** - * {@inheritdoc} - */ public function solve(AuthorizationChallenge $authorizationChallenge) { return $this->solveAll([$authorizationChallenge]); } - /** - * {@inheritdoc} - */ public function solveAll(array $authorizationChallenges) { Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); @@ -112,17 +103,11 @@ public function solveAll(array $authorizationChallenges) } } - /** - * {@inheritdoc} - */ public function cleanup(AuthorizationChallenge $authorizationChallenge) { return $this->cleanupAll([$authorizationChallenge]); } - /** - * {@inheritdoc} - */ public function cleanupAll(array $authorizationChallenges) { Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); diff --git a/src/Core/Challenge/Dns/LibDnsResolver.php b/src/Core/Challenge/Dns/LibDnsResolver.php index 3030da2e..2fa888b6 100644 --- a/src/Core/Challenge/Dns/LibDnsResolver.php +++ b/src/Core/Challenge/Dns/LibDnsResolver.php @@ -58,10 +58,10 @@ class LibDnsResolver implements DnsResolverInterface private $nameServer; public function __construct( - QuestionFactory $questionFactory = null, - MessageFactory $messageFactory = null, - Encoder $encoder = null, - Decoder $decoder = null, + ?QuestionFactory $questionFactory = null, + ?MessageFactory $messageFactory = null, + ?Encoder $encoder = null, + ?Decoder $decoder = null, $nameServer = '8.8.8.8' ) { $this->questionFactory = $questionFactory ?: new QuestionFactory(); diff --git a/src/Core/Challenge/Dns/Route53Solver.php b/src/Core/Challenge/Dns/Route53Solver.php index 6f28f8b2..53975320 100644 --- a/src/Core/Challenge/Dns/Route53Solver.php +++ b/src/Core/Challenge/Dns/Route53Solver.php @@ -43,32 +43,23 @@ class Route53Solver implements MultipleChallengesSolverInterface */ private $cacheZones; - public function __construct(DnsDataExtractor $extractor = null, Route53Client $client = null) + public function __construct(?DnsDataExtractor $extractor = null, ?Route53Client $client = null) { $this->extractor = $extractor ?: new DnsDataExtractor(); $this->client = $client ?: new Route53Client([]); $this->logger = new NullLogger(); } - /** - * {@inheritdoc} - */ public function supports(AuthorizationChallenge $authorizationChallenge): bool { return 'dns-01' === $authorizationChallenge->getType(); } - /** - * {@inheritdoc} - */ public function solve(AuthorizationChallenge $authorizationChallenge) { return $this->solveAll([$authorizationChallenge]); } - /** - * {@inheritdoc} - */ public function solveAll(array $authorizationChallenges) { Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); @@ -114,17 +105,11 @@ public function solveAll(array $authorizationChallenges) } } - /** - * {@inheritdoc} - */ public function cleanup(AuthorizationChallenge $authorizationChallenge) { return $this->cleanupAll([$authorizationChallenge]); } - /** - * {@inheritdoc} - */ public function cleanupAll(array $authorizationChallenges) { Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); @@ -198,7 +183,7 @@ function ($recordSet) use ($recordName) { private function getSaveRecordQuery($recordName, array $recordIndex) { - //remove old indexes + // remove old indexes $limitTime = time() - 86400; foreach ($recordIndex as $recordValue => $time) { if ($time < $limitTime) { diff --git a/src/Core/Challenge/Dns/SimpleDnsSolver.php b/src/Core/Challenge/Dns/SimpleDnsSolver.php index 80535f09..f5703acd 100644 --- a/src/Core/Challenge/Dns/SimpleDnsSolver.php +++ b/src/Core/Challenge/Dns/SimpleDnsSolver.php @@ -33,27 +33,17 @@ class SimpleDnsSolver implements SolverInterface */ protected $output; - /** - * @param DnsDataExtractor $extractor - * @param OutputInterface $output - */ - public function __construct(DnsDataExtractor $extractor = null, OutputInterface $output = null) + public function __construct(?DnsDataExtractor $extractor = null, ?OutputInterface $output = null) { $this->extractor = $extractor ?: new DnsDataExtractor(); $this->output = $output ?: new NullOutput(); } - /** - * {@inheritdoc} - */ public function supports(AuthorizationChallenge $authorizationChallenge): bool { return 'dns-01' === $authorizationChallenge->getType(); } - /** - * {@inheritdoc} - */ public function solve(AuthorizationChallenge $authorizationChallenge) { $recordName = $this->extractor->getRecordName($authorizationChallenge); @@ -80,9 +70,6 @@ public function solve(AuthorizationChallenge $authorizationChallenge) ); } - /** - * {@inheritdoc} - */ public function cleanup(AuthorizationChallenge $authorizationChallenge) { $recordName = $this->extractor->getRecordName($authorizationChallenge); diff --git a/src/Core/Challenge/Http/FilesystemSolver.php b/src/Core/Challenge/Http/FilesystemSolver.php index 8a4e879d..58a781e5 100644 --- a/src/Core/Challenge/Http/FilesystemSolver.php +++ b/src/Core/Challenge/Http/FilesystemSolver.php @@ -43,7 +43,7 @@ class FilesystemSolver implements SolverInterface, ConfigurableServiceInterface */ private $extractor; - public function __construct(ContainerInterface $filesystemFactoryLocator = null, HttpDataExtractor $extractor = null) + public function __construct(?ContainerInterface $filesystemFactoryLocator = null, ?HttpDataExtractor $extractor = null) { $this->filesystemFactoryLocator = $filesystemFactoryLocator ?: new ServiceLocator([]); $this->extractor = $extractor ?: new HttpDataExtractor(); @@ -59,17 +59,11 @@ public function configure(array $config) $this->filesystem = $factory->create($config); } - /** - * {@inheritdoc} - */ public function supports(AuthorizationChallenge $authorizationChallenge): bool { return 'http-01' === $authorizationChallenge->getType(); } - /** - * {@inheritdoc} - */ public function solve(AuthorizationChallenge $authorizationChallenge) { $checkPath = $this->extractor->getCheckPath($authorizationChallenge); @@ -78,9 +72,6 @@ public function solve(AuthorizationChallenge $authorizationChallenge) $this->filesystem->write($checkPath, $checkContent); } - /** - * {@inheritdoc} - */ public function cleanup(AuthorizationChallenge $authorizationChallenge) { $checkPath = $this->extractor->getCheckPath($authorizationChallenge); diff --git a/src/Core/Challenge/Http/HttpValidator.php b/src/Core/Challenge/Http/HttpValidator.php index 643572a2..f0933fe6 100644 --- a/src/Core/Challenge/Http/HttpValidator.php +++ b/src/Core/Challenge/Http/HttpValidator.php @@ -34,23 +34,17 @@ class HttpValidator implements ValidatorInterface */ private $client; - public function __construct(HttpDataExtractor $extractor = null, Client $client = null) + public function __construct(?HttpDataExtractor $extractor = null, ?Client $client = null) { $this->extractor = $extractor ?: new HttpDataExtractor(); $this->client = $client ?: new Client(); } - /** - * {@inheritdoc} - */ public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { return 'http-01' === $authorizationChallenge->getType() && !$solver instanceof MockServerHttpSolver; } - /** - * {@inheritdoc} - */ public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { $checkUrl = $this->extractor->getCheckUrl($authorizationChallenge); diff --git a/src/Core/Challenge/Http/MockHttpValidator.php b/src/Core/Challenge/Http/MockHttpValidator.php index d3d87ed6..15b2cbea 100644 --- a/src/Core/Challenge/Http/MockHttpValidator.php +++ b/src/Core/Challenge/Http/MockHttpValidator.php @@ -22,17 +22,11 @@ */ class MockHttpValidator implements ValidatorInterface { - /** - * {@inheritdoc} - */ public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { return 'http-01' === $authorizationChallenge->getType() && $solver instanceof MockServerHttpSolver; } - /** - * {@inheritdoc} - */ public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { return true; diff --git a/src/Core/Challenge/Http/MockServerHttpSolver.php b/src/Core/Challenge/Http/MockServerHttpSolver.php index 72c73a2c..4ea416d3 100644 --- a/src/Core/Challenge/Http/MockServerHttpSolver.php +++ b/src/Core/Challenge/Http/MockServerHttpSolver.php @@ -23,17 +23,11 @@ */ class MockServerHttpSolver implements SolverInterface { - /** - * {@inheritdoc} - */ public function supports(AuthorizationChallenge $authorizationChallenge): bool { return 'http-01' === $authorizationChallenge->getType(); } - /** - * {@inheritdoc} - */ public function solve(AuthorizationChallenge $authorizationChallenge) { (new Client())->post('http://localhost:8055/add-http01', [ @@ -44,9 +38,6 @@ public function solve(AuthorizationChallenge $authorizationChallenge) ]); } - /** - * {@inheritdoc} - */ public function cleanup(AuthorizationChallenge $authorizationChallenge) { (new Client())->post('http://localhost:8055/del-http01', [ diff --git a/src/Core/Challenge/Http/SimpleHttpSolver.php b/src/Core/Challenge/Http/SimpleHttpSolver.php index 7be53501..b0dea71c 100644 --- a/src/Core/Challenge/Http/SimpleHttpSolver.php +++ b/src/Core/Challenge/Http/SimpleHttpSolver.php @@ -33,23 +33,17 @@ class SimpleHttpSolver implements SolverInterface */ private $output; - public function __construct(HttpDataExtractor $extractor = null, OutputInterface $output = null) + public function __construct(?HttpDataExtractor $extractor = null, ?OutputInterface $output = null) { $this->extractor = $extractor ?: new HttpDataExtractor(); $this->output = $output ?: new NullOutput(); } - /** - * {@inheritdoc} - */ public function supports(AuthorizationChallenge $authorizationChallenge): bool { return 'http-01' === $authorizationChallenge->getType(); } - /** - * {@inheritdoc} - */ public function solve(AuthorizationChallenge $authorizationChallenge) { $checkUrl = $this->extractor->getCheckUrl($authorizationChallenge); @@ -75,9 +69,6 @@ public function solve(AuthorizationChallenge $authorizationChallenge) ); } - /** - * {@inheritdoc} - */ public function cleanup(AuthorizationChallenge $authorizationChallenge) { $checkUrl = $this->extractor->getCheckUrl($authorizationChallenge); diff --git a/src/Core/Challenge/WaitingValidator.php b/src/Core/Challenge/WaitingValidator.php index b2802096..7c6474c2 100644 --- a/src/Core/Challenge/WaitingValidator.php +++ b/src/Core/Challenge/WaitingValidator.php @@ -33,17 +33,11 @@ public function __construct(ValidatorInterface $validator, int $timeout = 180) $this->timeout = $timeout; } - /** - * {@inheritdoc} - */ public function supports(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { return $this->validator->supports($authorizationChallenge, $solver); } - /** - * {@inheritdoc} - */ public function isValid(AuthorizationChallenge $authorizationChallenge, SolverInterface $solver): bool { $limitEndTime = time() + $this->timeout; diff --git a/src/Core/Exception/AcmeCoreClientException.php b/src/Core/Exception/AcmeCoreClientException.php index c452df5f..71a16624 100644 --- a/src/Core/Exception/AcmeCoreClientException.php +++ b/src/Core/Exception/AcmeCoreClientException.php @@ -18,7 +18,7 @@ */ class AcmeCoreClientException extends AcmeCoreException { - public function __construct($message, \Exception $previous = null) + public function __construct($message, ?\Exception $previous = null) { parent::__construct($message, 0, $previous); } diff --git a/src/Core/Exception/AcmeCoreServerException.php b/src/Core/Exception/AcmeCoreServerException.php index 476c3659..f9f88e57 100644 --- a/src/Core/Exception/AcmeCoreServerException.php +++ b/src/Core/Exception/AcmeCoreServerException.php @@ -20,7 +20,7 @@ */ class AcmeCoreServerException extends AcmeCoreException { - public function __construct(RequestInterface $request, $message, \Exception $previous = null) + public function __construct(RequestInterface $request, $message, ?\Exception $previous = null) { parent::__construct($message, $previous ? $previous->getCode() : 0, $previous); } diff --git a/src/Core/Exception/AcmeDnsResolutionException.php b/src/Core/Exception/AcmeDnsResolutionException.php index 8c00aea1..c8340d3f 100644 --- a/src/Core/Exception/AcmeDnsResolutionException.php +++ b/src/Core/Exception/AcmeDnsResolutionException.php @@ -16,7 +16,7 @@ */ class AcmeDnsResolutionException extends AcmeCoreException { - public function __construct($message, \Exception $previous = null) + public function __construct($message, ?\Exception $previous = null) { parent::__construct(null === $message ? 'An exception was thrown during resolution of DNS' : $message, 0, $previous); } diff --git a/src/Core/Exception/Protocol/ChallengeFailedException.php b/src/Core/Exception/Protocol/ChallengeFailedException.php index fb42cdba..dac02348 100644 --- a/src/Core/Exception/Protocol/ChallengeFailedException.php +++ b/src/Core/Exception/Protocol/ChallengeFailedException.php @@ -18,7 +18,7 @@ class ChallengeFailedException extends ProtocolException { private $response; - public function __construct($response, \Exception $previous = null) + public function __construct($response, ?\Exception $previous = null) { parent::__construct( sprintf('Challenge failed (response: %s).', json_encode($response)), diff --git a/src/Core/Exception/Protocol/ChallengeNotSupportedException.php b/src/Core/Exception/Protocol/ChallengeNotSupportedException.php index f60a9bb9..01165824 100644 --- a/src/Core/Exception/Protocol/ChallengeNotSupportedException.php +++ b/src/Core/Exception/Protocol/ChallengeNotSupportedException.php @@ -16,7 +16,7 @@ */ class ChallengeNotSupportedException extends ProtocolException { - public function __construct(\Exception $previous = null) + public function __construct(?\Exception $previous = null) { parent::__construct('This ACME server does not expose supported challenge.', $previous); } diff --git a/src/Core/Exception/Protocol/ChallengeTimedOutException.php b/src/Core/Exception/Protocol/ChallengeTimedOutException.php index ffed7123..aaf0cf0b 100644 --- a/src/Core/Exception/Protocol/ChallengeTimedOutException.php +++ b/src/Core/Exception/Protocol/ChallengeTimedOutException.php @@ -18,7 +18,7 @@ class ChallengeTimedOutException extends ProtocolException { private $response; - public function __construct($response, \Exception $previous = null) + public function __construct($response, ?\Exception $previous = null) { parent::__construct( sprintf('Challenge timed out (response: %s).', json_encode($response)), diff --git a/src/Core/Exception/Server/BadCsrServerException.php b/src/Core/Exception/Server/BadCsrServerException.php index 333e19ea..b3a6a035 100644 --- a/src/Core/Exception/Server/BadCsrServerException.php +++ b/src/Core/Exception/Server/BadCsrServerException.php @@ -19,7 +19,7 @@ */ class BadCsrServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/BadNonceServerException.php b/src/Core/Exception/Server/BadNonceServerException.php index a731ce34..272d276b 100644 --- a/src/Core/Exception/Server/BadNonceServerException.php +++ b/src/Core/Exception/Server/BadNonceServerException.php @@ -19,7 +19,7 @@ */ class BadNonceServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/CaaServerException.php b/src/Core/Exception/Server/CaaServerException.php index 5def4239..abde3db7 100644 --- a/src/Core/Exception/Server/CaaServerException.php +++ b/src/Core/Exception/Server/CaaServerException.php @@ -19,7 +19,7 @@ */ class CaaServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/ConnectionServerException.php b/src/Core/Exception/Server/ConnectionServerException.php index b53e0d9f..841368f7 100644 --- a/src/Core/Exception/Server/ConnectionServerException.php +++ b/src/Core/Exception/Server/ConnectionServerException.php @@ -19,7 +19,7 @@ */ class ConnectionServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/DnsServerException.php b/src/Core/Exception/Server/DnsServerException.php index 16d1b999..05b1cb78 100644 --- a/src/Core/Exception/Server/DnsServerException.php +++ b/src/Core/Exception/Server/DnsServerException.php @@ -19,7 +19,7 @@ */ class DnsServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/IncorrectResponseServerException.php b/src/Core/Exception/Server/IncorrectResponseServerException.php index e6d30612..f528e625 100644 --- a/src/Core/Exception/Server/IncorrectResponseServerException.php +++ b/src/Core/Exception/Server/IncorrectResponseServerException.php @@ -19,7 +19,7 @@ */ class IncorrectResponseServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/InternalServerException.php b/src/Core/Exception/Server/InternalServerException.php index 299c3caa..bfaa9097 100644 --- a/src/Core/Exception/Server/InternalServerException.php +++ b/src/Core/Exception/Server/InternalServerException.php @@ -19,7 +19,7 @@ */ class InternalServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/InvalidContactServerException.php b/src/Core/Exception/Server/InvalidContactServerException.php index 4a736be6..eadb7d3b 100644 --- a/src/Core/Exception/Server/InvalidContactServerException.php +++ b/src/Core/Exception/Server/InvalidContactServerException.php @@ -19,7 +19,7 @@ */ class InvalidContactServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/InvalidEmailServerException.php b/src/Core/Exception/Server/InvalidEmailServerException.php index f1875187..bef0fa74 100644 --- a/src/Core/Exception/Server/InvalidEmailServerException.php +++ b/src/Core/Exception/Server/InvalidEmailServerException.php @@ -19,7 +19,7 @@ */ class InvalidEmailServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/MalformedServerException.php b/src/Core/Exception/Server/MalformedServerException.php index d6a57a08..5358bf54 100644 --- a/src/Core/Exception/Server/MalformedServerException.php +++ b/src/Core/Exception/Server/MalformedServerException.php @@ -19,7 +19,7 @@ */ class MalformedServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/OrderNotReadyServerException.php b/src/Core/Exception/Server/OrderNotReadyServerException.php index 6e6e88c4..9c584284 100644 --- a/src/Core/Exception/Server/OrderNotReadyServerException.php +++ b/src/Core/Exception/Server/OrderNotReadyServerException.php @@ -16,7 +16,7 @@ class OrderNotReadyServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/RateLimitedServerException.php b/src/Core/Exception/Server/RateLimitedServerException.php index 4ff34410..f0190b4f 100644 --- a/src/Core/Exception/Server/RateLimitedServerException.php +++ b/src/Core/Exception/Server/RateLimitedServerException.php @@ -19,7 +19,7 @@ */ class RateLimitedServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/RejectedIdentifierServerException.php b/src/Core/Exception/Server/RejectedIdentifierServerException.php index 7a0d53cd..fa4e1dde 100644 --- a/src/Core/Exception/Server/RejectedIdentifierServerException.php +++ b/src/Core/Exception/Server/RejectedIdentifierServerException.php @@ -19,7 +19,7 @@ */ class RejectedIdentifierServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/TlsServerException.php b/src/Core/Exception/Server/TlsServerException.php index b7d04ed1..45c50d21 100644 --- a/src/Core/Exception/Server/TlsServerException.php +++ b/src/Core/Exception/Server/TlsServerException.php @@ -19,7 +19,7 @@ */ class TlsServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/UnauthorizedServerException.php b/src/Core/Exception/Server/UnauthorizedServerException.php index 367d09f4..c76343b3 100644 --- a/src/Core/Exception/Server/UnauthorizedServerException.php +++ b/src/Core/Exception/Server/UnauthorizedServerException.php @@ -19,7 +19,7 @@ */ class UnauthorizedServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/UnknownHostServerException.php b/src/Core/Exception/Server/UnknownHostServerException.php index 33628c0c..671f366c 100644 --- a/src/Core/Exception/Server/UnknownHostServerException.php +++ b/src/Core/Exception/Server/UnknownHostServerException.php @@ -19,7 +19,7 @@ */ class UnknownHostServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/UnsupportedContactServerException.php b/src/Core/Exception/Server/UnsupportedContactServerException.php index 44c10e7c..e34b4d5e 100644 --- a/src/Core/Exception/Server/UnsupportedContactServerException.php +++ b/src/Core/Exception/Server/UnsupportedContactServerException.php @@ -19,7 +19,7 @@ */ class UnsupportedContactServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/UnsupportedIdentifierServerException.php b/src/Core/Exception/Server/UnsupportedIdentifierServerException.php index cdd189cb..e97db3c4 100644 --- a/src/Core/Exception/Server/UnsupportedIdentifierServerException.php +++ b/src/Core/Exception/Server/UnsupportedIdentifierServerException.php @@ -19,7 +19,7 @@ */ class UnsupportedIdentifierServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Exception/Server/UserActionRequiredServerException.php b/src/Core/Exception/Server/UserActionRequiredServerException.php index b255970b..a457d844 100644 --- a/src/Core/Exception/Server/UserActionRequiredServerException.php +++ b/src/Core/Exception/Server/UserActionRequiredServerException.php @@ -19,7 +19,7 @@ */ class UserActionRequiredServerException extends AcmeCoreServerException { - public function __construct(RequestInterface $request, string $detail, \Exception $previous = null) + public function __construct(RequestInterface $request, string $detail, ?\Exception $previous = null) { parent::__construct( $request, diff --git a/src/Core/Filesystem/Adapter/FlysystemFtpFactory.php b/src/Core/Filesystem/Adapter/FlysystemFtpFactory.php index 7faf42c0..e43b7586 100644 --- a/src/Core/Filesystem/Adapter/FlysystemFtpFactory.php +++ b/src/Core/Filesystem/Adapter/FlysystemFtpFactory.php @@ -18,9 +18,6 @@ class FlysystemFtpFactory implements FilesystemFactoryInterface { - /** - * {@inheritdoc} - */ public function create(array $config): FilesystemInterface { return new FlysystemAdapter(new Filesystem(new Ftp($config))); diff --git a/src/Core/Filesystem/Adapter/FlysystemLocalFactory.php b/src/Core/Filesystem/Adapter/FlysystemLocalFactory.php index a67e874b..6e21ff17 100644 --- a/src/Core/Filesystem/Adapter/FlysystemLocalFactory.php +++ b/src/Core/Filesystem/Adapter/FlysystemLocalFactory.php @@ -19,9 +19,6 @@ class FlysystemLocalFactory implements FilesystemFactoryInterface { - /** - * {@inheritdoc} - */ public function create(array $config): FilesystemInterface { Assert::keyExists($config, 'root', 'create::$config expected an array with the key %s.'); diff --git a/src/Core/Filesystem/Adapter/FlysystemSftpFactory.php b/src/Core/Filesystem/Adapter/FlysystemSftpFactory.php index fd6dd15f..0342adc5 100644 --- a/src/Core/Filesystem/Adapter/FlysystemSftpFactory.php +++ b/src/Core/Filesystem/Adapter/FlysystemSftpFactory.php @@ -18,9 +18,6 @@ class FlysystemSftpFactory implements FilesystemFactoryInterface { - /** - * {@inheritdoc} - */ public function create(array $config): FilesystemInterface { return new FlysystemAdapter(new Filesystem(new SftpAdapter($config))); diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index 6fac9553..127b1811 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -157,8 +157,6 @@ public function signJwkPayload(string $endpoint, $payload = null, bool $withNonc /** * Generates an External Account Binding payload signed with JWS. - * - * @param string|array|null $payload */ public function createExternalAccountPayload(ExternalAccount $externalAccount, string $url): array { @@ -189,11 +187,11 @@ public function createExternalAccountPayload(ExternalAccount $externalAccount, s * Send a request encoded in the format defined by the ACME protocol * and its content (optionally parsed as JSON). * + * @return array|string Array of parsed JSON if $returnJson = true, string otherwise + * * @throws AcmeCoreClientException when an error occured during response parsing * @throws ExpectedJsonException when $returnJson = true and the response is not valid JSON * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code - * - * @return array|string Array of parsed JSON if $returnJson = true, string otherwise */ public function request(string $method, string $endpoint, array $data = [], bool $returnJson = true) { @@ -294,7 +292,7 @@ public function getBase64Encoder(): Base64SafeEncoder /** * Sign the given Payload. */ - private function signPayload(array $protected, array $payload = null): array + private function signPayload(array $protected, ?array $payload = null): array { if (!isset($protected['alg'])) { throw new \InvalidArgumentException('The property "alg" is required in the protected array'); @@ -329,7 +327,7 @@ private function signPayload(array $protected, array $payload = null): array private function createRequest($method, $endpoint, $data, $acceptJson) { $request = new Request($method, $endpoint); - + if ($acceptJson) { $request = $request->withHeader('Accept', 'application/json,application/jose+json,'); } else { @@ -390,7 +388,7 @@ private function getAlg(): string return 'ES512'; } - // no break to let the default case + // no break to let the default case default: throw new AcmeCoreClientException('Private key type is not supported'); } diff --git a/src/Core/Http/ServerErrorHandler.php b/src/Core/Http/ServerErrorHandler.php index b161fa70..b9f5301c 100644 --- a/src/Core/Http/ServerErrorHandler.php +++ b/src/Core/Http/ServerErrorHandler.php @@ -93,7 +93,7 @@ public static function getResponseBodySummary(ResponseInterface $response): stri public function createAcmeExceptionForResponse( RequestInterface $request, ResponseInterface $response, - \Exception $previous = null + ?\Exception $previous = null ): AcmeCoreServerException { $body = Utils::copyToString($response->getBody()); @@ -127,7 +127,7 @@ public function createAcmeExceptionForResponse( private function createDefaultExceptionForResponse( RequestInterface $request, ResponseInterface $response, - \Exception $previous = null + ?\Exception $previous = null ): AcmeCoreServerException { return new AcmeCoreServerException( $request, diff --git a/src/Core/Protocol/CertificateOrder.php b/src/Core/Protocol/CertificateOrder.php index 2f7e11ec..48698fea 100644 --- a/src/Core/Protocol/CertificateOrder.php +++ b/src/Core/Protocol/CertificateOrder.php @@ -29,7 +29,7 @@ class CertificateOrder /** @var string */ private $status; - public function __construct(array $authorizationsChallenges, string $orderEndpoint = null, string $status = null) + public function __construct(array $authorizationsChallenges, ?string $orderEndpoint = null, ?string $status = null) { foreach ($authorizationsChallenges as &$authorizationChallenges) { foreach ($authorizationChallenges as &$authorizationChallenge) { diff --git a/src/Core/Util/JsonDecoder.php b/src/Core/Util/JsonDecoder.php index 9b357ac6..a783641c 100644 --- a/src/Core/Util/JsonDecoder.php +++ b/src/Core/Util/JsonDecoder.php @@ -32,8 +32,6 @@ class JsonDecoder * * @throws \InvalidArgumentException if the JSON cannot be decoded * - * @return mixed - * * @see http://www.php.net/manual/en/function.json-decode.php */ public static function decode(string $json, bool $assoc = false, int $depth = 512, int $options = 0) diff --git a/src/Ssl/Certificate.php b/src/Ssl/Certificate.php index 12465bd8..3d12ab5c 100644 --- a/src/Ssl/Certificate.php +++ b/src/Ssl/Certificate.php @@ -27,7 +27,7 @@ class Certificate /** @var Certificate */ private $issuerCertificate; - public function __construct(string $certificatePEM, self $issuerCertificate = null) + public function __construct(string $certificatePEM, ?self $issuerCertificate = null) { Assert::stringNotEmpty($certificatePEM, __CLASS__.'::$certificatePEM should not be an empty string. Got %s'); diff --git a/src/Ssl/DistinguishedName.php b/src/Ssl/DistinguishedName.php index 1136bb61..50de2b61 100644 --- a/src/Ssl/DistinguishedName.php +++ b/src/Ssl/DistinguishedName.php @@ -46,12 +46,12 @@ class DistinguishedName public function __construct( string $commonName, - string $countryName = null, - string $stateOrProvinceName = null, - string $localityName = null, - string $organizationName = null, - string $organizationalUnitName = null, - string $emailAddress = null, + ?string $countryName = null, + ?string $stateOrProvinceName = null, + ?string $localityName = null, + ?string $organizationName = null, + ?string $organizationalUnitName = null, + ?string $emailAddress = null, array $subjectAlternativeNames = [] ) { Assert::stringNotEmpty($commonName, __CLASS__.'::$commonName expected a non empty string. Got: %s'); diff --git a/src/Ssl/Generator/KeyPairGenerator.php b/src/Ssl/Generator/KeyPairGenerator.php index 67d4d56b..328be8bb 100644 --- a/src/Ssl/Generator/KeyPairGenerator.php +++ b/src/Ssl/Generator/KeyPairGenerator.php @@ -29,7 +29,7 @@ class KeyPairGenerator { private $generator; - public function __construct(PrivateKeyGeneratorInterface $generator = null) + public function __construct(?PrivateKeyGeneratorInterface $generator = null) { $this->generator = $generator ?: new ChainPrivateKeyGenerator( [ @@ -46,7 +46,7 @@ public function __construct(PrivateKeyGeneratorInterface $generator = null) * * @throws KeyPairGenerationException when OpenSSL failed to generate keys */ - public function generateKeyPair(KeyOption $keyOption = null): KeyPair + public function generateKeyPair(?KeyOption $keyOption = null): KeyPair { if (null === $keyOption) { $keyOption = new RsaKeyOption(); diff --git a/src/Ssl/ParsedCertificate.php b/src/Ssl/ParsedCertificate.php index f6adea4c..ef8c21a0 100644 --- a/src/Ssl/ParsedCertificate.php +++ b/src/Ssl/ParsedCertificate.php @@ -44,20 +44,14 @@ class ParsedCertificate /** @var array */ private $subjectAlternativeNames; - /** - * @param string $issuer - * @param \DateTime $validFrom - * @param \DateTime $validTo - * @param string $serialNumber - */ public function __construct( Certificate $source, string $subject, - string $issuer = null, + ?string $issuer = null, bool $selfSigned = true, - \DateTime $validFrom = null, - \DateTime $validTo = null, - string $serialNumber = null, + ?\DateTime $validFrom = null, + ?\DateTime $validTo = null, + ?string $serialNumber = null, array $subjectAlternativeNames = [] ) { Assert::stringNotEmpty($subject, __CLASS__.'::$subject expected a non empty string. Got: %s'); diff --git a/src/Ssl/PrivateKey.php b/src/Ssl/PrivateKey.php index c7080035..b4508089 100644 --- a/src/Ssl/PrivateKey.php +++ b/src/Ssl/PrivateKey.php @@ -21,9 +21,6 @@ */ class PrivateKey extends Key { - /** - * {@inheritdoc} - */ public function getResource() { if (!$resource = openssl_pkey_get_private($this->keyPEM)) { diff --git a/src/Ssl/PublicKey.php b/src/Ssl/PublicKey.php index f0a511d2..aa570c62 100644 --- a/src/Ssl/PublicKey.php +++ b/src/Ssl/PublicKey.php @@ -21,9 +21,6 @@ */ class PublicKey extends Key { - /** - * {@inheritdoc} - */ public function getResource() { if (!$resource = openssl_pkey_get_public($this->keyPEM)) { diff --git a/tests/Cli/Repository/RepositoryTest.php b/tests/Cli/Repository/RepositoryTest.php index 8fc69de8..a5074234 100644 --- a/tests/Cli/Repository/RepositoryTest.php +++ b/tests/Cli/Repository/RepositoryTest.php @@ -24,7 +24,6 @@ use League\Flysystem\Memory\MemoryAdapter; use PHPUnit\Framework\TestCase; use Symfony\Component\Serializer\Encoder\JsonEncoder; -use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Serializer; diff --git a/tests/Ssl/CertificateTest.php b/tests/Ssl/CertificateTest.php index 24b0a4c2..db35a756 100644 --- a/tests/Ssl/CertificateTest.php +++ b/tests/Ssl/CertificateTest.php @@ -22,7 +22,7 @@ class CertificateTest extends TestCase public function testGetPublicKeyReturnsAPublicKey() { $certificate = new Certificate( - ' + ' -----BEGIN CERTIFICATE----- MIIFkTCCBHmgAwIBAgITAP/g3ErooCmPSlx2kAVx9abKkTANBgkqhkiG9w0BAQsF ADAfMR0wGwYDVQQDExRoYXBweSBoYWNrZXIgZmFrZSBDQTAeFw0xNjAzMjUyMjI3 From d3800f19f387bbe73e337ba5fb3ad583ce1ecbed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 14 Aug 2024 14:43:00 +0200 Subject: [PATCH 069/121] Sync all version in composer.json + update some lowest constrainsts --- bin/acme | 4 ++-- composer.json | 22 +++++++++++----------- src/Cli/Application.php | 2 +- src/Core/composer.json | 2 +- src/Ssl/composer.json | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/bin/acme b/bin/acme index b9c0dd24..d164e5c9 100755 --- a/bin/acme +++ b/bin/acme @@ -10,8 +10,8 @@ * file that was distributed with this source code. */ -if (version_compare('5.5.0', PHP_VERSION, '>')) { - echo 'This version of Acme PHP requires PHP 5.5.0.'.PHP_EOL; +if (version_compare('8.1.0', PHP_VERSION, '>')) { + echo 'This version of Acme PHP requires PHP 8.1.0.'.PHP_EOL; exit; } diff --git a/composer.json b/composer.json index 6bb0b415..d52d4394 100644 --- a/composer.json +++ b/composer.json @@ -49,26 +49,26 @@ "lcobucci/jwt": "^3.3|^4.0", "league/flysystem": "^1.0.19", "league/flysystem-memory": "^1.0", - "league/flysystem-sftp": "^1.0.7", + "league/flysystem-sftp": "^1.0.22", "monolog/monolog": "^1.19|^2.0", "padraic/phar-updater": "^1.0", "psr/container": "^1.0", "psr/http-message": "^1.0", "psr/log": "^1.0", - "symfony/config": "^5.4 || ^6.4", - "symfony/console": "^5.4 || ^6.4", - "symfony/dependency-injection": "^5.4 || ^6.4", - "symfony/filesystem": "^5.4 || ^6.4", - "symfony/serializer": "^5.4 || ^6.4", - "symfony/yaml": "^5.4 || ^6.4", + "symfony/config": "^5.4.12 || ^6.4", + "symfony/console": "^5.4.12 || ^6.4", + "symfony/dependency-injection": "^5.4.12 || ^6.4", + "symfony/filesystem": "^5.4.12 || ^6.4", + "symfony/serializer": "^5.4.12 || ^6.4", + "symfony/yaml": "^5.4.12 || ^6.4", "webmozart/assert": "^1.0", "webmozart/path-util": "^2.3" }, "require-dev": { - "symfony/finder": "^5.4 || ^6.4", - "symfony/phpunit-bridge": "^5.4 || ^6.4", - "symfony/property-access": "^5.4 || ^6.4", - "symfony/var-dumper": "^5.4 || ^6.4" + "symfony/finder": "^5.4.12 || ^6.4", + "symfony/phpunit-bridge": "^5.4.12 || ^6.4", + "symfony/property-access": "^5.4.12 || ^6.4", + "symfony/var-dumper": "^5.4.12 || ^6.4" }, "autoload": { "psr-4": { diff --git a/src/Cli/Application.php b/src/Cli/Application.php index 19bb14db..bdb2cf42 100644 --- a/src/Cli/Application.php +++ b/src/Cli/Application.php @@ -36,7 +36,7 @@ class Application extends BaseApplication */ public function __construct() { - parent::__construct('Acme PHP - Let\'s Encrypt/ZeroSSL client', '2.0.0'); + parent::__construct('Acme PHP - Let\'s Encrypt/ZeroSSL client', '3.0.0'); } /** diff --git a/src/Core/composer.json b/src/Core/composer.json index 2e82021a..4955887d 100644 --- a/src/Core/composer.json +++ b/src/Core/composer.json @@ -27,7 +27,7 @@ } ], "require": { - "php": ">=7.2.5", + "php": ">=8.1", "ext-hash": "*", "ext-json": "*", "ext-openssl": "*", diff --git a/src/Ssl/composer.json b/src/Ssl/composer.json index f097d057..da51c8ec 100644 --- a/src/Ssl/composer.json +++ b/src/Ssl/composer.json @@ -32,7 +32,7 @@ } }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "ext-hash": "*", "ext-openssl": "*", "lib-openssl": ">=0.9.8", From 9db6e991c80fd8206ee4ece8e7c3f437116ba497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 14 Aug 2024 15:15:34 +0200 Subject: [PATCH 070/121] Update to flysystem v3 --- composer.json | 6 ++-- src/Cli/Action/FilesystemAction.php | 12 ++++--- src/Cli/Command/StatusCommand.php | 4 +-- src/Cli/Repository/Repository.php | 12 +++---- src/Cli/Resources/services.xml | 2 +- .../Filesystem/Adapter/FlysystemAdapter.php | 32 +++++++++++-------- .../Adapter/FlysystemSftpFactory.php | 17 ++++++++-- src/Core/Filesystem/Adapter/NullAdapter.php | 4 +-- tests/Cli/Repository/RepositoryTest.php | 4 +-- tests/Cli/SftpNginxProxyApplicationTest.php | 25 ++++++++------- 10 files changed, 68 insertions(+), 50 deletions(-) diff --git a/composer.json b/composer.json index d52d4394..b0d8e57e 100644 --- a/composer.json +++ b/composer.json @@ -47,9 +47,9 @@ "guzzlehttp/guzzle": "^7.2", "guzzlehttp/psr7": "^1.0", "lcobucci/jwt": "^3.3|^4.0", - "league/flysystem": "^1.0.19", - "league/flysystem-memory": "^1.0", - "league/flysystem-sftp": "^1.0.22", + "league/flysystem": "^3.10", + "league/flysystem-memory": "^3.10", + "league/flysystem-sftp-v3": "^3.10", "monolog/monolog": "^1.19|^2.0", "padraic/phar-updater": "^1.0", "psr/container": "^1.0", diff --git a/src/Cli/Action/FilesystemAction.php b/src/Cli/Action/FilesystemAction.php index 258b7030..0a8ab45e 100644 --- a/src/Cli/Action/FilesystemAction.php +++ b/src/Cli/Action/FilesystemAction.php @@ -14,7 +14,8 @@ use AcmePhp\Core\Filesystem\FilesystemFactoryInterface; use AcmePhp\Core\Filesystem\FilesystemInterface; use AcmePhp\Ssl\CertificateResponse; -use League\Flysystem\FilesystemInterface as FlysystemFilesystemInterface; +use League\Flysystem\FileAttributes; +use League\Flysystem\FilesystemOperator; use Psr\Container\ContainerInterface; use Symfony\Component\DependencyInjection\ServiceLocator; @@ -24,7 +25,7 @@ class FilesystemAction extends AbstractAction { /** - * @var FlysystemFilesystemInterface + * @var FilesystemOperator */ protected $storage; @@ -33,7 +34,7 @@ class FilesystemAction extends AbstractAction */ protected $filesystemFactoryLocator; - public function __construct(FlysystemFilesystemInterface $storage, ?ContainerInterface $locator = null) + public function __construct(FilesystemOperator $storage, ?ContainerInterface $locator = null) { $this->storage = $storage; $this->filesystemFactoryLocator = $locator ?: new ServiceLocator([]); @@ -48,12 +49,13 @@ public function handle(array $config, CertificateResponse $response) $filesystem = $factory->create($config); $files = $this->storage->listContents('.', true); + /** @var FileAttributes $file */ foreach ($files as $file) { - if (0 === strpos($file['basename'], '.')) { + if (str_starts_with(basename($file->path()), '.')) { continue; } - $this->mirror($file['type'], $file['path'], $filesystem); + $this->mirror($file->type(), $file->path(), $filesystem); } } diff --git a/src/Cli/Command/StatusCommand.php b/src/Cli/Command/StatusCommand.php index a54997b3..0838e0c4 100644 --- a/src/Cli/Command/StatusCommand.php +++ b/src/Cli/Command/StatusCommand.php @@ -12,7 +12,7 @@ namespace AcmePhp\Cli\Command; use AcmePhp\Ssl\Parser\CertificateParser; -use League\Flysystem\FilesystemInterface; +use League\Flysystem\FilesystemOperator; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -39,7 +39,7 @@ protected function execute(InputInterface $input, OutputInterface $output) { $repository = $this->getRepository(); - /** @var FilesystemInterface $master */ + /** @var FilesystemOperator $master */ $master = $this->getContainer()->get('repository.storage'); /** @var CertificateParser $certificateParser */ diff --git a/src/Cli/Repository/Repository.php b/src/Cli/Repository/Repository.php index fd80e8d2..6bb6bfb3 100644 --- a/src/Cli/Repository/Repository.php +++ b/src/Cli/Repository/Repository.php @@ -21,7 +21,7 @@ use AcmePhp\Ssl\KeyPair; use AcmePhp\Ssl\PrivateKey; use AcmePhp\Ssl\PublicKey; -use League\Flysystem\FilesystemInterface; +use League\Flysystem\FilesystemOperator; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\SerializerInterface; @@ -50,11 +50,11 @@ class Repository implements RepositoryInterface private $serializer; /** - * @var FilesystemInterface + * @var FilesystemOperator */ private $storage; - public function __construct(SerializerInterface $serializer, FilesystemInterface $storage) + public function __construct(SerializerInterface $serializer, FilesystemOperator $storage) { $this->serializer = $serializer; $this->storage = $storage; @@ -291,11 +291,7 @@ public function loadCertificateOrder(array $domains): CertificateOrder public function save(string $path, string $content, string $visibility = self::VISIBILITY_PRIVATE) { - if (!$this->storage->has($path)) { - $this->storage->write($path, $content); - } else { - $this->storage->update($path, $content); - } + $this->storage->write($path, $content); $this->storage->setVisibility($path, $visibility); } diff --git a/src/Cli/Resources/services.xml b/src/Cli/Resources/services.xml index d347e0ca..d939d50b 100644 --- a/src/Cli/Resources/services.xml +++ b/src/Cli/Resources/services.xml @@ -71,7 +71,7 @@ - + %app.storage_directory% diff --git a/src/Core/Filesystem/Adapter/FlysystemAdapter.php b/src/Core/Filesystem/Adapter/FlysystemAdapter.php index ad5cb944..f676d4f5 100644 --- a/src/Core/Filesystem/Adapter/FlysystemAdapter.php +++ b/src/Core/Filesystem/Adapter/FlysystemAdapter.php @@ -12,7 +12,8 @@ namespace AcmePhp\Core\Filesystem\Adapter; use AcmePhp\Core\Filesystem\FilesystemInterface; -use League\Flysystem\FilesystemInterface as FlysystemFilesystemInterface; +use League\Flysystem\FilesystemException; +use League\Flysystem\FilesystemOperator as FlysystemFilesystemInterface; class FlysystemAdapter implements FilesystemInterface { @@ -28,39 +29,42 @@ public function __construct(FlysystemFilesystemInterface $filesystem) public function write(string $path, string $content) { - $isOnRemote = $this->filesystem->has($path); - if ($isOnRemote && !$this->filesystem->update($path, $content)) { - throw $this->createRuntimeException($path, 'updated'); - } - if (!$isOnRemote && !$this->filesystem->write($path, $content)) { - throw $this->createRuntimeException($path, 'created'); + try { + $this->filesystem->write($path, $content); + } catch (FilesystemException $e) { + throw $this->createRuntimeException($path, 'created', $e); } } public function delete(string $path) { $isOnRemote = $this->filesystem->has($path); - if ($isOnRemote && !$this->filesystem->delete($path)) { - throw $this->createRuntimeException($path, 'delete'); + try { + if ($isOnRemote) { + $this->filesystem->delete($path); + } + } catch (FilesystemException $e) { + throw $this->createRuntimeException($path, 'deleted', $e); } } public function createDir(string $path) { - $isOnRemote = $this->filesystem->has($path); - if (!$isOnRemote && !$this->filesystem->createDir($path)) { - throw $this->createRuntimeException($path, 'created'); + try { + $this->filesystem->createDirectory($path); + } catch (FilesystemException $e) { + throw $this->createRuntimeException($path, 'created', $e); } } - private function createRuntimeException(string $path, string $action): \RuntimeException + private function createRuntimeException(string $path, string $action, FilesystemException $e): \RuntimeException { return new \RuntimeException( sprintf( 'File %s could not be %s because: %s', $path, $action, - error_get_last() + $e->getMessage(), ) ); } diff --git a/src/Core/Filesystem/Adapter/FlysystemSftpFactory.php b/src/Core/Filesystem/Adapter/FlysystemSftpFactory.php index 0342adc5..ffd38828 100644 --- a/src/Core/Filesystem/Adapter/FlysystemSftpFactory.php +++ b/src/Core/Filesystem/Adapter/FlysystemSftpFactory.php @@ -14,12 +14,25 @@ use AcmePhp\Core\Filesystem\FilesystemFactoryInterface; use AcmePhp\Core\Filesystem\FilesystemInterface; use League\Flysystem\Filesystem; -use League\Flysystem\Sftp\SftpAdapter; +use League\Flysystem\PhpseclibV3\SftpAdapter; +use League\Flysystem\PhpseclibV3\SftpConnectionProvider; class FlysystemSftpFactory implements FilesystemFactoryInterface { public function create(array $config): FilesystemInterface { - return new FlysystemAdapter(new Filesystem(new SftpAdapter($config))); + return new FlysystemAdapter( + new Filesystem( + new SftpAdapter( + new SftpConnectionProvider( + $config['host'], + $config['username'], + password: $config['password'] ?? null, + port: $config['port'] ?? 22, + ), + $config['root'] ?? '/', + ) + ) + ); } } diff --git a/src/Core/Filesystem/Adapter/NullAdapter.php b/src/Core/Filesystem/Adapter/NullAdapter.php index 169221aa..3f22d45f 100644 --- a/src/Core/Filesystem/Adapter/NullAdapter.php +++ b/src/Core/Filesystem/Adapter/NullAdapter.php @@ -12,12 +12,12 @@ namespace AcmePhp\Core\Filesystem\Adapter; use League\Flysystem\Filesystem; -use League\Flysystem\Memory\MemoryAdapter; +use League\Flysystem\InMemory\InMemoryFilesystemAdapter; class NullAdapter extends FlysystemAdapter { public function __construct() { - parent::__construct(new Filesystem(new MemoryAdapter())); + parent::__construct(new Filesystem(new InMemoryFilesystemAdapter())); } } diff --git a/tests/Cli/Repository/RepositoryTest.php b/tests/Cli/Repository/RepositoryTest.php index a5074234..6ce933d3 100644 --- a/tests/Cli/Repository/RepositoryTest.php +++ b/tests/Cli/Repository/RepositoryTest.php @@ -21,7 +21,7 @@ use AcmePhp\Ssl\PrivateKey; use AcmePhp\Ssl\PublicKey; use League\Flysystem\Filesystem; -use League\Flysystem\Memory\MemoryAdapter; +use League\Flysystem\InMemory\InMemoryFilesystemAdapter; use PHPUnit\Framework\TestCase; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; @@ -51,7 +51,7 @@ public function setUp(): void [new PemEncoder(), new JsonEncoder()] ); - $this->storage = new Filesystem(new MemoryAdapter()); + $this->storage = new Filesystem(new InMemoryFilesystemAdapter()); $this->repository = new Repository($this->serializer, $this->storage); } diff --git a/tests/Cli/SftpNginxProxyApplicationTest.php b/tests/Cli/SftpNginxProxyApplicationTest.php index a2754462..6a6b8154 100644 --- a/tests/Cli/SftpNginxProxyApplicationTest.php +++ b/tests/Cli/SftpNginxProxyApplicationTest.php @@ -12,7 +12,8 @@ namespace Tests\AcmePhp\Cli; use League\Flysystem\Filesystem; -use League\Flysystem\Sftp\SftpAdapter; +use League\Flysystem\PhpseclibV3\SftpAdapter; +use League\Flysystem\PhpseclibV3\SftpConnectionProvider; class SftpNginxProxyApplicationTest extends AbstractApplicationTest { @@ -32,18 +33,20 @@ protected function getConfigDir(): string public function testFullProcess() { - $sftpFilesystem = new Filesystem(new SftpAdapter([ - 'host' => 'localhost', - 'port' => 8022, - 'username' => 'acmephp', - 'password' => 'acmephp', - 'root' => '/share', - ])); + $sftpFilesystem = new Filesystem(new SftpAdapter( + new SftpConnectionProvider( + host: 'localhost', + port: 8022, + username: 'acmephp', + password: 'acmephp', + ), + '/share', + )); // Remove any old version of the files - $sftpFilesystem->has('private') && $sftpFilesystem->deleteDir('private'); - $sftpFilesystem->has('certs') && $sftpFilesystem->deleteDir('certs'); - $sftpFilesystem->has('nginxproxy') && $sftpFilesystem->deleteDir('nginxproxy'); + $sftpFilesystem->has('private') && $sftpFilesystem->deleteDirectory('private'); + $sftpFilesystem->has('certs') && $sftpFilesystem->deleteDirectory('certs'); + $sftpFilesystem->has('nginxproxy') && $sftpFilesystem->deleteDirectory('nginxproxy'); // Run the original full process parent::testFullProcess(); From 338b0865017e1560ff04306db1695ecc6e3e4ff9 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Mon, 18 Mar 2024 08:51:01 +0100 Subject: [PATCH 071/121] fix: update self-update command and phar generation config --- box.json.dist | 10 +- composer.json | 2 +- src/Cli/Application.php | 7 +- src/Cli/Command/SelfUpdateCommand.php | 335 -------------------------- 4 files changed, 8 insertions(+), 346 deletions(-) delete mode 100644 src/Cli/Command/SelfUpdateCommand.php diff --git a/box.json.dist b/box.json.dist index a3a93ac7..f477b50c 100644 --- a/box.json.dist +++ b/box.json.dist @@ -3,7 +3,7 @@ "files": [ "LICENSE" ], "finder": [ { - "name": "*.php", + "name": "{\\.(php|bash|fish|zsh)}", "exclude": [ "Tester", "Tests", @@ -16,12 +16,8 @@ "in": "vendor" } ], - "compactors": [ "KevinGH\\Box\\Compactor\\Php" ], - "check-requirements": false, + "git": "git", "compression": "GZ", "git-version": "package_version", - "main": "bin/acme", - "output": "build/acmephp.phar", - "algorithm": "OPENSSL", - "key": "../private.key" + "output": "build/acmephp.phar" } diff --git a/composer.json b/composer.json index d52d4394..6e20d12a 100644 --- a/composer.json +++ b/composer.json @@ -44,6 +44,7 @@ "alibabacloud/cdn": "^1.7", "alibabacloud/wafopenapi": "^1.7", "aws/aws-sdk-php": "^3.38", + "consolidation/self-update": "^2.2", "guzzlehttp/guzzle": "^7.2", "guzzlehttp/psr7": "^1.0", "lcobucci/jwt": "^3.3|^4.0", @@ -51,7 +52,6 @@ "league/flysystem-memory": "^1.0", "league/flysystem-sftp": "^1.0.22", "monolog/monolog": "^1.19|^2.0", - "padraic/phar-updater": "^1.0", "psr/container": "^1.0", "psr/http-message": "^1.0", "psr/log": "^1.0", diff --git a/src/Cli/Application.php b/src/Cli/Application.php index 2dc1171f..40ad7724 100644 --- a/src/Cli/Application.php +++ b/src/Cli/Application.php @@ -14,8 +14,8 @@ use AcmePhp\Cli\Command\Helper\DistinguishedNameHelper; use AcmePhp\Cli\Command\RevokeCommand; use AcmePhp\Cli\Command\RunCommand; -use AcmePhp\Cli\Command\SelfUpdateCommand; use AcmePhp\Cli\Command\StatusCommand; +use SelfUpdate\SelfUpdateCommand; use Symfony\Component\Console\Application as BaseApplication; use Symfony\Component\Console\Helper\HelperSet; use Webmozart\PathUtil\Path; @@ -33,7 +33,8 @@ class Application extends BaseApplication public function __construct() { - parent::__construct('Acme PHP - Let\'s Encrypt/ZeroSSL client', '3.0.0'); + // This is replaced by humbug/box with a string that looks like this: x.y.z@tag + parent::__construct('Acme PHP - Let\'s Encrypt/ZeroSSL client', '@git@'); } protected function getDefaultCommands(): array @@ -42,7 +43,7 @@ protected function getDefaultCommands(): array new RunCommand(), new RevokeCommand(), new StatusCommand(), - new SelfUpdateCommand(), + new SelfUpdateCommand($this->getName(), explode('@', $this->getVersion())[0], 'acmephp/acmephp'), ]); } diff --git a/src/Cli/Command/SelfUpdateCommand.php b/src/Cli/Command/SelfUpdateCommand.php deleted file mode 100644 index 8e6024d9..00000000 --- a/src/Cli/Command/SelfUpdateCommand.php +++ /dev/null @@ -1,335 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\Command; - -use Humbug\SelfUpdate\Strategy\GithubStrategy; -use Humbug\SelfUpdate\Strategy\ShaStrategy; -use Humbug\SelfUpdate\Updater; -use Humbug\SelfUpdate\VersionParser; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * Self-update command. - * - * Heavily inspired from https://github.com/padraic/phar-updater. - * - * @author Titouan Galopin - */ -class SelfUpdateCommand extends Command -{ - public const PACKAGE_NAME = 'acmephp/acmephp'; - public const FILE_NAME = 'acmephp.phar'; - public const VERSION_URL = 'https://acmephp.github.io/downloads/acmephp.version'; - public const PHAR_URL = 'https://acmephp.github.io/downloads/acmephp.phar'; - - /** - * @var string - */ - protected $version; - - /** - * @var OutputInterface - */ - protected $output; - - protected function configure() - { - $this - ->setName('self-update') - ->setDescription('Update acmephp.phar to most recent stable, pre-release or development build.') - ->addOption( - 'dev', - 'd', - InputOption::VALUE_NONE, - 'Update to most recent development build of Acme PHP.' - ) - ->addOption( - 'non-dev', - 'N', - InputOption::VALUE_NONE, - 'Update to most recent non-development (alpha/beta/stable) build of Acme PHP tagged on Github.' - ) - ->addOption( - 'pre', - 'p', - InputOption::VALUE_NONE, - 'Update to most recent pre-release version of Acme PHP (alpha/beta/rc) tagged on Github.' - ) - ->addOption( - 'stable', - 's', - InputOption::VALUE_NONE, - 'Update to most recent stable version tagged on Github.' - ) - ->addOption( - 'rollback', - 'r', - InputOption::VALUE_NONE, - 'Rollback to previous version of Acme PHP if available on filesystem.' - ) - ->addOption( - 'check', - 'c', - InputOption::VALUE_NONE, - 'Checks what updates are available across all possible stability tracks.' - ); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->output = $output; - $this->version = $this->getApplication()->getVersion(); - $parser = new VersionParser(); - - /* - * Check for ancilliary options - */ - if ($input->getOption('rollback')) { - $this->rollback(); - - return 0; - } - - if ($input->getOption('check')) { - $this->printAvailableUpdates(); - - return 0; - } - - /* - * Update to any specified stability option - */ - if ($input->getOption('dev')) { - $this->updateToDevelopmentBuild(); - - return 0; - } - - if ($input->getOption('pre')) { - $this->updateToPreReleaseBuild(); - - return 0; - } - - if ($input->getOption('stable')) { - $this->updateToStableBuild(); - - return 0; - } - - if ($input->getOption('non-dev')) { - $this->updateToMostRecentNonDevRemote(); - - return 0; - } - - /* - * If current build is stable, only update to more recent stable - * versions if available. User may specify otherwise using options. - */ - if ($parser->isStable($this->version)) { - $this->updateToStableBuild(); - - return 0; - } - - /* - * By default, update to most recent remote version regardless - * of stability. - */ - $this->updateToMostRecentNonDevRemote(); - - return 0; - } - - protected function getStableUpdater() - { - $updater = new Updater(); - $updater->setStrategy(Updater::STRATEGY_GITHUB); - - return $this->getGithubReleasesUpdater($updater); - } - - protected function getPreReleaseUpdater() - { - $updater = new Updater(); - $updater->setStrategy(Updater::STRATEGY_GITHUB); - $updater->getStrategy()->setStability(GithubStrategy::UNSTABLE); - - return $this->getGithubReleasesUpdater($updater); - } - - protected function getMostRecentNonDevUpdater() - { - $updater = new Updater(); - $updater->setStrategy(Updater::STRATEGY_GITHUB); - $updater->getStrategy()->setStability(GithubStrategy::ANY); - - return $this->getGithubReleasesUpdater($updater); - } - - protected function getGithubReleasesUpdater(Updater $updater) - { - $updater->getStrategy()->setPackageName(self::PACKAGE_NAME); - $updater->getStrategy()->setPharName(self::FILE_NAME); - $updater->getStrategy()->setCurrentLocalVersion($this->version); - - return $updater; - } - - protected function getDevelopmentUpdater() - { - $updater = new Updater(); - $updater->getStrategy()->setPharUrl(self::PHAR_URL); - $updater->getStrategy()->setVersionUrl(self::VERSION_URL); - - return $updater; - } - - protected function updateToStableBuild() - { - $this->update($this->getStableUpdater()); - } - - protected function updateToPreReleaseBuild() - { - $this->update($this->getPreReleaseUpdater()); - } - - protected function updateToMostRecentNonDevRemote() - { - $this->update($this->getMostRecentNonDevUpdater()); - } - - protected function updateToDevelopmentBuild() - { - $this->update($this->getDevelopmentUpdater()); - } - - protected function update(Updater $updater) - { - $this->output->writeln('Updating...'.PHP_EOL); - - try { - $result = $updater->update(); - - $newVersion = $updater->getNewVersion(); - $oldVersion = $updater->getOldVersion(); - if (40 === \strlen($newVersion)) { - $newVersion = 'dev-'.$newVersion; - } - if (40 === \strlen($oldVersion)) { - $oldVersion = 'dev-'.$oldVersion; - } - - if ($result) { - $this->output->writeln('Acme PHP has been updated.'); - $this->output->writeln(sprintf( - 'Current version is: %s.', - $newVersion - )); - $this->output->writeln(sprintf( - 'Previous version was: %s.', - $oldVersion - )); - } else { - $this->output->writeln('Acme PHP is currently up to date.'); - $this->output->writeln(sprintf( - 'Current version is: %s.', - $oldVersion - )); - } - } catch (\Exception $e) { - $this->output->writeln(sprintf('Error: %s', $e->getMessage())); - } - $this->output->write(PHP_EOL); - $this->output->writeln('You can also select update stability using --dev, --pre (alpha/beta/rc) or --stable.'); - } - - protected function rollback() - { - $updater = new Updater(); - - try { - $result = $updater->rollback(); - if ($result) { - $this->output->writeln('Acme PHP has been rolled back to prior version.'); - } else { - $this->output->writeln('Rollback failed for reasons unknown.'); - } - } catch (\Exception $e) { - $this->output->writeln(sprintf('Error: %s', $e->getMessage())); - } - } - - protected function printAvailableUpdates() - { - $this->printCurrentLocalVersion(); - $this->printCurrentStableVersion(); - $this->printCurrentPreReleaseVersion(); - $this->printCurrentDevVersion(); - $this->output->writeln('You can select update stability using --dev, --pre or --stable when self-updating.'); - } - - protected function printCurrentLocalVersion() - { - $this->output->writeln(sprintf( - 'Your current local build version is: %s', - $this->version - )); - } - - protected function printCurrentStableVersion() - { - $this->printVersion($this->getStableUpdater()); - } - - protected function printCurrentPreReleaseVersion() - { - $this->printVersion($this->getPreReleaseUpdater()); - } - - protected function printCurrentDevVersion() - { - $this->printVersion($this->getDevelopmentUpdater()); - } - - protected function printVersion(Updater $updater) - { - $stability = 'stable'; - if ($updater->getStrategy() instanceof ShaStrategy) { - $stability = 'development'; - } elseif ($updater->getStrategy() instanceof GithubStrategy - && GithubStrategy::UNSTABLE === $updater->getStrategy()->getStability()) { - $stability = 'pre-release'; - } - - try { - if ($updater->hasUpdate()) { - $this->output->writeln(sprintf( - 'The current %s build available remotely is: %s', - $stability, - $updater->getNewVersion() - )); - } elseif (false === $updater->getNewVersion()) { - $this->output->writeln(sprintf('There are no %s builds available.', $stability)); - } else { - $this->output->writeln(sprintf('You have the current %s build installed.', $stability)); - } - } catch (\Exception $e) { - $this->output->writeln(sprintf('Error: %s', $e->getMessage())); - } - } -} From 5c1c7940b59fcaa971ffd1f014f803c0c3f8dd5f Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 14 Aug 2024 15:21:31 +0200 Subject: [PATCH 072/121] chore: fix cs --- src/Cli/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/Application.php b/src/Cli/Application.php index 40ad7724..1224cced 100644 --- a/src/Cli/Application.php +++ b/src/Cli/Application.php @@ -43,7 +43,7 @@ protected function getDefaultCommands(): array new RunCommand(), new RevokeCommand(), new StatusCommand(), - new SelfUpdateCommand($this->getName(), explode('@', $this->getVersion())[0], 'acmephp/acmephp'), + new SelfUpdateCommand($this->getName(), explode('@', $this->getVersion())[0], 'acmephp/acmephp'), ]); } From 9ab7e1b6eb2b9eb4140cf5ef9487dcf9bcdb0728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 14 Aug 2024 15:28:27 +0200 Subject: [PATCH 073/121] Setup PHPStan --- .github/workflows/test-build.yaml | 21 +- CHANGELOG.md | 8 + composer.json | 1 + phpstan-baseline.neon | 536 ++++++++++++++++++++++++++++++ phpstan.neon | 17 + 5 files changed, 581 insertions(+), 2 deletions(-) create mode 100644 phpstan-baseline.neon create mode 100644 phpstan.neon diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 38b8644b..4e9799db 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -8,12 +8,12 @@ on: jobs: php-cs: + name: PHP-CS-Fixer runs-on: ubuntu-latest steps: - uses: shivammathur/setup-php@v2 with: - php-version: '8.1' - coverage: none + php-version: '8.3' - uses: actions/checkout@master @@ -23,6 +23,23 @@ jobs: - name: Check coding style run: php php-cs-fixer.phar fix --dry-run --diff + phpstan: + name: Test PHP ${{ matrix.php-version }} ${{ matrix.name }} + runs-on: ubuntu-latest + steps: + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + + - uses: actions/checkout@v4 + + - name: Install Composer dependencies + run: | + composer update --prefer-dist --no-interaction + + - name: Run PHPStan + run: vendor/bin/phpstan analyse + ci: name: Test PHP ${{ matrix.php-version }} ${{ matrix.name }} runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index ed010da4..64e0b869 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,16 @@ > [!NOTE] > From now on, a particular attention will be given to provide a nice changelog. +### BC Break + * Drop support for PHP <8.1 * Drop support for Symfony <5.4, and 6.0, 6.1, 6.2, 6.3 +* Upgrade from FlySystem v1 to v3 + +### Internal + +* Update PHP-CS-Fixer to 3.62.0 +* Analyse code with PHPStan ## 07/06/2022 22:41 2.1.0 Add compatibility for PHP 8.0/8.1, Symfony 6 and other improvements diff --git a/composer.json b/composer.json index b0d8e57e..3e91c0ed 100644 --- a/composer.json +++ b/composer.json @@ -65,6 +65,7 @@ "webmozart/path-util": "^2.3" }, "require-dev": { + "phpstan/phpstan": "^1.11", "symfony/finder": "^5.4.12 || ^6.4", "symfony/phpunit-bridge": "^5.4.12 || ^6.4", "symfony/property-access": "^5.4.12 || ^6.4", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 00000000..1b309a64 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,536 @@ +parameters: + ignoreErrors: + - + message: "#^Call to an undefined method AlibabaCloud\\\\WafOpenapi\\\\V20161111\\\\UpgradeInstance\\:\\:withCert\\(\\)\\.$#" + count: 1 + path: src/Cli/Action/InstallAliyunWafAction.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Application\\:\\:getStorageDirectory\\(\\)\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Call to an undefined method object\\:\\:createSecureHttpClient\\(\\)\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:alert\\(\\) with return type void returns null but should not return anything\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:critical\\(\\) with return type void returns null but should not return anything\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:debug\\(\\) with return type void returns null but should not return anything\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:emergency\\(\\) with return type void returns null but should not return anything\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:error\\(\\) with return type void returns null but should not return anything\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:getCliLogger\\(\\) should return Psr\\\\Log\\\\LoggerInterface but returns object\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:getRepository\\(\\) should return AcmePhp\\\\Cli\\\\Repository\\\\RepositoryInterface but returns object\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:info\\(\\) with return type void returns null but should not return anything\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:log\\(\\) with return type void returns null but should not return anything\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:notice\\(\\) with return type void returns null but should not return anything\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:warning\\(\\) with return type void returns null but should not return anything\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:alert\\(\\) \\(void\\) is used\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:critical\\(\\) \\(void\\) is used\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:debug\\(\\) \\(void\\) is used\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:emergency\\(\\) \\(void\\) is used\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:error\\(\\) \\(void\\) is used\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:info\\(\\) \\(void\\) is used\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:log\\(\\) \\(void\\) is used\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:notice\\(\\) \\(void\\) is used\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:warning\\(\\) \\(void\\) is used\\.$#" + count: 1 + path: src/Cli/Command/AbstractCommand.php + + - + message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\RevokeCommand\\:\\:execute\\(\\) should return int but empty return statement found\\.$#" + count: 3 + path: src/Cli/Command/RevokeCommand.php + + - + message: "#^Access to an undefined property object\\:\\:\\$eab_hmac_key\\.$#" + count: 2 + path: src/Cli/Command/RunCommand.php + + - + message: "#^Access to an undefined property object\\:\\:\\$eab_kid\\.$#" + count: 2 + path: src/Cli/Command/RunCommand.php + + - + message: "#^Call to an undefined method object\\:\\:generateKeyPair\\(\\)\\.$#" + count: 2 + path: src/Cli/Command/RunCommand.php + + - + message: "#^Call to an undefined method object\\:\\:get\\(\\)\\.$#" + count: 1 + path: src/Cli/Command/RunCommand.php + + - + message: "#^Call to an undefined method object\\:\\:parse\\(\\)\\.$#" + count: 2 + path: src/Cli/Command/RunCommand.php + + - + message: "#^PHPDoc tag @var for variable \\$solverLocator contains generic class Symfony\\\\Component\\\\DependencyInjection\\\\ServiceLocator but does not specify its types\\: T$#" + count: 1 + path: src/Cli/Command/RunCommand.php + + - + message: "#^Parameter \\#1 \\$input of static method Symfony\\\\Component\\\\Yaml\\\\Yaml\\:\\:parse\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: src/Cli/Command/RunCommand.php + + - + message: "#^Parameter \\#2 \\$basePath of static method Webmozart\\\\PathUtil\\\\Path\\:\\:makeAbsolute\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: src/Cli/Command/RunCommand.php + + - + message: "#^Property AcmePhp\\\\Cli\\\\Command\\\\RunCommand\\:\\:\\$config has no type specified\\.$#" + count: 1 + path: src/Cli/Command/RunCommand.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\TreeBuilder\\:\\:root\\(\\)\\.$#" + count: 5 + path: src/Cli/Configuration/DomainConfiguration.php + + - + message: "#^Class Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\TreeBuilder constructor invoked with 0 parameters, 1\\-3 required\\.$#" + count: 4 + path: src/Cli/Configuration/DomainConfiguration.php + + - + message: "#^Parameter \\#1 \\$string of function sha1 expects string, string\\|false given\\.$#" + count: 1 + path: src/Cli/Repository/Repository.php + + - + message: "#^Method AcmePhp\\\\Cli\\\\Serializer\\\\PemNormalizer\\:\\:normalize\\(\\) return type with generic class ArrayObject does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Cli/Serializer/PemNormalizer.php + + - + message: "#^Method AcmePhp\\\\Core\\\\AcmeClient\\:\\:registerAccount\\(\\) should return array but returns array\\|string\\.$#" + count: 1 + path: src/Core/AcmeClient.php + + - + message: "#^Negated boolean expression is always false\\.$#" + count: 2 + path: src/Core/AcmeClient.php + + - + message: "#^Offset 'challenges' does not exist on array\\|string\\.$#" + count: 2 + path: src/Core/AcmeClient.php + + - + message: "#^Offset 'finalize' does not exist on array\\|string\\.$#" + count: 2 + path: src/Core/AcmeClient.php + + - + message: "#^Offset 'identifier' does not exist on array\\|string\\.$#" + count: 2 + path: src/Core/AcmeClient.php + + - + message: "#^Offset 'status' does not exist on array\\|string\\.$#" + count: 2 + path: src/Core/AcmeClient.php + + - + message: "#^Parameter \\#1 \\$certificate of function openssl_x509_export expects OpenSSLCertificate\\|string, OpenSSLCertificate\\|false given\\.$#" + count: 1 + path: src/Core/AcmeClient.php + + - + message: "#^Parameter \\#1 \\$serverResources of class AcmePhp\\\\Core\\\\Protocol\\\\ResourcesDirectory constructor expects array, array\\|string given\\.$#" + count: 1 + path: src/Core/AcmeClient.php + + - + message: "#^Parameter \\#1 \\$string of function trim expects string, array\\\\|string given\\.$#" + count: 1 + path: src/Core/AcmeClient.php + + - + message: "#^Parameter \\#2 \\$certificate of method AcmePhp\\\\Core\\\\AcmeClient\\:\\:createCertificateResponse\\(\\) expects string, array\\|string given\\.$#" + count: 1 + path: src/Core/AcmeClient.php + + - + message: "#^Property AcmePhp\\\\Core\\\\Challenge\\\\Dns\\\\GandiSolver\\:\\:\\$cacheZones is unused\\.$#" + count: 1 + path: src/Core/Challenge/Dns/GandiSolver.php + + - + message: "#^Access to constant NS on an unknown class LibDNS\\\\Records\\\\ResourceTypes\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Access to constant QUERY on an unknown class LibDNS\\\\Messages\\\\MessageTypes\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Access to constant TXT on an unknown class LibDNS\\\\Records\\\\ResourceTypes\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Call to method create\\(\\) on an unknown class LibDNS\\\\Decoder\\\\DecoderFactory\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Call to method create\\(\\) on an unknown class LibDNS\\\\Encoder\\\\EncoderFactory\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Call to method create\\(\\) on an unknown class LibDNS\\\\Messages\\\\MessageFactory\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Call to method create\\(\\) on an unknown class LibDNS\\\\Records\\\\QuestionFactory\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Call to method decode\\(\\) on an unknown class LibDNS\\\\Decoder\\\\Decoder\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Call to method encode\\(\\) on an unknown class LibDNS\\\\Encoder\\\\Encoder\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Instantiated class LibDNS\\\\Decoder\\\\DecoderFactory not found\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Instantiated class LibDNS\\\\Encoder\\\\EncoderFactory not found\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Instantiated class LibDNS\\\\Messages\\\\MessageFactory not found\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Instantiated class LibDNS\\\\Records\\\\QuestionFactory not found\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Parameter \\#1 \\$json of function json_decode expects string, int\\|string given\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Parameter \\#1 \\$read of function stream_select expects TRead of array\\\\|null, array\\ given\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Parameter \\#1 \\$socket of function stream_socket_sendto expects resource, resource\\|false given\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Parameter \\#1 \\$stream of function fread expects resource, resource\\|false given\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Parameter \\$decoder of method AcmePhp\\\\Core\\\\Challenge\\\\Dns\\\\LibDnsResolver\\:\\:__construct\\(\\) has invalid type LibDNS\\\\Decoder\\\\Decoder\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Parameter \\$encoder of method AcmePhp\\\\Core\\\\Challenge\\\\Dns\\\\LibDnsResolver\\:\\:__construct\\(\\) has invalid type LibDNS\\\\Encoder\\\\Encoder\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Parameter \\$messageFactory of method AcmePhp\\\\Core\\\\Challenge\\\\Dns\\\\LibDnsResolver\\:\\:__construct\\(\\) has invalid type LibDNS\\\\Messages\\\\MessageFactory\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Parameter \\$questionFactory of method AcmePhp\\\\Core\\\\Challenge\\\\Dns\\\\LibDnsResolver\\:\\:__construct\\(\\) has invalid type LibDNS\\\\Records\\\\QuestionFactory\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Property AcmePhp\\\\Core\\\\Challenge\\\\Dns\\\\LibDnsResolver\\:\\:\\$decoder has unknown class LibDNS\\\\Decoder\\\\Decoder as its type\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Property AcmePhp\\\\Core\\\\Challenge\\\\Dns\\\\LibDnsResolver\\:\\:\\$encoder has unknown class LibDNS\\\\Encoder\\\\Encoder as its type\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Property AcmePhp\\\\Core\\\\Challenge\\\\Dns\\\\LibDnsResolver\\:\\:\\$messageFactory has unknown class LibDNS\\\\Messages\\\\MessageFactory as its type\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Property AcmePhp\\\\Core\\\\Challenge\\\\Dns\\\\LibDnsResolver\\:\\:\\$questionFactory has unknown class LibDNS\\\\Records\\\\QuestionFactory as its type\\.$#" + count: 1 + path: src/Core/Challenge/Dns/LibDnsResolver.php + + - + message: "#^Parameter \\#1 \\$string of function addslashes expects string, int\\|string\\|false given\\.$#" + count: 1 + path: src/Core/Challenge/Dns/Route53Solver.php + + - + message: "#^Argument of an invalid type array\\\\|false supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/Core/Challenge/Dns/SimpleDnsResolver.php + + - + message: "#^Constructor of class AcmePhp\\\\Core\\\\Exception\\\\AcmeCoreServerException has an unused parameter \\$request\\.$#" + count: 1 + path: src/Core/Exception/AcmeCoreServerException.php + + - + message: "#^Property AcmePhp\\\\Core\\\\Exception\\\\Protocol\\\\ChallengeFailedException\\:\\:\\$response has no type specified\\.$#" + count: 1 + path: src/Core/Exception/Protocol/ChallengeFailedException.php + + - + message: "#^Property AcmePhp\\\\Core\\\\Exception\\\\Protocol\\\\ChallengeTimedOutException\\:\\:\\$response has no type specified\\.$#" + count: 1 + path: src/Core/Exception/Protocol/ChallengeTimedOutException.php + + - + message: "#^Instantiated class League\\\\Flysystem\\\\Adapter\\\\Ftp not found\\.$#" + count: 1 + path: src/Core/Filesystem/Adapter/FlysystemFtpFactory.php + + - + message: "#^Parameter \\#1 \\$adapter of class League\\\\Flysystem\\\\Filesystem constructor expects League\\\\Flysystem\\\\FilesystemAdapter, League\\\\Flysystem\\\\Adapter\\\\Ftp given\\.$#" + count: 1 + path: src/Core/Filesystem/Adapter/FlysystemFtpFactory.php + + - + message: "#^Instantiated class League\\\\Flysystem\\\\Adapter\\\\Local not found\\.$#" + count: 1 + path: src/Core/Filesystem/Adapter/FlysystemLocalFactory.php + + - + message: "#^Parameter \\#1 \\$adapter of class League\\\\Flysystem\\\\Filesystem constructor expects League\\\\Flysystem\\\\FilesystemAdapter, League\\\\Flysystem\\\\Adapter\\\\Local given\\.$#" + count: 1 + path: src/Core/Filesystem/Adapter/FlysystemLocalFactory.php + + - + message: "#^Call to an undefined method Lcobucci\\\\JWT\\\\Signer\\\\Hmac\\\\Sha256\\:\\:getAlgorithmId\\(\\)\\.$#" + count: 1 + path: src/Core/Http/SecureHttpClient.php + + - + message: "#^Left side of && is always true\\.$#" + count: 1 + path: src/Core/Http/SecureHttpClient.php + + - + message: "#^Parameter \\#1 \\$contents of static method Lcobucci\\\\JWT\\\\Signer\\\\Key\\\\InMemory\\:\\:plainText\\(\\) expects non\\-empty\\-string, string given\\.$#" + count: 1 + path: src/Core/Http/SecureHttpClient.php + + - + message: "#^Parameter \\#1 \\$input of method AcmePhp\\\\Core\\\\Http\\\\Base64SafeEncoder\\:\\:encode\\(\\) expects string, string\\|false given\\.$#" + count: 4 + path: src/Core/Http/SecureHttpClient.php + + - + message: "#^Parameter \\#2 \\$data of function hash expects string, string\\|false given\\.$#" + count: 1 + path: src/Core/Http/SecureHttpClient.php + + - + message: "#^Parameter \\#2 \\$key of method Lcobucci\\\\JWT\\\\Signer\\\\Hmac\\:\\:sign\\(\\) expects Lcobucci\\\\JWT\\\\Signer\\\\Key, Lcobucci\\\\JWT\\\\Signer\\\\Key\\\\InMemory\\|string given\\.$#" + count: 1 + path: src/Core/Http/SecureHttpClient.php + + - + message: "#^Parameter \\#2 \\$payload of method AcmePhp\\\\Core\\\\Http\\\\SecureHttpClient\\:\\:signPayload\\(\\) expects array\\|null, array\\|string\\|null given\\.$#" + count: 2 + path: src/Core/Http/SecureHttpClient.php + + - + message: "#^Method AcmePhp\\\\Core\\\\Http\\\\ServerErrorHandler\\:\\:createAcmeExceptionForResponse\\(\\) should return AcmePhp\\\\Core\\\\Exception\\\\AcmeCoreServerException but returns object\\.$#" + count: 1 + path: src/Core/Http/ServerErrorHandler.php + + - + message: "#^Property AcmePhp\\\\Core\\\\Http\\\\ServerErrorHandler\\:\\:\\$exceptions has no type specified\\.$#" + count: 1 + path: src/Core/Http/ServerErrorHandler.php + + - + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 1 + path: src/Core/Protocol/RevocationReason.php + + - + message: "#^Parameter \\#3 \\$depth of function json_decode expects int\\<1, max\\>, int given\\.$#" + count: 1 + path: src/Core/Util/JsonDecoder.php + + - + message: "#^Cannot access offset 'key' on array\\|false\\.$#" + count: 1 + path: src/Ssl/Certificate.php + + - + message: "#^Method AcmePhp\\\\Ssl\\\\Certificate\\:\\:getPublicKeyResource\\(\\) should return resource but returns OpenSSLAsymmetricKey\\.$#" + count: 1 + path: src/Ssl/Certificate.php + + - + message: "#^Parameter \\#1 \\$key of function openssl_pkey_get_details expects OpenSSLAsymmetricKey, resource given\\.$#" + count: 1 + path: src/Ssl/Certificate.php + + - + message: "#^Property AcmePhp\\\\Ssl\\\\Generator\\\\ChainPrivateKeyGenerator\\:\\:\\$generators \\(array\\\\) does not accept iterable\\\\.$#" + count: 1 + path: src/Ssl/Generator/ChainPrivateKeyGenerator.php + + - + message: "#^Parameter \\#2 \\$values of static method Webmozart\\\\Assert\\\\Assert\\:\\:oneOf\\(\\) expects array, array\\\\|false given\\.$#" + count: 1 + path: src/Ssl/Generator/EcKey/EcKeyOption.php + + - + message: "#^Parameter \\#1 \\$key of function openssl_free_key expects OpenSSLAsymmetricKey, OpenSSLAsymmetricKey\\|resource given\\.$#" + count: 1 + path: src/Ssl/Parser/KeyParser.php + + - + message: "#^Parameter \\#1 \\$key of function openssl_pkey_get_details expects OpenSSLAsymmetricKey, OpenSSLAsymmetricKey\\|resource given\\.$#" + count: 1 + path: src/Ssl/Parser/KeyParser.php + + - + message: "#^Parameter \\#1 \\$key of function openssl_free_key expects OpenSSLAsymmetricKey, OpenSSLAsymmetricKey\\|resource given\\.$#" + count: 1 + path: src/Ssl/PrivateKey.php + + - + message: "#^Parameter \\#1 \\$key of function openssl_pkey_get_details expects OpenSSLAsymmetricKey, OpenSSLAsymmetricKey\\|resource given\\.$#" + count: 1 + path: src/Ssl/PrivateKey.php + + - + message: "#^Cannot access offset 1 on array\\|false\\.$#" + count: 1 + path: src/Ssl/Signer/DataSigner.php + + - + message: "#^Parameter \\#1 \\$key of function openssl_free_key expects OpenSSLAsymmetricKey, OpenSSLAsymmetricKey\\|resource given\\.$#" + count: 1 + path: src/Ssl/Signer/DataSigner.php + + - + message: "#^Parameter \\#2 \\$start of function mb_substr expects int, float\\|int given\\.$#" + count: 1 + path: src/Ssl/Signer/DataSigner.php + + - + message: "#^Parameter \\#3 \\$length of function mb_substr expects int\\|null, float\\|int given\\.$#" + count: 2 + path: src/Ssl/Signer/DataSigner.php + + - + message: "#^Parameter \\#3 \\$private_key of function openssl_sign expects array\\|OpenSSLAsymmetricKey\\|OpenSSLCertificate\\|string, OpenSSLAsymmetricKey\\|resource given\\.$#" + count: 1 + path: src/Ssl/Signer/DataSigner.php diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 00000000..03a0bac5 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,17 @@ +includes: + - phpstan-baseline.neon + +parameters: + level: 7 + paths: + - src + + inferPrivatePropertyTypeFromConstructor: true + + ignoreErrors: + - + identifier: missingType.return + - + identifier: missingType.iterableValue + - + identifier: missingType.parameter From ef358f17e04a4b371b1596b4b851b865a98e2b2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 14 Aug 2024 15:47:48 +0200 Subject: [PATCH 074/121] Drop support for lcobucci/jwt < 4.1 + Add support for ^5.3 --- CHANGELOG.md | 12 ++++++++++++ composer.json | 2 +- src/Core/Http/SecureHttpClient.php | 2 +- tests/Core/Http/SecureHttpClientTest.php | 22 ++++++++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed010da4..d981552e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,20 @@ > [!NOTE] > From now on, a particular attention will be given to provide a nice changelog. +### Features + +* Add support for lcobucci/jwt ^5.3 + +### BC Break + * Drop support for PHP <8.1 * Drop support for Symfony <5.4, and 6.0, 6.1, 6.2, 6.3 +* Drop support for lcobucci/jwt < 4.1 +* Upgrade from FlySystem v1 to v3 + +### Internal + +* Update PHP-CS-Fixer to 3.62.0 ## 07/06/2022 22:41 2.1.0 Add compatibility for PHP 8.0/8.1, Symfony 6 and other improvements diff --git a/composer.json b/composer.json index b0d8e57e..ae40803c 100644 --- a/composer.json +++ b/composer.json @@ -46,7 +46,7 @@ "aws/aws-sdk-php": "^3.38", "guzzlehttp/guzzle": "^7.2", "guzzlehttp/psr7": "^1.0", - "lcobucci/jwt": "^3.3|^4.0", + "lcobucci/jwt": "^4.1 || ^5.3", "league/flysystem": "^3.10", "league/flysystem-memory": "^3.10", "league/flysystem-sftp-v3": "^3.10", diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index 127b1811..4172f1bf 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -163,7 +163,7 @@ public function createExternalAccountPayload(ExternalAccount $externalAccount, s $signer = new Sha256(); $protected = [ - 'alg' => method_exists($signer, 'algorithmId') ? $signer->algorithmId() : $signer->getAlgorithmId(), + 'alg' => $signer->algorithmId(), 'kid' => $externalAccount->getId(), 'url' => $url, ]; diff --git a/tests/Core/Http/SecureHttpClientTest.php b/tests/Core/Http/SecureHttpClientTest.php index 649bc9da..3902a990 100644 --- a/tests/Core/Http/SecureHttpClientTest.php +++ b/tests/Core/Http/SecureHttpClientTest.php @@ -15,6 +15,7 @@ use AcmePhp\Core\Http\Base64SafeEncoder; use AcmePhp\Core\Http\SecureHttpClient; use AcmePhp\Core\Http\ServerErrorHandler; +use AcmePhp\Core\Protocol\ExternalAccount; use AcmePhp\Ssl\Generator\KeyPairGenerator; use AcmePhp\Ssl\Parser\KeyParser; use AcmePhp\Ssl\Signer\DataSigner; @@ -142,6 +143,27 @@ public function testInvalidJsonRequest() $client->request('GET', '/foo', ['foo' => 'bar'], true); } + public function testCreateExternalAccountPayload(): void + { + $client = new SecureHttpClient( + (new KeyPairGenerator())->generateKeyPair(), + new Client(), + new Base64SafeEncoder(), + new KeyParser(), + new DataSigner(), + new ServerErrorHandler(), + ); + + $payload = $client->createExternalAccountPayload(new ExternalAccount('id', str_repeat('hmacKey', '100')), 'bar'); + + $this->assertArrayHasKey('protected', $payload); + $this->assertSame('eyJhbGciOiJIUzI1NiIsImtpZCI6ImlkIiwidXJsIjoiYmFyIn0', $payload['protected']); + $this->assertArrayHasKey('payload', $payload); + $this->assertStringStartsWith('ey', $payload['payload']); + $this->assertArrayHasKey('signature', $payload); + $this->assertNotEmpty($payload['signature']); + } + public function testRequestPayload() { $container = []; From 0e659d45c1765ad020d13f2aa5bf98eadcbe2a75 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 14 Aug 2024 16:08:09 +0200 Subject: [PATCH 075/121] fix: allow application to run without a git version set --- src/Cli/Application.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Cli/Application.php b/src/Cli/Application.php index 1224cced..18347d33 100644 --- a/src/Cli/Application.php +++ b/src/Cli/Application.php @@ -39,11 +39,12 @@ public function __construct() protected function getDefaultCommands(): array { + $version = explode('@', $this->getVersion())[0]; return array_merge(parent::getDefaultCommands(), [ new RunCommand(), new RevokeCommand(), new StatusCommand(), - new SelfUpdateCommand($this->getName(), explode('@', $this->getVersion())[0], 'acmephp/acmephp'), + new SelfUpdateCommand($this->getName(), $version === '' ? '0.0.0' : $version, 'acmephp/acmephp'), ]); } From 0c7b448c07c735d3725df544e980b54756c93f3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 14 Aug 2024 18:24:53 +0200 Subject: [PATCH 076/121] update monolog and psr/log --- CHANGELOG.md | 13 ++++++++++++ composer.json | 4 ++-- src/Cli/Command/AbstractCommand.php | 2 +- src/Cli/Monolog/ConsoleFormatter.php | 30 +++++++++++++++++---------- src/Cli/Monolog/ConsoleHandler.php | 15 +++++++++++--- tests/Cli/AbstractApplicationTest.php | 15 ++++++++++---- 6 files changed, 58 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed010da4..890b5d61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,21 @@ > [!NOTE] > From now on, a particular attention will be given to provide a nice changelog. +### Features + +* Drop support for monolog ^3 +* Drop support for psr/log ^2 || ^3 + +### BC Break + * Drop support for PHP <8.1 * Drop support for Symfony <5.4, and 6.0, 6.1, 6.2, 6.3 +* Drop support for monolog < 2 +* Drop support for psr/log < 2 + +### Internal + +* Update PHP-CS-Fixer to 3.62.0 ## 07/06/2022 22:41 2.1.0 Add compatibility for PHP 8.0/8.1, Symfony 6 and other improvements diff --git a/composer.json b/composer.json index b0d8e57e..d14c3566 100644 --- a/composer.json +++ b/composer.json @@ -50,11 +50,11 @@ "league/flysystem": "^3.10", "league/flysystem-memory": "^3.10", "league/flysystem-sftp-v3": "^3.10", - "monolog/monolog": "^1.19|^2.0", + "monolog/monolog": "^2.0 || ^3", "padraic/phar-updater": "^1.0", "psr/container": "^1.0", "psr/http-message": "^1.0", - "psr/log": "^1.0", + "psr/log": "^2 || ^3", "symfony/config": "^5.4.12 || ^6.4", "symfony/console": "^5.4.12 || ^6.4", "symfony/dependency-injection": "^5.4.12 || ^6.4", diff --git a/src/Cli/Command/AbstractCommand.php b/src/Cli/Command/AbstractCommand.php index 408bd164..ce208626 100644 --- a/src/Cli/Command/AbstractCommand.php +++ b/src/Cli/Command/AbstractCommand.php @@ -30,7 +30,7 @@ /** * @author Titouan Galopin */ -abstract class AbstractCommand extends Command implements LoggerInterface +abstract class AbstractCommand extends Command { /** * @var InputInterface diff --git a/src/Cli/Monolog/ConsoleFormatter.php b/src/Cli/Monolog/ConsoleFormatter.php index 36346f2f..127e527b 100644 --- a/src/Cli/Monolog/ConsoleFormatter.php +++ b/src/Cli/Monolog/ConsoleFormatter.php @@ -25,29 +25,37 @@ */ class ConsoleFormatter extends LineFormatter { - public const SIMPLE_FORMAT = "%start_tag%%message% %context% %extra%%end_tag%\n"; + public const SIMPLE_FORMAT = "%extra.start_tag%%message% %context% %extra%%extra.end_tag%\n"; public function __construct($format = null, $dateFormat = null, $allowInlineLineBreaks = false, $ignoreEmptyContextAndExtra = true) { parent::__construct($format, $dateFormat, $allowInlineLineBreaks, $ignoreEmptyContextAndExtra); } - public function format(array $record): string + /** + * @param LogRecord|array $record + */ + public function format($record): string { if ($record['level'] >= Logger::ERROR) { - $record['start_tag'] = ''; - $record['end_tag'] = ''; + $extra['start_tag'] = ''; + $extra['end_tag'] = ''; } elseif ($record['level'] >= Logger::WARNING) { - $record['start_tag'] = ''; - $record['end_tag'] = ''; + $extra['start_tag'] = ''; + $extra['end_tag'] = ''; } elseif ($record['level'] >= Logger::NOTICE) { - $record['start_tag'] = ''; - $record['end_tag'] = ''; + $extra['start_tag'] = ''; + $extra['end_tag'] = ''; } else { - $record['start_tag'] = ''; - $record['end_tag'] = ''; + $extra['start_tag'] = ''; + $extra['end_tag'] = ''; } - return parent::format($record); + $record['extra'] = [ + ...$record['extra'], + ...$extra, + ]; + + return dump(parent::format($record)); } } diff --git a/src/Cli/Monolog/ConsoleHandler.php b/src/Cli/Monolog/ConsoleHandler.php index f509b638..6c06608d 100644 --- a/src/Cli/Monolog/ConsoleHandler.php +++ b/src/Cli/Monolog/ConsoleHandler.php @@ -63,12 +63,18 @@ public function __construct(?OutputInterface $output = null, bool $bubble = true } } - public function isHandling(array $record): bool + /** + * @param LogRecord|array $record + */ + public function isHandling($record): bool { return $this->updateLevel() && parent::isHandling($record); } - public function handle(array $record): bool + /** + * @param LogRecord|array $record + */ + public function handle($record): bool { // we have to update the logging level each time because the verbosity of the // console output might have changed in the meantime (it is not immutable) @@ -95,7 +101,10 @@ public function close(): void parent::close(); } - protected function write(array $record): void + /** + * @param LogRecord|array $record + */ + protected function write($record): void { $this->output->write((string) $record['formatted']); } diff --git a/tests/Cli/AbstractApplicationTest.php b/tests/Cli/AbstractApplicationTest.php index be8da5c0..62ebf8ae 100644 --- a/tests/Cli/AbstractApplicationTest.php +++ b/tests/Cli/AbstractApplicationTest.php @@ -43,13 +43,20 @@ public function tearDown(): void public function testFullProcess() { $runTester = new CommandTester($this->application->find('run')); - $runTester->execute([ - 'command' => 'run', - 'config' => $this->getConfigDir().'/'.('eab' === getenv('PEBBLE_MODE') ? 'eab' : 'default').'.yaml', - ]); + $runTester->execute( + [ + 'command' => 'run', + 'config' => $this->getConfigDir().'/'.('eab' === getenv('PEBBLE_MODE') ? 'eab' : 'default').'.yaml', + ], + [ + 'decorated' => true, + ] + ); $output = $runTester->getDisplay(); + $this->assertStringContainsString("\e[32mLoading account key pair... \e[39m", $output); + // Register $this->assertStringContainsString('No account key pair was found, generating one', $output); $this->assertStringContainsString('Account registered successfully', $output); From bef542f37d95deaa30536833872e0ca6ebdf4204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 14 Aug 2024 18:04:29 +0200 Subject: [PATCH 077/121] update guzzlehttp/psr7 to ^2.4.5 --- CHANGELOG.md | 12 ++++++++++++ composer.json | 2 +- src/Cli/Action/PushRancherAction.php | 4 ++-- src/Cli/Command/RunCommand.php | 4 ++-- tests/Core/Http/SecureHttpClientTest.php | 3 +-- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed010da4..6232754d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,20 @@ > [!NOTE] > From now on, a particular attention will be given to provide a nice changelog. +### Features + +* Add support for guzzlehttp/psr7 ^2.4 + +### BC Break + * Drop support for PHP <8.1 * Drop support for Symfony <5.4, and 6.0, 6.1, 6.2, 6.3 +* Drop support for guzzlehttp/psr7 < 2 +* Upgrade from FlySystem v1 to v3 + +### Internal + +* Update PHP-CS-Fixer to 3.62.0 ## 07/06/2022 22:41 2.1.0 Add compatibility for PHP 8.0/8.1, Symfony 6 and other improvements diff --git a/composer.json b/composer.json index b0d8e57e..eebdb334 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "alibabacloud/wafopenapi": "^1.7", "aws/aws-sdk-php": "^3.38", "guzzlehttp/guzzle": "^7.2", - "guzzlehttp/psr7": "^1.0", + "guzzlehttp/psr7": "^2.4.5", "lcobucci/jwt": "^3.3|^4.0", "league/flysystem": "^3.10", "league/flysystem-memory": "^3.10", diff --git a/src/Cli/Action/PushRancherAction.php b/src/Cli/Action/PushRancherAction.php index 015804ea..1dd33c2c 100644 --- a/src/Cli/Action/PushRancherAction.php +++ b/src/Cli/Action/PushRancherAction.php @@ -65,7 +65,7 @@ private function createRancherPayloadFromResponse(CertificateResponse $response) return $certificate->getPEM(); }, $certificate->getIssuerChain()); - return \GuzzleHttp\json_encode([ + return json_encode([ 'name' => $response->getCertificateRequest()->getDistinguishedName()->getCommonName(), 'description' => 'Generated with Acme PHP', 'cert' => $certificate->getPEM(), @@ -123,6 +123,6 @@ private function request($method, $url, $body = null) 'body' => $body ?: '', ]); - return \GuzzleHttp\json_decode(\GuzzleHttp\Psr7\copy_to_string($response->getBody()), true); + return json_decode((string) $response->getBody(), true); } } diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index cca39a3c..bd85f273 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -145,7 +145,7 @@ private function resolveEabKid(): ?ExternalAccount if ('zerossl' === $this->config['provider']) { // If an API key is provided, use it if ($this->config['zerossl_api_key']) { - $eabCredentials = \GuzzleHttp\json_decode( + $eabCredentials = json_decode( (new Client()) ->post('https://api.zerossl.com/acme/eab-credentials/?access_key='.$this->config['zerossl_api_key']) ->getBody() @@ -160,7 +160,7 @@ private function resolveEabKid(): ?ExternalAccount } // Otherwise register on the fly - $eabCredentials = \GuzzleHttp\json_decode( + $eabCredentials = json_decode( (new Client()) ->post('https://api.zerossl.com/acme/eab-credentials-email', [ 'form_params' => ['email' => $this->config['contact_email']], diff --git a/tests/Core/Http/SecureHttpClientTest.php b/tests/Core/Http/SecureHttpClientTest.php index 649bc9da..fb20b080 100644 --- a/tests/Core/Http/SecureHttpClientTest.php +++ b/tests/Core/Http/SecureHttpClientTest.php @@ -177,8 +177,7 @@ public function testRequestPayload() $this->assertEquals('POST', $request->getMethod()); $this->assertEquals('/acme/new-reg', ($request->getUri() instanceof Uri) ? $request->getUri()->getPath() : $request->getUri()); - $body = \GuzzleHttp\Psr7\copy_to_string($request->getBody()); - $payload = @json_decode($body, true); + $payload = @json_decode((string) $request->getBody(), true); $this->assertIsArray($payload); $this->assertArrayHasKey('protected', $payload); From cf7113dec734c21d0a9af5cbd07bb54ad97e96e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 14 Aug 2024 18:29:22 +0200 Subject: [PATCH 078/121] Restore missing tests with PEBBLE_MODE=EAB --- .github/workflows/test-build.yaml | 12 +++++++++++- tests/Cli/Fixtures/config/sfpt_nginxproxy/eab.yaml | 2 +- tests/Cli/Fixtures/config/simple/eab.yaml | 2 +- tests/Core/AcmeClientTest.php | 4 ++-- tests/Fixtures/pebble-config-eab.json | 2 +- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 38b8644b..f748d12f 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -24,7 +24,7 @@ jobs: run: php php-cs-fixer.phar fix --dry-run --diff ci: - name: Test PHP ${{ matrix.php-version }} ${{ matrix.name }} + name: Test PHP ${{ matrix.php-version }} ${{ matrix.pebble_mode }} ${{ matrix.name }} runs-on: ubuntu-latest strategy: fail-fast: false @@ -32,10 +32,20 @@ jobs: php-version: ["8.2", "8.3"] composer-flags: [""] name: [""] + pebble_mode: [""] include: - php-version: 8.1 composer-flags: "--prefer-lowest" name: "(prefer lowest dependencies)" + - php-version: 8.1 + composer-flags: "--prefer-lowest" + name: "(prefer lowest dependencies - EAB)" + pebble_mode: eab + - php-version: 8.3 + name: "(EAB)" + pebble_mode: eab + env: + PEBBLE_MODE: "${{ matrix.pebble_mode }}" steps: - uses: shivammathur/setup-php@v2 diff --git a/tests/Cli/Fixtures/config/sfpt_nginxproxy/eab.yaml b/tests/Cli/Fixtures/config/sfpt_nginxproxy/eab.yaml index c3f20952..cfc6dfca 100644 --- a/tests/Cli/Fixtures/config/sfpt_nginxproxy/eab.yaml +++ b/tests/Cli/Fixtures/config/sfpt_nginxproxy/eab.yaml @@ -2,7 +2,7 @@ contact_email: foo@example.com key_type: RSA provider: localhost eab_kid: kid1 -eab_hmac_key: dGVzdGluZw +eab_hmac_key: dGVzdGluZ3Rlc3Rpbmd0ZXN0aW5ndGVzdGluZ3Rlc3Rpbmd0ZXN0aW5n defaults: distinguished_name: diff --git a/tests/Cli/Fixtures/config/simple/eab.yaml b/tests/Cli/Fixtures/config/simple/eab.yaml index 12f78b1e..6d8ee8ce 100644 --- a/tests/Cli/Fixtures/config/simple/eab.yaml +++ b/tests/Cli/Fixtures/config/simple/eab.yaml @@ -2,7 +2,7 @@ contact_email: foo@example.com key_type: RSA provider: localhost eab_kid: kid1 -eab_hmac_key: dGVzdGluZw +eab_hmac_key: dGVzdGluZ3Rlc3Rpbmd0ZXN0aW5ndGVzdGluZ3Rlc3Rpbmd0ZXN0aW5n defaults: distinguished_name: diff --git a/tests/Core/AcmeClientTest.php b/tests/Core/AcmeClientTest.php index 0838684c..302ab02b 100644 --- a/tests/Core/AcmeClientTest.php +++ b/tests/Core/AcmeClientTest.php @@ -62,7 +62,7 @@ public function testFullProcess(KeyOption $keyOption, bool $useAlternateCertific * Register account */ if ('eab' === getenv('PEBBLE_MODE')) { - $data = $client->registerAccount('titouan.galopin@acmephp.com', new ExternalAccount('kid1', 'dGVzdGluZw')); + $data = $client->registerAccount('titouan.galopin@acmephp.com', new ExternalAccount('kid1', 'dGVzdGluZ3Rlc3Rpbmd0ZXN0aW5ndGVzdGluZ3Rlc3Rpbmd0ZXN0aW5n')); } else { $data = $client->registerAccount('titouan.galopin@acmephp.com'); } @@ -152,7 +152,7 @@ public function testRequestAuthorizationAllowsCapitalisation(KeyOption $keyOptio * Register account */ if ('eab' === getenv('PEBBLE_MODE')) { - $client->registerAccount('titouan.galopin@acmephp.com', new ExternalAccount('kid1', 'dGVzdGluZw')); + $client->registerAccount('titouan.galopin@acmephp.com', new ExternalAccount('kid1', 'dGVzdGluZ3Rlc3Rpbmd0ZXN0aW5ndGVzdGluZ3Rlc3Rpbmd0ZXN0aW5n')); } else { $client->registerAccount('titouan.galopin@acmephp.com'); } diff --git a/tests/Fixtures/pebble-config-eab.json b/tests/Fixtures/pebble-config-eab.json index 6c01253f..95a4e69b 100644 --- a/tests/Fixtures/pebble-config-eab.json +++ b/tests/Fixtures/pebble-config-eab.json @@ -9,7 +9,7 @@ "ocspResponderURL": "", "externalAccountBindingRequired": true, "externalAccountMACKeys": { - "kid1": "dGVzdGluZw" + "kid1": "dGVzdGluZ3Rlc3Rpbmd0ZXN0aW5ndGVzdGluZ3Rlc3Rpbmd0ZXN0aW5n" } } } From a043367f391ef3db7ecf8a9d93666cd301fdc45f Mon Sep 17 00:00:00 2001 From: W0rma Date: Sat, 1 Jan 2022 22:54:21 +0100 Subject: [PATCH 079/121] Replace deprected webmozart/path-util with symfony/filesystem --- composer.json | 3 +-- src/Cli/Application.php | 2 +- src/Cli/Command/RunCommand.php | 2 +- tests/Cli/Mock/TestApplication.php | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index b0d8e57e..15fc2214 100644 --- a/composer.json +++ b/composer.json @@ -61,8 +61,7 @@ "symfony/filesystem": "^5.4.12 || ^6.4", "symfony/serializer": "^5.4.12 || ^6.4", "symfony/yaml": "^5.4.12 || ^6.4", - "webmozart/assert": "^1.0", - "webmozart/path-util": "^2.3" + "webmozart/assert": "^1.0" }, "require-dev": { "symfony/finder": "^5.4.12 || ^6.4", diff --git a/src/Cli/Application.php b/src/Cli/Application.php index 2dc1171f..e34fb54f 100644 --- a/src/Cli/Application.php +++ b/src/Cli/Application.php @@ -18,7 +18,7 @@ use AcmePhp\Cli\Command\StatusCommand; use Symfony\Component\Console\Application as BaseApplication; use Symfony\Component\Console\Helper\HelperSet; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; /** * @author Titouan Galopin diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index cca39a3c..25716128 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -38,8 +38,8 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Path; use Symfony\Component\Yaml\Yaml; -use Webmozart\PathUtil\Path; /** * @author Jérémy Derussé diff --git a/tests/Cli/Mock/TestApplication.php b/tests/Cli/Mock/TestApplication.php index dbc4f4c3..4ab3234b 100644 --- a/tests/Cli/Mock/TestApplication.php +++ b/tests/Cli/Mock/TestApplication.php @@ -11,7 +11,7 @@ namespace Tests\AcmePhp\Cli\Mock; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; class TestApplication extends \AcmePhp\Cli\Application { From f75cb60c327c568aedaf5fd77db2f6190913b39c Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 10:48:55 +0200 Subject: [PATCH 080/121] chore(cs): manually fix cs issues --- src/Cli/Application.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Cli/Application.php b/src/Cli/Application.php index 18347d33..b7fd7bba 100644 --- a/src/Cli/Application.php +++ b/src/Cli/Application.php @@ -40,11 +40,12 @@ public function __construct() protected function getDefaultCommands(): array { $version = explode('@', $this->getVersion())[0]; + return array_merge(parent::getDefaultCommands(), [ new RunCommand(), new RevokeCommand(), new StatusCommand(), - new SelfUpdateCommand($this->getName(), $version === '' ? '0.0.0' : $version, 'acmephp/acmephp'), + new SelfUpdateCommand($this->getName(), '' === $version ? '0.0.0' : $version, 'acmephp/acmephp'), ]); } From 8ac1a5b143c3b3d2dd3eed5398412b4d27933ebc Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 11:22:39 +0200 Subject: [PATCH 081/121] chore: drop old php BREAKING CHANGE: requires PHP8.3 or greater --- composer.json | 2 +- src/Core/composer.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index b0d8e57e..c176a170 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.3", "ext-filter": "*", "ext-hash": "*", "ext-json": "*", diff --git a/src/Core/composer.json b/src/Core/composer.json index 4955887d..1e72f35d 100644 --- a/src/Core/composer.json +++ b/src/Core/composer.json @@ -27,7 +27,7 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.3", "ext-hash": "*", "ext-json": "*", "ext-openssl": "*", @@ -35,6 +35,7 @@ "guzzlehttp/guzzle": "^6.0|^7.0", "guzzlehttp/psr7": "^1.7|^2.1", "lcobucci/jwt": "^3.3|^4.0", + "psr/http-client": "^1.0", "psr/http-message": "^1.0", "psr/log": "^1.0|^2.0|^3.0", "webmozart/assert": "^1.0" From 701f901bdea558b9f99601fb8b7034ff6c17fe6b Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 11:25:38 +0200 Subject: [PATCH 082/121] fix: remove rancher 1 push action BREAKING CHANGE: Rancher is no longer supported --- src/Cli/Action/PushRancherAction.php | 128 --------------------------- 1 file changed, 128 deletions(-) delete mode 100644 src/Cli/Action/PushRancherAction.php diff --git a/src/Cli/Action/PushRancherAction.php b/src/Cli/Action/PushRancherAction.php deleted file mode 100644 index 015804ea..00000000 --- a/src/Cli/Action/PushRancherAction.php +++ /dev/null @@ -1,128 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace AcmePhp\Cli\Action; - -use AcmePhp\Ssl\Certificate; -use AcmePhp\Ssl\CertificateResponse; -use GuzzleHttp\Client; -use GuzzleHttp\Psr7\Uri; - -/** - * Action to upload SSL certificates to Rancher using its API. - * - * @see http://docs.rancher.com/rancher/v1.2/en/api/api-resources/certificate/ - * - * @author Titouan Galopin - */ -class PushRancherAction implements ActionInterface -{ - /** - * @var Client - */ - private $httpClient; - - public function __construct(Client $httpClient) - { - $this->httpClient = $httpClient; - } - - public function handle(array $config, CertificateResponse $response) - { - $payload = $this->createRancherPayloadFromResponse($response); - - $commonName = $response->getCertificateRequest()->getDistinguishedName()->getCommonName(); - $currentCertificates = $this->getRancherCertificates($config); - - $updated = false; - - foreach ($currentCertificates as $certificate) { - if ($certificate['name'] === $commonName) { - $updated = true; - $this->updateRancherCertificate($config, $certificate['id'], $payload); - } - } - - if (!$updated) { - $this->createRancherCertificate($config, $payload); - } - } - - private function createRancherPayloadFromResponse(CertificateResponse $response) - { - $certificate = $response->getCertificate(); - $privateKey = $response->getCertificateRequest()->getKeyPair()->getPrivateKey(); - - $issuerChain = array_map(function (Certificate $certificate) { - return $certificate->getPEM(); - }, $certificate->getIssuerChain()); - - return \GuzzleHttp\json_encode([ - 'name' => $response->getCertificateRequest()->getDistinguishedName()->getCommonName(), - 'description' => 'Generated with Acme PHP', - 'cert' => $certificate->getPEM(), - 'certChain' => implode("\n", $issuerChain), - 'key' => $privateKey->getPEM(), - ]); - } - - private function getRancherCertificates($config) - { - $nextPage = $this->createUrl($config, '/v1/certificates'); - $certificates = []; - - while ($nextPage) { - $page = $this->request('GET', $nextPage); - $certificates = array_merge($certificates, $page['data']); - - $nextPage = null; - if (isset($page['pagination'], $page['pagination']['next']) && \is_string($page['pagination']['next'])) { - $nextPage = $page['pagination']['next']; - } - } - - return $certificates; - } - - private function updateRancherCertificate($config, $previousCertificateId, $newPayload) - { - $this->request('PUT', $this->createUrl($config, '/v1/certificates/'.$previousCertificateId), $newPayload); - } - - private function createRancherCertificate($config, $payload) - { - $this->request('POST', $this->createUrl($config, '/v1/certificates'), $payload); - } - - private function createUrl($config, $endpoint) - { - $url = (new Uri()) - ->withScheme($config['ssl'] ? 'https' : 'http') - ->withUserInfo($config['access_key'], $config['secret_key']) - ->withHost($config['host']) - ->withPort($config['port']) - ->withPath($endpoint); - - return (string) $url; - } - - private function request($method, $url, $body = null) - { - $response = $this->httpClient->request($method, $url, [ - 'headers' => [ - 'Content-Type' => 'application/json', - ], - 'body' => $body ?: '', - ]); - - return \GuzzleHttp\json_decode(\GuzzleHttp\Psr7\copy_to_string($response->getBody()), true); - } -} From f7505d1217c8b96737e8dd96f41395f7d5bf674c Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 11:58:59 +0200 Subject: [PATCH 083/121] chore: remove old php versions from ci --- .github/workflows/test-build.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 38b8644b..ca820259 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -12,7 +12,7 @@ jobs: steps: - uses: shivammathur/setup-php@v2 with: - php-version: '8.1' + php-version: '8.3' coverage: none - uses: actions/checkout@master @@ -29,11 +29,11 @@ jobs: strategy: fail-fast: false matrix: - php-version: ["8.2", "8.3"] + php-version: ["8.3"] composer-flags: [""] name: [""] include: - - php-version: 8.1 + - php-version: 8.3 composer-flags: "--prefer-lowest" name: "(prefer lowest dependencies)" From 90870e63571f9a6b047e77af48c0852c7ff850e9 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 12:23:20 +0200 Subject: [PATCH 084/121] chore: clean changelog --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64e0b869..841899ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,12 +9,10 @@ * Drop support for PHP <8.1 * Drop support for Symfony <5.4, and 6.0, 6.1, 6.2, 6.3 -* Upgrade from FlySystem v1 to v3 ### Internal * Update PHP-CS-Fixer to 3.62.0 -* Analyse code with PHPStan ## 07/06/2022 22:41 2.1.0 Add compatibility for PHP 8.0/8.1, Symfony 6 and other improvements From 1deb706bb03188d5ff0d7adddfacca3c61406995 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 12:25:43 +0200 Subject: [PATCH 085/121] chore: clean changelog correctly --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 841899ce..d6ba6976 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ ### Internal -* Update PHP-CS-Fixer to 3.62.0 +* Analyse code with PHPStan ## 07/06/2022 22:41 2.1.0 Add compatibility for PHP 8.0/8.1, Symfony 6 and other improvements From b77609ac00b178313216ed87f45366511acdb63e Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 13:19:55 +0200 Subject: [PATCH 086/121] chore: fix phpstan issues --- phpstan-baseline.neon | 5 ----- phpstan.neon | 2 ++ src/Cli/Command/RunCommand.php | 8 +++++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 1b309a64..1e98fab9 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -155,11 +155,6 @@ parameters: count: 1 path: src/Cli/Command/RunCommand.php - - - message: "#^Parameter \\#2 \\$basePath of static method Webmozart\\\\PathUtil\\\\Path\\:\\:makeAbsolute\\(\\) expects string, string\\|false given\\.$#" - count: 1 - path: src/Cli/Command/RunCommand.php - - message: "#^Property AcmePhp\\\\Cli\\\\Command\\\\RunCommand\\:\\:\\$config has no type specified\\.$#" count: 1 diff --git a/phpstan.neon b/phpstan.neon index 03a0bac5..17850572 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,6 +3,8 @@ includes: parameters: level: 7 + excludePaths: + - src/Core/vendor paths: - src diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index 25716128..daa13489 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -71,7 +71,13 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { - $this->config = $this->getConfig(Path::makeAbsolute($input->getArgument('config'), getcwd())); + + $cwd = getcwd(); + if ($cwd === false) { + throw new \RuntimeException('Failed to get current working directory'); + } + + $this->config = $this->getConfig(Path::makeAbsolute($input->getArgument('config'), $cwd)); $keyOption = $this->createKeyOption($this->config['key_type']); $this->register($this->config['contact_email'], $keyOption); From e38b4dd0f3073bac7c8122cb6ba32d2c0967e23a Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 13:21:19 +0200 Subject: [PATCH 087/121] chore(cs): fix cs --- src/Cli/Command/RunCommand.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index daa13489..a26d2a28 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -71,9 +71,8 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { - $cwd = getcwd(); - if ($cwd === false) { + if (false === $cwd) { throw new \RuntimeException('Failed to get current working directory'); } From ebf5df0483bb4e82876ea8e7b7d65a3751f4baf9 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 13:53:35 +0200 Subject: [PATCH 088/121] chore: dont use (string) cast if it can be avoided --- tests/Core/Http/SecureHttpClientTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Core/Http/SecureHttpClientTest.php b/tests/Core/Http/SecureHttpClientTest.php index fb20b080..4bffdf64 100644 --- a/tests/Core/Http/SecureHttpClientTest.php +++ b/tests/Core/Http/SecureHttpClientTest.php @@ -177,7 +177,7 @@ public function testRequestPayload() $this->assertEquals('POST', $request->getMethod()); $this->assertEquals('/acme/new-reg', ($request->getUri() instanceof Uri) ? $request->getUri()->getPath() : $request->getUri()); - $payload = @json_decode((string) $request->getBody(), true); + $payload = json_decode($request->getBody()->getContents(), true, JSON_THROW_ON_ERROR); $this->assertIsArray($payload); $this->assertArrayHasKey('protected', $payload); From 2ba32c98bb08f79c3b10df06d0d7e0cfb15c5fd5 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 13:57:10 +0200 Subject: [PATCH 089/121] chore(ci): use php 8.3 in test --- .github/workflows/test-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 71891768..5a5a6e4d 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -54,7 +54,7 @@ jobs: - php-version: 8.3 composer-flags: "--prefer-lowest" name: "(prefer lowest dependencies)" - - php-version: 8.1 + - php-version: 8.3 composer-flags: "--prefer-lowest" name: "(prefer lowest dependencies - EAB)" pebble_mode: eab From 632f74772cebaed65fd2e89a66085ae0bb236786 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 14:05:24 +0200 Subject: [PATCH 090/121] fix: update to lcobucci/jwt ^5.3 --- composer.json | 2 +- src/Core/composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index a169d46e..780f3824 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,7 @@ "consolidation/self-update": "^2.2", "guzzlehttp/guzzle": "^7.2", "guzzlehttp/psr7": "^1.0", - "lcobucci/jwt": "^4.1 || ^5.3", + "lcobucci/jwt": "^5.3", "league/flysystem": "^3.10", "league/flysystem-memory": "^3.10", "league/flysystem-sftp-v3": "^3.10", diff --git a/src/Core/composer.json b/src/Core/composer.json index 1e72f35d..615dc6a2 100644 --- a/src/Core/composer.json +++ b/src/Core/composer.json @@ -34,7 +34,7 @@ "acmephp/ssl": "^2.0", "guzzlehttp/guzzle": "^6.0|^7.0", "guzzlehttp/psr7": "^1.7|^2.1", - "lcobucci/jwt": "^3.3|^4.0", + "lcobucci/jwt": "^5.3", "psr/http-client": "^1.0", "psr/http-message": "^1.0", "psr/log": "^1.0|^2.0|^3.0", From 52564bb4d4ab8827dca0373bcc10b63afb94afe1 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 14:13:00 +0200 Subject: [PATCH 091/121] chore(sa): regenerate baseline --- phpstan-baseline.neon | 5 ----- 1 file changed, 5 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 1e98fab9..9088f242 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -405,11 +405,6 @@ parameters: count: 1 path: src/Core/Filesystem/Adapter/FlysystemLocalFactory.php - - - message: "#^Call to an undefined method Lcobucci\\\\JWT\\\\Signer\\\\Hmac\\\\Sha256\\:\\:getAlgorithmId\\(\\)\\.$#" - count: 1 - path: src/Core/Http/SecureHttpClient.php - - message: "#^Left side of && is always true\\.$#" count: 1 From 1e293154665945aa5c857fbd36196982c4cb66a3 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 14:38:03 +0200 Subject: [PATCH 092/121] fix: also drop monolog 2 for better typing --- CHANGELOG.md | 4 +- composer.json | 2 +- phpstan-baseline.neon | 90 ---------------------------- src/Cli/Command/AbstractCommand.php | 18 +++--- src/Cli/Monolog/ConsoleFormatter.php | 40 ++++++------- src/Cli/Monolog/ConsoleHandler.php | 50 ++++++---------- 6 files changed, 50 insertions(+), 154 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecb5a39f..d7251c4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,8 +17,8 @@ * Drop support for lcobucci/jwt < 5.3 * Drop support for guzzlehttp/psr7 < 2 * Upgrade from FlySystem v1 to v3 -* Drop support for monolog ^3 -* Drop support for psr/log ^2 || ^3 +* Drop support for monolog ^1 +* Drop support for psr/log ^1 ### Internal diff --git a/composer.json b/composer.json index 740d6071..f27d0a98 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,7 @@ "league/flysystem": "^3.10", "league/flysystem-memory": "^3.10", "league/flysystem-sftp-v3": "^3.10", - "monolog/monolog": "^2.0 || ^3", + "monolog/monolog": "^3", "psr/container": "^1.0", "psr/http-message": "^1.0", "psr/log": "^2 || ^3", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 9088f242..53c9bea1 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -15,31 +15,6 @@ parameters: count: 1 path: src/Cli/Command/AbstractCommand.php - - - message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:alert\\(\\) with return type void returns null but should not return anything\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - - - message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:critical\\(\\) with return type void returns null but should not return anything\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - - - message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:debug\\(\\) with return type void returns null but should not return anything\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - - - message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:emergency\\(\\) with return type void returns null but should not return anything\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - - - message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:error\\(\\) with return type void returns null but should not return anything\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:getCliLogger\\(\\) should return Psr\\\\Log\\\\LoggerInterface but returns object\\.$#" count: 1 @@ -50,71 +25,6 @@ parameters: count: 1 path: src/Cli/Command/AbstractCommand.php - - - message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:info\\(\\) with return type void returns null but should not return anything\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - - - message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:log\\(\\) with return type void returns null but should not return anything\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - - - message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:notice\\(\\) with return type void returns null but should not return anything\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - - - message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\AbstractCommand\\:\\:warning\\(\\) with return type void returns null but should not return anything\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - - - message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:alert\\(\\) \\(void\\) is used\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - - - message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:critical\\(\\) \\(void\\) is used\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - - - message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:debug\\(\\) \\(void\\) is used\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - - - message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:emergency\\(\\) \\(void\\) is used\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - - - message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:error\\(\\) \\(void\\) is used\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - - - message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:info\\(\\) \\(void\\) is used\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - - - message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:log\\(\\) \\(void\\) is used\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - - - message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:notice\\(\\) \\(void\\) is used\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - - - message: "#^Result of method Psr\\\\Log\\\\LoggerInterface\\:\\:warning\\(\\) \\(void\\) is used\\.$#" - count: 1 - path: src/Cli/Command/AbstractCommand.php - - message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\RevokeCommand\\:\\:execute\\(\\) should return int but empty return statement found\\.$#" count: 3 diff --git a/src/Cli/Command/AbstractCommand.php b/src/Cli/Command/AbstractCommand.php index ce208626..356561d4 100644 --- a/src/Cli/Command/AbstractCommand.php +++ b/src/Cli/Command/AbstractCommand.php @@ -133,46 +133,46 @@ private function initializeContainer() public function emergency($message, array $context = []) { - return $this->getCliLogger()->emergency($message, $context); + $this->getCliLogger()->emergency($message, $context); } public function alert($message, array $context = []) { - return $this->getCliLogger()->alert($message, $context); + $this->getCliLogger()->alert($message, $context); } public function critical($message, array $context = []) { - return $this->getCliLogger()->critical($message, $context); + $this->getCliLogger()->critical($message, $context); } public function error($message, array $context = []) { - return $this->getCliLogger()->error($message, $context); + $this->getCliLogger()->error($message, $context); } public function warning($message, array $context = []) { - return $this->getCliLogger()->warning($message, $context); + $this->getCliLogger()->warning($message, $context); } public function notice($message, array $context = []) { - return $this->getCliLogger()->notice($message, $context); + $this->getCliLogger()->notice($message, $context); } public function info($message, array $context = []) { - return $this->getCliLogger()->info($message, $context); + $this->getCliLogger()->info($message, $context); } public function debug($message, array $context = []) { - return $this->getCliLogger()->debug($message, $context); + $this->getCliLogger()->debug($message, $context); } public function log($level, $message, array $context = []) { - return $this->getCliLogger()->log($level, $message, $context); + $this->getCliLogger()->log($level, $message, $context); } } diff --git a/src/Cli/Monolog/ConsoleFormatter.php b/src/Cli/Monolog/ConsoleFormatter.php index 127e527b..8f8e63f6 100644 --- a/src/Cli/Monolog/ConsoleFormatter.php +++ b/src/Cli/Monolog/ConsoleFormatter.php @@ -12,7 +12,9 @@ namespace AcmePhp\Cli\Monolog; use Monolog\Formatter\LineFormatter; +use Monolog\Level; use Monolog\Logger; +use Monolog\LogRecord; /** * Formats incoming records for console output by coloring them depending on log level. @@ -25,37 +27,35 @@ */ class ConsoleFormatter extends LineFormatter { - public const SIMPLE_FORMAT = "%extra.start_tag%%message% %context% %extra%%extra.end_tag%\n"; + public const string SIMPLE_FORMAT = "%extra.start_tag%%message% %context% %extra%%extra.end_tag%\n"; public function __construct($format = null, $dateFormat = null, $allowInlineLineBreaks = false, $ignoreEmptyContextAndExtra = true) { parent::__construct($format, $dateFormat, $allowInlineLineBreaks, $ignoreEmptyContextAndExtra); } - /** - * @param LogRecord|array $record - */ - public function format($record): string + public function format(LogRecord $record): string { - if ($record['level'] >= Logger::ERROR) { - $extra['start_tag'] = ''; - $extra['end_tag'] = ''; - } elseif ($record['level'] >= Logger::WARNING) { - $extra['start_tag'] = ''; - $extra['end_tag'] = ''; - } elseif ($record['level'] >= Logger::NOTICE) { - $extra['start_tag'] = ''; - $extra['end_tag'] = ''; + if ($record->level->isHigherThan(Level::Error)) { + $start = ''; + $end = ''; + } elseif ($record->level->isHigherThan(Level::Warning)) { + $start = ''; + $end = ''; + } elseif ($record->level->isHigherThan(Level::Notice)) { + $start = ''; + $end = ''; } else { - $extra['start_tag'] = ''; - $extra['end_tag'] = ''; + $start = ''; + $end = ''; } - $record['extra'] = [ - ...$record['extra'], - ...$extra, + $record->extra = [ + ...$record->extra, + 'start_tag' => $start, + 'end_tag' => $end, ]; - return dump(parent::format($record)); + return parent::format($record); } } diff --git a/src/Cli/Monolog/ConsoleHandler.php b/src/Cli/Monolog/ConsoleHandler.php index 6c06608d..f15a2a3e 100644 --- a/src/Cli/Monolog/ConsoleHandler.php +++ b/src/Cli/Monolog/ConsoleHandler.php @@ -13,7 +13,9 @@ use Monolog\Formatter\FormatterInterface; use Monolog\Handler\AbstractProcessingHandler; +use Monolog\Level; use Monolog\Logger; +use Monolog\LogRecord; use Symfony\Component\Console\Output\OutputInterface; /** @@ -28,19 +30,14 @@ class ConsoleHandler extends AbstractProcessingHandler { /** - * @var OutputInterface|null + * @var array */ - private $output; - - /** - * @var array - */ - private $verbosityLevelMap = [ - OutputInterface::VERBOSITY_QUIET => Logger::ERROR, - OutputInterface::VERBOSITY_NORMAL => Logger::INFO, - OutputInterface::VERBOSITY_VERBOSE => Logger::INFO, - OutputInterface::VERBOSITY_VERY_VERBOSE => Logger::INFO, - OutputInterface::VERBOSITY_DEBUG => Logger::DEBUG, + private array $verbosityLevelMap = [ + OutputInterface::VERBOSITY_QUIET => Level::Error, + OutputInterface::VERBOSITY_NORMAL => Level::Info, + OutputInterface::VERBOSITY_VERBOSE => Level::Info, + OutputInterface::VERBOSITY_VERY_VERBOSE => Level::Info, + OutputInterface::VERBOSITY_DEBUG => Level::Info, ]; /** @@ -52,29 +49,21 @@ class ConsoleHandler extends AbstractProcessingHandler * @param array $verbosityLevelMap Array that maps the OutputInterface verbosity to a minimum logging * level (leave empty to use the default mapping) */ - public function __construct(?OutputInterface $output = null, bool $bubble = true, array $verbosityLevelMap = []) + public function __construct(private OutputInterface|null $output = null, bool $bubble = true, array $verbosityLevelMap = []) { - parent::__construct(Logger::DEBUG, $bubble); + parent::__construct(Level::Debug, $bubble); - $this->output = $output; - - if ($verbosityLevelMap) { + if ($verbosityLevelMap !== []) { $this->verbosityLevelMap = $verbosityLevelMap; } } - /** - * @param LogRecord|array $record - */ - public function isHandling($record): bool + public function isHandling(LogRecord $record): bool { return $this->updateLevel() && parent::isHandling($record); } - /** - * @param LogRecord|array $record - */ - public function handle($record): bool + public function handle(LogRecord $record): bool { // we have to update the logging level each time because the verbosity of the // console output might have changed in the meantime (it is not immutable) @@ -86,7 +75,7 @@ public function handle($record): bool * * @param OutputInterface $output The console output to use */ - public function setOutput(OutputInterface $output) + public function setOutput(OutputInterface $output): void { $this->output = $output; } @@ -101,10 +90,7 @@ public function close(): void parent::close(); } - /** - * @param LogRecord|array $record - */ - protected function write($record): void + protected function write(LogRecord|array $record): void { $this->output->write((string) $record['formatted']); } @@ -122,7 +108,7 @@ protected function getDefaultFormatter(): FormatterInterface * * @return bool Whether the handler is enabled and verbosity is not set to quiet */ - private function updateLevel() + private function updateLevel(): bool { if (null === $this->output) { return false; @@ -132,7 +118,7 @@ private function updateLevel() if (isset($this->verbosityLevelMap[$verbosity])) { $this->setLevel($this->verbosityLevelMap[$verbosity]); } else { - $this->setLevel(Logger::DEBUG); + $this->setLevel(Level::Debug); } return true; From 86a5615662b4358c199adfdf90a258d965c65515 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 14:49:34 +0200 Subject: [PATCH 093/121] fix: log level coloring using LogRecord checks --- src/Cli/Monolog/ConsoleFormatter.php | 6 +++--- src/Cli/Monolog/ConsoleHandler.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Cli/Monolog/ConsoleFormatter.php b/src/Cli/Monolog/ConsoleFormatter.php index 8f8e63f6..a740219e 100644 --- a/src/Cli/Monolog/ConsoleFormatter.php +++ b/src/Cli/Monolog/ConsoleFormatter.php @@ -36,13 +36,13 @@ public function __construct($format = null, $dateFormat = null, $allowInlineLine public function format(LogRecord $record): string { - if ($record->level->isHigherThan(Level::Error)) { + if (Level::Error->includes($record->level)) { $start = ''; $end = ''; - } elseif ($record->level->isHigherThan(Level::Warning)) { + } elseif (Level::Warning->includes($record->level)) { $start = ''; $end = ''; - } elseif ($record->level->isHigherThan(Level::Notice)) { + } elseif (Level::Notice->includes($record->level)) { $start = ''; $end = ''; } else { diff --git a/src/Cli/Monolog/ConsoleHandler.php b/src/Cli/Monolog/ConsoleHandler.php index f15a2a3e..716426d7 100644 --- a/src/Cli/Monolog/ConsoleHandler.php +++ b/src/Cli/Monolog/ConsoleHandler.php @@ -30,7 +30,7 @@ class ConsoleHandler extends AbstractProcessingHandler { /** - * @var array + * @var array */ private array $verbosityLevelMap = [ OutputInterface::VERBOSITY_QUIET => Level::Error, From 507a60ee290d2f25c787a6fc2570891ad66ad66d Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 14:51:25 +0200 Subject: [PATCH 094/121] chore(cs): manually fix cs --- src/Cli/Monolog/ConsoleFormatter.php | 2 +- src/Cli/Monolog/ConsoleHandler.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Cli/Monolog/ConsoleFormatter.php b/src/Cli/Monolog/ConsoleFormatter.php index a740219e..0c713585 100644 --- a/src/Cli/Monolog/ConsoleFormatter.php +++ b/src/Cli/Monolog/ConsoleFormatter.php @@ -37,7 +37,7 @@ public function __construct($format = null, $dateFormat = null, $allowInlineLine public function format(LogRecord $record): string { if (Level::Error->includes($record->level)) { - $start = ''; + $start = ''; $end = ''; } elseif (Level::Warning->includes($record->level)) { $start = ''; diff --git a/src/Cli/Monolog/ConsoleHandler.php b/src/Cli/Monolog/ConsoleHandler.php index 716426d7..59b1a78a 100644 --- a/src/Cli/Monolog/ConsoleHandler.php +++ b/src/Cli/Monolog/ConsoleHandler.php @@ -49,11 +49,11 @@ class ConsoleHandler extends AbstractProcessingHandler * @param array $verbosityLevelMap Array that maps the OutputInterface verbosity to a minimum logging * level (leave empty to use the default mapping) */ - public function __construct(private OutputInterface|null $output = null, bool $bubble = true, array $verbosityLevelMap = []) + public function __construct(private ?OutputInterface $output = null, bool $bubble = true, array $verbosityLevelMap = []) { parent::__construct(Level::Debug, $bubble); - if ($verbosityLevelMap !== []) { + if ([] !== $verbosityLevelMap) { $this->verbosityLevelMap = $verbosityLevelMap; } } From d5bda73f321a47033b70356796b58b7982356657 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 14:51:50 +0200 Subject: [PATCH 095/121] chore(cs): remove unused import --- src/Cli/Monolog/ConsoleFormatter.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Cli/Monolog/ConsoleFormatter.php b/src/Cli/Monolog/ConsoleFormatter.php index 0c713585..2605bcde 100644 --- a/src/Cli/Monolog/ConsoleFormatter.php +++ b/src/Cli/Monolog/ConsoleFormatter.php @@ -13,7 +13,6 @@ use Monolog\Formatter\LineFormatter; use Monolog\Level; -use Monolog\Logger; use Monolog\LogRecord; /** From cc72046ab5ca24de561f3ddd4f6bbeaef4e8e310 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 14:53:05 +0200 Subject: [PATCH 096/121] chore(cs): remove unused import --- src/Cli/Monolog/ConsoleHandler.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Cli/Monolog/ConsoleHandler.php b/src/Cli/Monolog/ConsoleHandler.php index 59b1a78a..a6cec9e7 100644 --- a/src/Cli/Monolog/ConsoleHandler.php +++ b/src/Cli/Monolog/ConsoleHandler.php @@ -14,7 +14,6 @@ use Monolog\Formatter\FormatterInterface; use Monolog\Handler\AbstractProcessingHandler; use Monolog\Level; -use Monolog\Logger; use Monolog\LogRecord; use Symfony\Component\Console\Output\OutputInterface; From 32c2e96298bac9e48d5dd847b42fb88cf7f2d40a Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 15:32:02 +0200 Subject: [PATCH 097/121] feat: build docker container on every push to master and during tests --- .github/workflows/build-docker-master.yaml | 37 +++++++++ .github/workflows/test-build.yaml | 29 +++++++ Dockerfile | 93 ++++++---------------- 3 files changed, 90 insertions(+), 69 deletions(-) create mode 100644 .github/workflows/build-docker-master.yaml diff --git a/.github/workflows/build-docker-master.yaml b/.github/workflows/build-docker-master.yaml new file mode 100644 index 00000000..0ad0ce41 --- /dev/null +++ b/.github/workflows/build-docker-master.yaml @@ -0,0 +1,37 @@ +name: Continuous integration (release) +on: + push: + branches: + - master +jobs: + build_container: + name: Build container + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + tools: box + - uses: ramsey/composer-install@v3 + - name: Build PHAR + run: box compile + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3.3.0 + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push + uses: docker/build-push-action@v6.0.0 + with: + context: build/ + file: Dockerfile + push: true + tags: "ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }}" +permissions: + packages: write + contents: read diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 5a5a6e4d..fd2b2220 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -23,6 +23,35 @@ jobs: - name: Check coding style run: php php-cs-fixer.phar fix --dry-run --diff + test_docker_build: + name: Build container + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + tools: box + - uses: ramsey/composer-install@v3 + - name: Build PHAR + run: box compile + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3.3.0 + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push + uses: docker/build-push-action@v6.0.0 + with: + context: build/ + file: Dockerfile + push: false + tags: "ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }}" + phpstan: name: Test PHP ${{ matrix.php-version }} ${{ matrix.name }} runs-on: ubuntu-latest diff --git a/Dockerfile b/Dockerfile index b67283f0..a666b269 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,76 +1,31 @@ -FROM alpine:3.8 AS builder - -COPY --from=composer:latest /usr/bin/composer /usr/bin/composer - -WORKDIR /srv - -# Composer dependencies -RUN apk add --no-cache \ - php7 \ - php7-phar \ - php7-json \ - php7-iconv \ - php7-mbstring \ - php7-curl \ - php7-ctype \ - php7-opcache \ - php7-sockets \ - php7-openssl - -RUN composer global require "hirak/prestissimo" "jderusse/composer-warmup" - -RUN echo "opcache.enable_cli=1" > /etc/php7/conf.d/opcache.ini \ - && echo "opcache.file_cache='/tmp/opcache'" >> /etc/php7/conf.d/opcache.ini \ - && echo "opcache.file_update_protection=0" >> /etc/php7/conf.d/opcache.ini \ - && mkdir /tmp/opcache - -COPY composer.json /srv/ - -# App dependencies +FROM alpine:edge +ADD acmephp.phar /srv/bin/acme RUN apk add --no-cache \ - php7-simplexml \ - php7-dom \ - php7-tokenizer - -RUN composer install --no-dev --no-scripts --no-suggest --optimize-autoloader \ - && composer require "daverandom/libdns:^2.0.1" --no-scripts --no-suggest --optimize-autoloader - -COPY ./src /srv/src -COPY ./res /srv/res -COPY ./bin /srv/bin - -RUN composer warmup-opcode -- /srv - -# ============================= - -FROM alpine:3.8 + php83 \ + php83-opcache \ + php83-apcu \ + php83-openssl \ + php83-dom \ + php83-mbstring \ + php83-json \ + php83-ctype \ + php83-posix \ + php83-simplexml \ + php83-xmlwriter \ + php83-xml \ + php83-phar \ + php83-curl \ + php83-fileinfo \ + php83-sodium \ + ca-certificates WORKDIR /srv - -# PHP -RUN apk add --no-cache \ - php7 \ - php7-opcache \ - php7-apcu \ - php7-openssl \ - php7-dom \ - php7-mbstring \ - php7-json \ - php7-ctype \ - php7-posix \ - php7-simplexml \ - php7-xmlwriter \ - php7-xml \ - ca-certificates - -RUN echo "date.timezone = UTC" > /etc/php7/conf.d/symfony.ini \ - && echo "opcache.enable_cli=1" > /etc/php7/conf.d/opcache.ini \ - && echo "opcache.file_cache='/tmp/opcache'" >> /etc/php7/conf.d/opcache.ini \ - && echo "opcache.file_update_protection=0" >> /etc/php7/conf.d/opcache.ini \ +RUN chmod +x /srv/bin/acme +RUN echo "date.timezone = UTC" > /etc/php83/conf.d/symfony.ini \ + && echo "opcache.enable_cli=1" > /etc/php83/conf.d/opcache.ini \ + && echo "opcache.file_cache='/tmp/opcache'" >> /etc/php83/conf.d/opcache.ini \ + && echo "opcache.file_update_protection=0" >> /etc/php83/conf.d/opcache.ini \ && mkdir /tmp/opcache ENTRYPOINT ["/srv/bin/acme"] CMD ["list"] - -COPY --from=builder /tmp/opcache /tmp/opcache -COPY --from=builder /srv /srv From 3547c1fb672dfddf488be35b4f29a06fa4b89a66 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 15:41:57 +0200 Subject: [PATCH 098/121] fix: manually tag during ci so we can build the phar --- .github/workflows/test-build.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index fd2b2220..4234f8ff 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -28,6 +28,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - run: git tag ci - name: Setup PHP uses: shivammathur/setup-php@v2 with: From 18b5cf5de4c73ed1568a15d234f63a70fe6a2a8c Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 15:48:22 +0200 Subject: [PATCH 099/121] chore: test the docker container after building it --- .github/workflows/build-docker-master.yaml | 2 ++ .github/workflows/test-build.yaml | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-docker-master.yaml b/.github/workflows/build-docker-master.yaml index 0ad0ce41..32874ce0 100644 --- a/.github/workflows/build-docker-master.yaml +++ b/.github/workflows/build-docker-master.yaml @@ -32,6 +32,8 @@ jobs: file: Dockerfile push: true tags: "ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }}" + - name: Confirm that we can run ACME php via docker + run: docker run --rm -it ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }} permissions: packages: write contents: read diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 4234f8ff..49532e7d 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -52,7 +52,8 @@ jobs: file: Dockerfile push: false tags: "ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }}" - + - name: Confirm that we can run ACME php via docker + run: docker run --rm -it ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }} phpstan: name: Test PHP ${{ matrix.php-version }} ${{ matrix.name }} runs-on: ubuntu-latest From 2efd9255d38b31a3010287cf62cef3efab4d8cdd Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 15:49:36 +0200 Subject: [PATCH 100/121] chore: run container without tty --- .github/workflows/test-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 49532e7d..c9bba719 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -53,7 +53,7 @@ jobs: push: false tags: "ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }}" - name: Confirm that we can run ACME php via docker - run: docker run --rm -it ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }} + run: docker run --rm ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }} phpstan: name: Test PHP ${{ matrix.php-version }} ${{ matrix.name }} runs-on: ubuntu-latest From a2b798bb58567152a8a4805a9c7bf9f5905fd1c8 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 15:51:43 +0200 Subject: [PATCH 101/121] chore: use fixed tag name --- .github/workflows/test-build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index c9bba719..c945ba48 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -51,9 +51,9 @@ jobs: context: build/ file: Dockerfile push: false - tags: "ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }}" + tags: acmephp - name: Confirm that we can run ACME php via docker - run: docker run --rm ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }} + run: docker run --rm acmephp phpstan: name: Test PHP ${{ matrix.php-version }} ${{ matrix.name }} runs-on: ubuntu-latest From cd3cce8fdadf7770623247f7b7bee5de3e936f71 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 15:57:07 +0200 Subject: [PATCH 102/121] chore: use buildx load --- .github/workflows/build-docker-master.yaml | 1 + .github/workflows/test-build.yaml | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-docker-master.yaml b/.github/workflows/build-docker-master.yaml index 32874ce0..ddd81827 100644 --- a/.github/workflows/build-docker-master.yaml +++ b/.github/workflows/build-docker-master.yaml @@ -31,6 +31,7 @@ jobs: context: build/ file: Dockerfile push: true + load: true tags: "ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }}" - name: Confirm that we can run ACME php via docker run: docker run --rm -it ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }} diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index c945ba48..34a98d8e 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -51,9 +51,10 @@ jobs: context: build/ file: Dockerfile push: false - tags: acmephp + load: true + tags: ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }} - name: Confirm that we can run ACME php via docker - run: docker run --rm acmephp + run: docker run --rm ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }} phpstan: name: Test PHP ${{ matrix.php-version }} ${{ matrix.name }} runs-on: ubuntu-latest From f41b7c6dca4a89ccda1d786640d832a72fe00fa9 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 15:59:00 +0200 Subject: [PATCH 103/121] chore: use git tag with valid number --- .github/workflows/test-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 34a98d8e..34058470 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - run: git tag ci + - run: git tag "0.0.0" - name: Setup PHP uses: shivammathur/setup-php@v2 with: From e4adb4415dbe120b6a5c43c7ad55747fdb26ff31 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 16:07:26 +0200 Subject: [PATCH 104/121] chore: try full checkout instead of manual tag --- .github/workflows/build-docker-master.yaml | 2 ++ .github/workflows/test-build.yaml | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-docker-master.yaml b/.github/workflows/build-docker-master.yaml index ddd81827..171561da 100644 --- a/.github/workflows/build-docker-master.yaml +++ b/.github/workflows/build-docker-master.yaml @@ -9,6 +9,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Setup PHP uses: shivammathur/setup-php@v2 with: diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 34058470..4e5c020a 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -28,7 +28,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - run: git tag "0.0.0" + with: + fetch-depth: 0 - name: Setup PHP uses: shivammathur/setup-php@v2 with: From 74b4e47faaff47eeaf8089bbc5b5656264ef0753 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 15 Aug 2024 16:11:23 +0200 Subject: [PATCH 105/121] fix(ci): dont run with tty --- .github/workflows/build-docker-master.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-docker-master.yaml b/.github/workflows/build-docker-master.yaml index 171561da..ea000eb1 100644 --- a/.github/workflows/build-docker-master.yaml +++ b/.github/workflows/build-docker-master.yaml @@ -36,7 +36,7 @@ jobs: load: true tags: "ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }}" - name: Confirm that we can run ACME php via docker - run: docker run --rm -it ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }} + run: docker run --rm ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }} permissions: packages: write contents: read From e3d456010cdd0151539d0fa4ce0ce747983e72a3 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 10 Jan 2023 12:34:37 +0200 Subject: [PATCH 106/121] Rewrite Gandi soulver to support wildcard domains --- src/Core/Challenge/Dns/GandiSolver.php | 86 ++++++++++++++++---------- 1 file changed, 53 insertions(+), 33 deletions(-) diff --git a/src/Core/Challenge/Dns/GandiSolver.php b/src/Core/Challenge/Dns/GandiSolver.php index eb52c0a0..1d5e39a4 100644 --- a/src/Core/Challenge/Dns/GandiSolver.php +++ b/src/Core/Challenge/Dns/GandiSolver.php @@ -76,30 +76,26 @@ public function solve(AuthorizationChallenge $authorizationChallenge) public function solveAll(array $authorizationChallenges) { - Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); - - foreach ($authorizationChallenges as $authorizationChallenge) { - $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); - $recordName = $this->extractor->getRecordName($authorizationChallenge); - $recordValue = $this->extractor->getRecordValue($authorizationChallenge); - - $subDomain = \str_replace('.'.$topLevelDomain.'.', '', $recordName); - - $this->client->request( - 'PUT', - 'https://dns.api.gandi.net/api/v5/domains/'.$topLevelDomain.'/records/'.$subDomain.'/TXT', - [ - 'headers' => [ - 'X-Api-Key' => $this->apiKey, - ], - 'json' => [ - 'rrset_type' => 'TXT', - 'rrset_ttl' => 600, - 'rrset_name' => $subDomain, - 'rrset_values' => [$recordValue], - ], - ] - ); + $apiData = $this->prepareDataForAPICalls($authorizationChallenges); + + foreach ($apiData as $topLevelDomain => $subDomains) { + foreach ($subDomains as $subDomain => $recordValues) { + $this->client->request( + 'PUT', + 'https://dns.api.gandi.net/api/v5/domains/'.$topLevelDomain.'/records/'.$subDomain.'/TXT', + [ + 'headers' => [ + 'X-Api-Key' => $this->apiKey, + ], + 'json' => [ + 'rrset_type' => 'TXT', + 'rrset_ttl' => 600, + 'rrset_name' => $subDomain, + 'rrset_values' => $recordValues, + ], + ] + ); + } } } @@ -109,25 +105,49 @@ public function cleanup(AuthorizationChallenge $authorizationChallenge) } public function cleanupAll(array $authorizationChallenges) + { + $apiData = $this->prepareDataForAPICalls($authorizationChallenges); + + foreach ($apiData as $topLevelDomain => $subDomains) { + foreach (\array_keys($subDomains) as $subDomain) { + $this->client->request( + 'DELETE', + 'https://dns.api.gandi.net/api/v5/domains/'.$topLevelDomain.'/records/'.$subDomain.'/TXT', + [ + 'headers' => [ + 'X-Api-Key' => $this->apiKey, + ], + ] + ); + } + } + } + + public function prepareDataForAPICalls(array $authorizationChallenges) { Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); + $apiData = []; + foreach ($authorizationChallenges as $authorizationChallenge) { $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); $recordName = $this->extractor->getRecordName($authorizationChallenge); + $recordValue = $this->extractor->getRecordValue($authorizationChallenge); $subDomain = \str_replace('.'.$topLevelDomain.'.', '', $recordName); - $this->client->request( - 'DELETE', - 'https://dns.api.gandi.net/api/v5/domains/'.$topLevelDomain.'/records/'.$subDomain.'/TXT', - [ - 'headers' => [ - 'X-Api-Key' => $this->apiKey, - ], - ] - ); + if (!\array_key_exists($topLevelDomain, $apiData)) { + $apiData[$topLevelDomain] = []; + } + + if (!\array_key_exists($subDomain, $apiData[$topLevelDomain])) { + $apiData[$topLevelDomain][$subDomain] = []; + } + + $apiData[$topLevelDomain][$subDomain][] = $recordValue; } + + return $apiData; } protected function getTopLevelDomain(string $domain): string From 6e0f24da89e1c48f96e59ae17a27eabeeb722e74 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Fri, 16 Aug 2024 14:28:44 +0200 Subject: [PATCH 107/121] chore(ci): upload phar file artifact for testing --- .github/workflows/test-build.yaml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 4e5c020a..249bb14f 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -7,6 +7,28 @@ on: - master jobs: + create_phar: + name: Create phar + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + tools: box + - uses: ramsey/composer-install@v3 + - name: Build PHAR + run: box compile + - uses: actions/upload-artifact@v4 + with: + name: amcephp.phar + path: build/acmephp.phar + if-no-files-found: error + overwrite: true + php-cs: name: PHP-CS-Fixer runs-on: ubuntu-latest From 88adf0a160955558b68ed294c6d404631ff510a6 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Fri, 16 Aug 2024 14:36:37 +0200 Subject: [PATCH 108/121] chore(ci): put link in comment --- .github/workflows/pr-phar.yaml | 31 +++++++++++++++++++++++++++++++ .github/workflows/test-build.yaml | 2 ++ 2 files changed, 33 insertions(+) create mode 100644 .github/workflows/pr-phar.yaml diff --git a/.github/workflows/pr-phar.yaml b/.github/workflows/pr-phar.yaml new file mode 100644 index 00000000..bff425d6 --- /dev/null +++ b/.github/workflows/pr-phar.yaml @@ -0,0 +1,31 @@ +name: Create phar +on: + pull_request: +jobs: + create_phar: + name: Create phar + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + tools: box + - uses: ramsey/composer-install@v3 + - name: Build PHAR + run: box compile + - uses: actions/upload-artifact@v4 + with: + name: amcephp.phar + path: build/acmephp.phar + if-no-files-found: error + overwrite: true + - uses: mshick/add-pr-comment@v2 + with: + message: | + We have created a phar file for testing, find it here: ${{ steps.artifact-upload-step.outputs.artifact-url }} diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 249bb14f..e212242d 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -28,6 +28,8 @@ jobs: path: build/acmephp.phar if-no-files-found: error overwrite: true + - name: Output artifact ID + run: echo 'Artifact ID is ${{ steps.artifact-upload-step.outputs.artifact-id }}' php-cs: name: PHP-CS-Fixer From 2c15112322fb740d58b8e07b6f2694c30b189cd0 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Fri, 16 Aug 2024 14:39:32 +0200 Subject: [PATCH 109/121] chore(ci): clean up fix output name --- .github/workflows/pr-phar.yaml | 3 ++- .github/workflows/test-build.yaml | 24 ------------------------ 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/.github/workflows/pr-phar.yaml b/.github/workflows/pr-phar.yaml index bff425d6..95ad4598 100644 --- a/.github/workflows/pr-phar.yaml +++ b/.github/workflows/pr-phar.yaml @@ -20,6 +20,7 @@ jobs: - name: Build PHAR run: box compile - uses: actions/upload-artifact@v4 + id: artifact-upload with: name: amcephp.phar path: build/acmephp.phar @@ -28,4 +29,4 @@ jobs: - uses: mshick/add-pr-comment@v2 with: message: | - We have created a phar file for testing, find it here: ${{ steps.artifact-upload-step.outputs.artifact-url }} + We have created a phar file for testing, find it here: ${{ steps.artifact-upload.outputs.artifact-url }} diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index e212242d..4e5c020a 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -7,30 +7,6 @@ on: - master jobs: - create_phar: - name: Create phar - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.3' - tools: box - - uses: ramsey/composer-install@v3 - - name: Build PHAR - run: box compile - - uses: actions/upload-artifact@v4 - with: - name: amcephp.phar - path: build/acmephp.phar - if-no-files-found: error - overwrite: true - - name: Output artifact ID - run: echo 'Artifact ID is ${{ steps.artifact-upload-step.outputs.artifact-id }}' - php-cs: name: PHP-CS-Fixer runs-on: ubuntu-latest From 7f4e6406ba7f6870a73f23889580ccdc8542912a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 21 Aug 2024 11:00:41 +0200 Subject: [PATCH 110/121] Fix serializer --- .github/workflows/test-build.yaml | 2 +- phpstan-baseline.neon | 5 ----- src/Cli/Resources/services.xml | 4 ++-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test-build.yaml b/.github/workflows/test-build.yaml index 4e5c020a..e2290e95 100644 --- a/.github/workflows/test-build.yaml +++ b/.github/workflows/test-build.yaml @@ -57,7 +57,7 @@ jobs: - name: Confirm that we can run ACME php via docker run: docker run --rm ghcr.io/${{ github.repository }}/acmephp:master-${{ github.sha }} phpstan: - name: Test PHP ${{ matrix.php-version }} ${{ matrix.name }} + name: PHPStan runs-on: ubuntu-latest steps: - uses: shivammathur/setup-php@v2 diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 53c9bea1..8ea5f4dd 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -105,11 +105,6 @@ parameters: count: 2 path: src/Core/AcmeClient.php - - - message: "#^Offset 'finalize' does not exist on array\\|string\\.$#" - count: 2 - path: src/Core/AcmeClient.php - - message: "#^Offset 'identifier' does not exist on array\\|string\\.$#" count: 2 diff --git a/src/Cli/Resources/services.xml b/src/Cli/Resources/services.xml index d939d50b..b518bc07 100644 --- a/src/Cli/Resources/services.xml +++ b/src/Cli/Resources/services.xml @@ -53,14 +53,14 @@ - + - + From c8f3198250a15e6f483438142475e7b5bcbdf43c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 21 Aug 2024 11:31:23 +0200 Subject: [PATCH 111/121] Relax constraint on psr/http-message and psr/container --- CHANGELOG.md | 11 +++++++---- composer.json | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7251c4e..43cac332 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,16 +9,19 @@ * Add support for lcobucci/jwt ^5.3 * Add support for guzzlehttp/psr7 ^2.4 +* Add support for psr/http-message ^2 +* Add support for psr/container ^2 +* Add support for psr/log ^2 || ^3 ### BC Break -* Drop support for PHP <8.3 -* Drop support for Symfony <5.4, and 6.0, 6.1, 6.2, 6.3 +* Drop support for PHP < 8.3 +* Drop support for Symfony < 5.4, and 6.0, 6.1, 6.2, 6.3 * Drop support for lcobucci/jwt < 5.3 * Drop support for guzzlehttp/psr7 < 2 * Upgrade from FlySystem v1 to v3 -* Drop support for monolog ^1 -* Drop support for psr/log ^1 +* Drop support for monolog < 3 +* Drop support for psr/log < 2 ### Internal diff --git a/composer.json b/composer.json index f27d0a98..145c543f 100644 --- a/composer.json +++ b/composer.json @@ -52,8 +52,8 @@ "league/flysystem-memory": "^3.10", "league/flysystem-sftp-v3": "^3.10", "monolog/monolog": "^3", - "psr/container": "^1.0", - "psr/http-message": "^1.0", + "psr/container": "^1 || ^2", + "psr/http-message": "^1 || ^2", "psr/log": "^2 || ^3", "symfony/config": "^5.4.12 || ^6.4", "symfony/console": "^5.4.12 || ^6.4", From e63ef396cc67790e5a2a247ba016671b4bf196a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 21 Aug 2024 12:19:37 +0200 Subject: [PATCH 112/121] Fixed type hinting --- src/Cli/Monolog/ConsoleHandler.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Cli/Monolog/ConsoleHandler.php b/src/Cli/Monolog/ConsoleHandler.php index a6cec9e7..b1938b6f 100644 --- a/src/Cli/Monolog/ConsoleHandler.php +++ b/src/Cli/Monolog/ConsoleHandler.php @@ -22,7 +22,7 @@ * * Extracted from Symfony Monolog bridge. * - * @see https://github.com/symfony/monolog-bridge/edit/master/Handler/ConsoleHandler.php + * @see https://github.com/symfony/monolog-bridge/blob/7.1/Handler/ConsoleHandler.php * * @author Tobias Schultze */ @@ -40,8 +40,6 @@ class ConsoleHandler extends AbstractProcessingHandler ]; /** - * Constructor. - * * @param OutputInterface|null $output The console output to use (the handler remains disabled when passing null * until the output is set, e.g. by using console events) * @param bool $bubble Whether the messages that are handled can bubble up the stack @@ -71,8 +69,6 @@ public function handle(LogRecord $record): bool /** * Sets the console output to use for printing logs. - * - * @param OutputInterface $output The console output to use */ public function setOutput(OutputInterface $output): void { @@ -89,9 +85,9 @@ public function close(): void parent::close(); } - protected function write(LogRecord|array $record): void + protected function write(LogRecord $record): void { - $this->output->write((string) $record['formatted']); + $this->output->write((string) $record->formatted); } protected function getDefaultFormatter(): FormatterInterface From 423fe2f58ae6dbc41b2d222ece73e2bea7739473 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 21 Aug 2024 13:01:36 +0200 Subject: [PATCH 113/121] chore(ci): allow pr comment from fork prs --- .github/workflows/pr-phar.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-phar.yaml b/.github/workflows/pr-phar.yaml index 95ad4598..e4e986f1 100644 --- a/.github/workflows/pr-phar.yaml +++ b/.github/workflows/pr-phar.yaml @@ -1,6 +1,6 @@ name: Create phar on: - pull_request: + pull_request_target: jobs: create_phar: name: Create phar From 338f53a0476b261ada3814823d959d6d16611736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 21 Aug 2024 12:02:29 +0200 Subject: [PATCH 114/121] Add support for symfony ^7.1 --- CHANGELOG.md | 1 + composer.json | 22 +++++++++++----------- phpstan-baseline.neon | 5 ----- src/Cli/Serializer/PemEncoder.php | 8 ++++---- src/Cli/Serializer/PemNormalizer.php | 8 ++++---- 5 files changed, 20 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7251c4e..bca6f7bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * Add support for lcobucci/jwt ^5.3 * Add support for guzzlehttp/psr7 ^2.4 +* Add support for symfony ^7.1 ### BC Break diff --git a/composer.json b/composer.json index f27d0a98..72fbc7b6 100644 --- a/composer.json +++ b/composer.json @@ -55,20 +55,20 @@ "psr/container": "^1.0", "psr/http-message": "^1.0", "psr/log": "^2 || ^3", - "symfony/config": "^5.4.12 || ^6.4", - "symfony/console": "^5.4.12 || ^6.4", - "symfony/dependency-injection": "^5.4.12 || ^6.4", - "symfony/filesystem": "^5.4.12 || ^6.4", - "symfony/serializer": "^5.4.12 || ^6.4", - "symfony/yaml": "^5.4.12 || ^6.4", + "symfony/config": "^5.4.12 || ^6.4 || ^7.1", + "symfony/console": "^5.4.12 || ^6.4 || ^7.1", + "symfony/dependency-injection": "^5.4.12 || ^6.4 || ^7.1", + "symfony/filesystem": "^5.4.12 || ^6.4 || ^7.1", + "symfony/serializer": "^5.4.12 || ^6.4 || ^7.1", + "symfony/yaml": "^5.4.12 || ^6.4 || ^7.1", "webmozart/assert": "^1.0" }, "require-dev": { - "phpstan/phpstan": "^1.11", - "symfony/finder": "^5.4.12 || ^6.4", - "symfony/phpunit-bridge": "^5.4.12 || ^6.4", - "symfony/property-access": "^5.4.12 || ^6.4", - "symfony/var-dumper": "^5.4.12 || ^6.4" + "phpstan/phpstan": "^1.11.11", + "symfony/finder": "^5.4.12 || ^6.4 || ^7.1", + "symfony/phpunit-bridge": "^5.4.12 || ^6.4 || ^7.1", + "symfony/property-access": "^5.4.12 || ^6.4 || ^7.1", + "symfony/var-dumper": "^5.4.12 || ^6.4 || ^7.1" }, "autoload": { "psr-4": { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 8ea5f4dd..73d04f7c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -85,11 +85,6 @@ parameters: count: 1 path: src/Cli/Repository/Repository.php - - - message: "#^Method AcmePhp\\\\Cli\\\\Serializer\\\\PemNormalizer\\:\\:normalize\\(\\) return type with generic class ArrayObject does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/Cli/Serializer/PemNormalizer.php - - message: "#^Method AcmePhp\\\\Core\\\\AcmeClient\\:\\:registerAccount\\(\\) should return array but returns array\\|string\\.$#" count: 1 diff --git a/src/Cli/Serializer/PemEncoder.php b/src/Cli/Serializer/PemEncoder.php index e1f0a459..94e52613 100644 --- a/src/Cli/Serializer/PemEncoder.php +++ b/src/Cli/Serializer/PemEncoder.php @@ -21,22 +21,22 @@ class PemEncoder implements EncoderInterface, DecoderInterface { public const FORMAT = 'pem'; - public function encode($data, $format, array $context = []): string + public function encode(mixed $data, string $format, array $context = []): string { return trim($data)."\n"; } - public function decode($data, $format, array $context = []) + public function decode(string $data, string $format, array $context = []): string { return trim($data)."\n"; } - public function supportsEncoding($format): bool + public function supportsEncoding(string $format): bool { return self::FORMAT === $format; } - public function supportsDecoding($format) + public function supportsDecoding(string $format): bool { return self::FORMAT === $format; } diff --git a/src/Cli/Serializer/PemNormalizer.php b/src/Cli/Serializer/PemNormalizer.php index 0b9f8f57..984c022f 100644 --- a/src/Cli/Serializer/PemNormalizer.php +++ b/src/Cli/Serializer/PemNormalizer.php @@ -21,22 +21,22 @@ */ class PemNormalizer implements NormalizerInterface, DenormalizerInterface { - public function normalize($object, ?string $format = null, array $context = []) + public function normalize(mixed $object, ?string $format = null, array $context = []): string { return $object->getPEM(); } - public function denormalize($data, $class, ?string $format = null, array $context = []) + public function denormalize(mixed $data, string $class, ?string $format = null, array $context = []): object { return new $class($data); } - public function supportsNormalization($data, ?string $format = null) + public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool { return \is_object($data) && ($data instanceof Certificate || $data instanceof Key); } - public function supportsDenormalization($data, $type, ?string $format = null) + public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool { return \is_string($data); } From 26e64e54fb8bc2f1a3e453c815811d8e5256043d Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Wed, 21 Aug 2024 14:57:02 +0200 Subject: [PATCH 115/121] Update the badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ec35b55..114963dd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Acme PHP ======== -[![Build Status](https://img.shields.io/github/workflow/status/acmephp/acmephp/Test%20and%20build?style=flat-square)](https://github.com/acmephp/acmephp/actions?query=branch%3Amaster+workflow%3A%22Test+and+build%22) +[![Build Status](https://img.shields.io/github/actions/workflow/status/acmephp/acmephp/test-build.yaml?branch=master&style=flat-square)](https://github.com/acmephp/acmephp/actions/workflows/test-build.yaml?query=branch%3Amaster) [![Packagist Version](https://img.shields.io/packagist/v/acmephp/acmephp.svg?style=flat-square)](https://packagist.org/packages/acmephp/acmephp) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) From 69f210bc2f7f698c46c9721cfac794fbe8b52621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 21 Aug 2024 16:23:10 +0200 Subject: [PATCH 116/121] Sync all composer.json --- src/Core/composer.json | 9 ++++----- src/Ssl/composer.json | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Core/composer.json b/src/Core/composer.json index 615dc6a2..80f67104 100644 --- a/src/Core/composer.json +++ b/src/Core/composer.json @@ -32,12 +32,11 @@ "ext-json": "*", "ext-openssl": "*", "acmephp/ssl": "^2.0", - "guzzlehttp/guzzle": "^6.0|^7.0", - "guzzlehttp/psr7": "^1.7|^2.1", + "guzzlehttp/guzzle": "^7.2", + "guzzlehttp/psr7": "^2.4.5", "lcobucci/jwt": "^5.3", - "psr/http-client": "^1.0", - "psr/http-message": "^1.0", - "psr/log": "^1.0|^2.0|^3.0", + "psr/http-message": "^1 || ^2", + "psr/log": "^2 || ^3", "webmozart/assert": "^1.0" }, "autoload": { diff --git a/src/Ssl/composer.json b/src/Ssl/composer.json index da51c8ec..57b41d61 100644 --- a/src/Ssl/composer.json +++ b/src/Ssl/composer.json @@ -32,7 +32,7 @@ } }, "require": { - "php": ">=8.1", + "php": ">=8.3", "ext-hash": "*", "ext-openssl": "*", "lib-openssl": ">=0.9.8", From d8b27b17572791f5d60e2324f9cd62323b23b371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 22 Aug 2024 17:42:57 +0200 Subject: [PATCH 117/121] Reword a bit the changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4ec21f3..fc31099e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # CHANGELOG -## 3.0.0 (not released yet) +## 2.2.0 (not released yet) > [!NOTE] > From now on, a particular attention will be given to provide a nice changelog. @@ -14,7 +14,7 @@ * Add support for psr/container ^2 * Add support for psr/log ^2 || ^3 -### BC Break +### End of support * Drop support for PHP < 8.3 * Drop support for Symfony < 5.4, and 6.0, 6.1, 6.2, 6.3 From 70f32569b5fb8b88727a51699bfc49294b5cf64b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 13 Nov 2024 10:33:35 +0100 Subject: [PATCH 118/121] Add support for consolidation/self-update ^2.2 || ^3 This unlock full support for SF 7.1 --- CHANGELOG.md | 1 + composer.json | 2 +- phpstan-baseline.neon | 5 ----- src/Cli/Application.php | 11 ++++++++++- src/Cli/Command/AbstractCommand.php | 2 +- src/Cli/Command/Helper/DistinguishedNameHelper.php | 2 +- src/Cli/Command/RevokeCommand.php | 10 +++++----- src/Cli/Command/RunCommand.php | 4 ++-- src/Cli/Command/StatusCommand.php | 4 ++-- src/Cli/Configuration/DomainConfiguration.php | 2 +- 10 files changed, 24 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc31099e..004e9c23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * Add support for psr/http-message ^2 * Add support for psr/container ^2 * Add support for psr/log ^2 || ^3 +* Add support for consolidation/self-update ^2.2 || ^3 ### End of support diff --git a/composer.json b/composer.json index 873d5822..a6736502 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,7 @@ "alibabacloud/cdn": "^1.7", "alibabacloud/wafopenapi": "^1.7", "aws/aws-sdk-php": "^3.38", - "consolidation/self-update": "^2.2", + "consolidation/self-update": "^2.2 || ^3", "guzzlehttp/guzzle": "^7.2", "guzzlehttp/psr7": "^2.4.5", "lcobucci/jwt": "^5.3", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 73d04f7c..24d4273b 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -25,11 +25,6 @@ parameters: count: 1 path: src/Cli/Command/AbstractCommand.php - - - message: "#^Method AcmePhp\\\\Cli\\\\Command\\\\RevokeCommand\\:\\:execute\\(\\) should return int but empty return statement found\\.$#" - count: 3 - path: src/Cli/Command/RevokeCommand.php - - message: "#^Access to an undefined property object\\:\\:\\$eab_hmac_key\\.$#" count: 2 diff --git a/src/Cli/Application.php b/src/Cli/Application.php index 7dbb087b..58ecbb8e 100644 --- a/src/Cli/Application.php +++ b/src/Cli/Application.php @@ -16,6 +16,7 @@ use AcmePhp\Cli\Command\RunCommand; use AcmePhp\Cli\Command\StatusCommand; use SelfUpdate\SelfUpdateCommand; +use SelfUpdate\SelfUpdateManager; use Symfony\Component\Console\Application as BaseApplication; use Symfony\Component\Console\Helper\HelperSet; use Symfony\Component\Filesystem\Path; @@ -41,11 +42,19 @@ protected function getDefaultCommands(): array { $version = explode('@', $this->getVersion())[0]; + if (class_exists(SelfUpdateManager::class)) { + $selfUpdateCommand = new SelfUpdateCommand(new SelfUpdateManager($this->getName(), '' === $version ? '0.0.0' : $version, 'acmephp/acmephp')); + } else { + // Support for older versions of the self-update package + // @phpstan-ignore-next-line + $selfUpdateCommand = new SelfUpdateCommand($this->getName(), '' === $version ? '0.0.0' : $version, 'acmephp/acmephp'); + } + return array_merge(parent::getDefaultCommands(), [ new RunCommand(), new RevokeCommand(), new StatusCommand(), - new SelfUpdateCommand($this->getName(), '' === $version ? '0.0.0' : $version, 'acmephp/acmephp'), + $selfUpdateCommand, ]); } diff --git a/src/Cli/Command/AbstractCommand.php b/src/Cli/Command/AbstractCommand.php index 356561d4..8d27d6e6 100644 --- a/src/Cli/Command/AbstractCommand.php +++ b/src/Cli/Command/AbstractCommand.php @@ -47,7 +47,7 @@ abstract class AbstractCommand extends Command */ private $container; - protected function initialize(InputInterface $input, OutputInterface $output) + protected function initialize(InputInterface $input, OutputInterface $output): void { $this->input = $input; $this->output = $output; diff --git a/src/Cli/Command/Helper/DistinguishedNameHelper.php b/src/Cli/Command/Helper/DistinguishedNameHelper.php index 5b0452b7..daa964da 100644 --- a/src/Cli/Command/Helper/DistinguishedNameHelper.php +++ b/src/Cli/Command/Helper/DistinguishedNameHelper.php @@ -23,7 +23,7 @@ */ class DistinguishedNameHelper extends Helper { - public function getName() + public function getName(): string { return 'distinguished_name'; } diff --git a/src/Cli/Command/RevokeCommand.php b/src/Cli/Command/RevokeCommand.php index 18e4e227..c6e0fa02 100644 --- a/src/Cli/Command/RevokeCommand.php +++ b/src/Cli/Command/RevokeCommand.php @@ -21,7 +21,7 @@ class RevokeCommand extends AbstractCommand { - protected function configure() + protected function configure(): void { $reasons = implode(PHP_EOL, RevocationReason::getFormattedReasons()); @@ -41,7 +41,7 @@ protected function configure() ->setHelp('The %command.name% command revoke a previously obtained certificate for a given domain'); } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { if (!isset(Application::PROVIDERS[$this->input->getOption('provider')])) { throw new \InvalidArgumentException('Invalid provider, supported: '.implode(', ', Application::PROVIDERS)); @@ -58,13 +58,13 @@ protected function execute(InputInterface $input, OutputInterface $output) } catch (\InvalidArgumentException $e) { $this->error('Reason code must be one of: '.PHP_EOL.implode(PHP_EOL, RevocationReason::getFormattedReasons())); - return; + return 1; } if (!$repository->hasDomainCertificate($domain)) { $this->error('Certificate for '.$domain.' not found locally'); - return; + return 1; } $certificate = $repository->loadDomainCertificate($domain); @@ -74,7 +74,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } catch (CertificateRevocationException $e) { $this->error($e->getMessage()); - return; + return 1; } $this->notice('Certificate revoked successfully!'); diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index 87e1db3f..8bdd7b72 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -50,7 +50,7 @@ class RunCommand extends AbstractCommand private $config; - protected function configure() + protected function configure(): void { $this->setName('run') ->setDefinition( @@ -69,7 +69,7 @@ protected function configure() ->setHelp('The %command.name% challenge the domains, request the certificates and install them following a given configuration.'); } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $cwd = getcwd(); if (false === $cwd) { diff --git a/src/Cli/Command/StatusCommand.php b/src/Cli/Command/StatusCommand.php index 0838e0c4..a1cfbae7 100644 --- a/src/Cli/Command/StatusCommand.php +++ b/src/Cli/Command/StatusCommand.php @@ -23,7 +23,7 @@ */ class StatusCommand extends AbstractCommand { - protected function configure() + protected function configure(): void { $this->setName('status') ->setDescription('List all the certificates handled by Acme PHP') @@ -35,7 +35,7 @@ protected function configure() ); } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $repository = $this->getRepository(); diff --git a/src/Cli/Configuration/DomainConfiguration.php b/src/Cli/Configuration/DomainConfiguration.php index 251bde9f..4add46eb 100644 --- a/src/Cli/Configuration/DomainConfiguration.php +++ b/src/Cli/Configuration/DomainConfiguration.php @@ -20,7 +20,7 @@ */ class DomainConfiguration implements ConfigurationInterface { - public function getConfigTreeBuilder() + public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('acmephp'); if (method_exists(TreeBuilder::class, 'getRootNode')) { From c35219954f23274af863789bb55fb6f1e1054e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 13 Nov 2024 11:31:31 +0100 Subject: [PATCH 119/121] Lock phpstan/phpdoc-parser to v1 in test Symfony is not compatible with v2 yet --- phpunit.xml.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 46e21548..a05da4c4 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -14,7 +14,7 @@ - + From 577cd5bb0e1366748f4b4887ba87c2fe1d825d2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 13 Nov 2024 11:34:38 +0100 Subject: [PATCH 120/121] Add https://redirection.io/ as co-maintainer See https://github.com/acmephp/acmephp/pulls?q=author%3Alyrixx+ --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 114963dd..9247c2eb 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,10 @@ able to deeply integrate the management of your certificates directly in your ap by these features, have a look at the [acmephp/core](https://github.com/acmephp/core) and [acmephp/ssl](https://github.com/acmephp/ssl) libraries. -> Acme PHP is now maintained by https://zerossl.com. +> Acme PHP is now maintained by https://zerossl.com +> +> Other companies are welcome to contribute to the maintenance of the library. Thanks to: +> - https://redirection.io/ ## Why should I use Acme PHP when I have an official client? From 4baf15a020655c8fb67f2c19784f7c2ce2290856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Mon, 18 Nov 2024 10:46:45 +0100 Subject: [PATCH 121/121] Use GHA to run the subtree --- .github/workflows/gitsplit.yaml | 22 ++++++++++++++++++++++ .gitsplit.yml | 3 --- 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/gitsplit.yaml diff --git a/.github/workflows/gitsplit.yaml b/.github/workflows/gitsplit.yaml new file mode 100644 index 00000000..388e8a7a --- /dev/null +++ b/.github/workflows/gitsplit.yaml @@ -0,0 +1,22 @@ +name: gitsplit +on: + push: + branches: + - master + tags: + - '*' + release: + types: [published] + +jobs: + gitsplit: + runs-on: ubuntu-latest + steps: + - name: checkout + run: git clone https://github.com/acmephp/acmephp /home/runner/work/acmephp/acmephp && cd /home/runner/work/acmephp/acmephp + - name: Split repositories + uses: docker://jderusse/gitsplit:latest + with: + args: gitsplit + env: + GH_TOKEN: ${{ secrets.PRIVATE_TOKEN }} diff --git a/.gitsplit.yml b/.gitsplit.yml index 2c60b22e..fb72d98d 100644 --- a/.gitsplit.yml +++ b/.gitsplit.yml @@ -1,8 +1,5 @@ # See https://github.com/jderusse/docker-gitsplit -# Path to a cache directory Used to speed up the split over time by reusing git's objects -cache_url: "/cache/gitsplit" - splits: - prefix: "src/Core" target: "https://${GH_TOKEN}@github.com/acmephp/core.git"