diff --git a/.gitignore b/.gitignore index f98c043..5130aa1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -composer.phar +/.idea/ +/composer.phar /vendor/ /private/ /tests/functional/*.pdf diff --git a/README.md b/README.md index 081b90f..1477204 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # SetaPDF-Signer component modules for the GlobalSign Digital Signing Service. -This package offers modules for the [SetaPDF-Signer](https://www.setasign.com/signer) component that allow you to use the [Cloud-based Digital Signing Service](https://www.globalsign.com/en/digital-signatures/cloud/) by [GlobalSign](https://www.globalsign.com) to **digital sign and timestamp PDF documents in pure PHP**. +This package offers modules for the [SetaPDF-Signer](https://www.setasign.com/signer) component that allow you +to use the [Cloud-based Digital Signing Service](https://www.globalsign.com/en/digital-signatures/cloud/) by +[GlobalSign](https://www.globalsign.com) to **digital sign and timestamp PDF documents in pure PHP**. ## Requirements @@ -12,7 +14,29 @@ To use this package you need credentials for the GlobalSign Digital Signing Serv See "GlobalSign-Digital-Signing-Service-Guide 1.3.pdf" (or newer) for details. Ask a GlobalSign contact for this document. -This package is developed and tested on PHP >= 7.1. Requirements of the [SetaPDF-Signer](https://www.setasign.com/signer) component can be found [here](https://manuals.setasign.com/setapdf-signer-manual/getting-started/#index-1). +This package is developed and tested on PHP >= 7.1. Requirements of the [SetaPDF-Signer](https://www.setasign.com/signer) +component can be found [here](https://manuals.setasign.com/setapdf-signer-manual/getting-started/#index-1). + +We're using [PSR-17 (HTTP Factories)](https://www.php-fig.org/psr/psr-17/) and +[PSR-18 (HTTP Client)](https://www.php-fig.org/psr/psr-18/) for the requests. So you'll need an implementation of +these. We recommend using Guzzle. + +### For PHP 7.1 +``` + "require" : { + "guzzlehttp/guzzle": "^6.5", + "http-interop/http-factory-guzzle": "^1.0", + "mjelamanov/psr18-guzzle": "^1.3" + } +``` + +### For >= PHP 7.2 +``` + "require" : { + "guzzlehttp/guzzle": "^7.0", + "http-interop/http-factory-guzzle": "^1.0" + } +``` ## Installation Add following to your composer.json: @@ -20,7 +44,7 @@ Add following to your composer.json: ```json { "require": { - "setasign/seta-pdf-signer-addon-global-sign-dss": "^1.0" + "setasign/seta-pdf-signer-addon-global-sign-dss": "^2.0" }, "repositories": [ { @@ -31,10 +55,13 @@ Add following to your composer.json: } ``` -and execute `composer update`. You need to define the `repository` to evaluate the dependency to the [SetaPDF-Signer](https://www.setasign.com/signer) component (see [here](https://getcomposer.org/doc/faqs/why-can%27t-composer-load-repositories-recursively.md) for more details). +and execute `composer update`. You need to define the `repository` to evaluate the dependency to the +[SetaPDF-Signer](https://www.setasign.com/signer) component (see +[here](https://getcomposer.org/doc/faqs/why-can%27t-composer-load-repositories-recursively.md) for more details). ### Evaluation version -By default this packages depends on a licensed version of the [SetaPDF-Signer](https://www.setasign.com/signer) component. If you want to use it with an evaluation version please use following in your composer.json: +By default, this packages depends on a licensed version of the [SetaPDF-Signer](https://www.setasign.com/signer) component. +If you want to use it with an evaluation version please use following in your composer.json: ```json { @@ -50,38 +77,28 @@ By default this packages depends on a licensed version of the [SetaPDF-Signer](h } ``` -### Without Composer - -Make sure, that the [SetaPDF-Signer](https://www.setasign.com/signer) component is [installed](https://manuals.setasign.com/setapdf-core-manual/installation/#index-2) and its [autoloader is registered](https://manuals.setasign.com/setapdf-core-manual/getting-started/#index-1) correctly. - -Then simply require the `src/autoload.php` file or register this package in your own PSR-4 compatible autoload implementation: - -```php -$loader = new \Example\Psr4AutoloaderClass; -$loader->register(); -$loader->addNamespace('setasign\SetaPDF\Signer\Module\GlobalSign\Dss', 'path/to/src/'); -``` - ## Usage All classes in this package are located in the namespace `setasign\SetaPDF\Signer\Module\GlobalSign\Dss`. ### The `Client` class -There's a simple `Client` class which wraps the [REST API](https://downloads.globalsign.com/acton/media/2674/digital-signing-service-api-documentation) into simple PHP methods. It handles the authentication, requests and responses internally. For the communication with the API it uses [Guzzle](http://guzzlephp.org/). +There's a simple `Client` class which wraps the [REST API](https://downloads.globalsign.com/acton/media/2674/digital-signing-service-api-documentation) +into simple PHP methods. It handles the authentication, requests and responses internally. -The constructor of this class requires 3 arguments: +The constructor of this class requires the following arguments: -`$options` which are the [request options](http://docs.guzzlephp.org/en/stable/request-options.html) for Guzzle. To authenticate to the API endpoint it requires the `cert` (the client certificated issued by GlobalSign) and `ssl_key` (your private key) options. The `headers` and `body` options are set/overwritten internally. - -`$apiKey` is your API key received from GlobalSign. - -`$apiSecret` is the secret to your API key received from GlobalSign. +- `$httpClient` PSR-18 HTTP Client implementation. +- `$requestFactory` PSR-17 HTTP Factory implementation. +- `$streamFactory` PSR-17 HTTP Factory implementation. +- `$apiKey` is your API key received from GlobalSign. +- `$apiSecret` is the secret to your API key received from GlobalSign. A common creation could look like: ```php $options = [ + 'http_errors' => false, // recommended for guzzle - because of PSR-18 'cert' => 'path/to/tls-cert.pem', 'ssl_key' => 'path/to/private/key.pem' ]; @@ -89,7 +106,13 @@ $options = [ $apiKey = 'xxxxxxxxxxxxxxxx'; $apiSecret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; -$client = new Dss\Client($options, $apiKey, $apiSecret); +$httpClient = new GuzzleHttp\Client($options); +// if you are using php 7.0 or 7.1 +//$httpClient = new Mjelamanov\GuzzlePsr18\Client($httpClient); +$requestFactory = new Http\Factory\Guzzle\RequestFactory(); +$streamFactory = new Http\Factory\Guzzle\StreamFactory(); + +$client = new Dss\Client($httpClient, $requestFactory, $streamFactory, $apiKey, $apiSecret); ``` You can use this instance to e.g. query general information: @@ -100,13 +123,18 @@ $remainingSignatures = $client->getQuota(Dss\Client::TYPE_SIGNATURES); $signaturesCount = $client->getCount(Dss\Client::TYPE_SIGNATURES); ``` -To create a digital signature you need to create a signing certificate first which can be done with the `getIdentity()` method. The argument to this method can be an associative array as defined [here](https://downloads.globalsign.com/acton/media/2674/digital-signing-service-api-documentation#identity_post). The method will return an `Identity` instance which is nothing more than a data wrapper of the returned id, signing certificate and OCSP response. +To create a digital signature you need to create a signing certificate first which can be done with the `getIdentity()` +method. The argument to this method can be an associative array as defined +[here](https://downloads.globalsign.com/acton/media/2674/digital-signing-service-api-documentation#identity_post). +The method will return an `Identity` instance which is nothing more than a data wrapper of the returned id, signing +certificate and OCSP response. ```php $identity = $client->getIdentity(); ``` -This `Identity` needs to be forward to the signature module which internally passes it back to the `Dss\Client\sign()` method to get the final signature. It is also possible to use this method individually (just for completion): +This `Identity` needs to be forward to the signature module which internally passes it back to the `Dss\Client\sign()` +method to get the final signature. It is also possible to use this method individually (just for completion): ```php $signature = $client->sign($identity, hash('sha256', $data)); @@ -114,21 +142,20 @@ $signature = $client->sign($identity, hash('sha256', $data)); ### The `SignatureModule` class -This is the main signature module which can be used with the [SetaPDF-Signer](https://www.setasign.com/signer) component. Its constructor requires 4 arguments: - -`$signer` is the instance of the `\SetaPDF_Signer` class to which the module is passed afterwards. Internally [`$signer->setAllowSignatureContentLengthChange(false)`](https://manuals.setasign.com/api-reference/setapdf/c/SetaPDF.Signer#method_setAllowSignatureContentLengthChange) is called to prohibit redundant signature requests. - -`$client` needs to be the `Dss\Client` instance. +This is the main signature module which can be used with the [SetaPDF-Signer](https://www.setasign.com/signer) component. +Its constructor requires these arguments: -`$identity` a `Dss\Identity` instance. - -`$module` needs to be a [CMS](https://manuals.setasign.com/api-reference/setapdf/c/SetaPDF.Signer.Signature.Module.Cms) or [PAdES](https://manuals.setasign.com/api-reference/setapdf/c/SetaPDF.Signer.Signature.Module.Pades) signature module instance. It is used internally to create the CMS container. +- `$signer` is the instance of the `\SetaPDF_Signer` class to which the module is passed afterwards. Internally [`$signer->setAllowSignatureContentLengthChange(false)`](https://manuals.setasign.com/api-reference/setapdf/c/SetaPDF.Signer#method_setAllowSignatureContentLengthChange) is called to prohibit redundant signature requests. +- `$client` needs to be the `Dss\Client` instance. +- `$identity` a `Dss\Identity` instance. +- `$module` needs to be a [CMS](https://manuals.setasign.com/api-reference/setapdf/c/SetaPDF.Signer.Signature.Module.Cms) or [PAdES](https://manuals.setasign.com/api-reference/setapdf/c/SetaPDF.Signer.Signature.Module.Pades) signature module instance. It is used internally to create the CMS container. A simple complete signature process would look like this: ```php -// setup the client and identity +// set up the client and identity $options = [ + 'http_errors' => false, 'cert' => 'path/to/tls-cert.pem', 'ssl_key' => 'path/to/private/key.pem' ]; @@ -136,7 +163,13 @@ $options = [ $apiKey = 'xxxxxxxxxxxxxxxx'; $apiSecret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; -$client = new Dss\Client($options, $apiKey, $apiSecret); +$httpClient = new GuzzleHttp\Client($options); +// if you are using php 7.0 or 7.1 +//$httpClient = new Mjelamanov\GuzzlePsr18\Client($httpClient); +$requestFactory = new Http\Factory\Guzzle\RequestFactory(); +$streamFactory = new Http\Factory\Guzzle\StreamFactory(); + +$client = new Dss\Client($httpClient, $requestFactory, $streamFactory, $apiKey, $apiSecret); $identity = $client->getIdentity(); // now start the signature process @@ -178,7 +211,8 @@ $signer->timestamp(); ## Information about Tests -The test suite currently only comes with functional tests, which invoke **real service calls**! Keep in mind that these calls are deducted from your signature contingent and you should not execute these tests in an automated environment! +The test suite currently only comes with functional tests, which invoke **real service calls**! Keep in mind that these +calls are deducted from your signature contingent. You should not execute these tests in an automated environment!! To execute the tests, you need to create a folder in the root of this package with the following file: @@ -201,4 +235,4 @@ return [ ## License -This package is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). \ No newline at end of file +This package is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). diff --git a/composer.json b/composer.json index d5478f9..1116125 100644 --- a/composer.json +++ b/composer.json @@ -2,14 +2,6 @@ "name": "setasign/seta-pdf-signer-addon-global-sign-dss", "description": "A SetaPDF-Signer component signature module for the GlobalSign Digital Signing Service.", "type": "library", - "require": { - "php": "^7.1 || ^8.0", - "guzzlehttp/guzzle": "^6.0", - "setasign/setapdf-signer": "^2.31" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, "license": "MIT", "authors": [ { @@ -17,12 +9,21 @@ "email": "jan.slabon@setasign.com" } ], - "repositories": [ - { - "type": "composer", - "url": "https://www.setasign.com/downloads/" - } - ], + "require": { + "php": "^7.1 || ^8.0", + "ext-json": "*", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "setasign/setapdf-signer": "^2.31" + }, + "require-dev": { + "ext-openssl": "*", + "phpunit/phpunit": "^7.0", + "guzzlehttp/guzzle": "^6.5", + "http-interop/http-factory-guzzle": "^1.0", + "mjelamanov/psr18-guzzle": "^1.3" + }, + "autoload": { "psr-4": { "setasign\\SetaPDF\\Signer\\Module\\GlobalSign\\Dss\\": "src/" @@ -32,5 +33,12 @@ "psr-4": { "setasign\\SetaPDF\\Signer\\Module\\GlobalSign\\Dss\\tests\\": "tests/" } - } + }, + + "repositories": [ + { + "type": "composer", + "url": "https://www.setasign.com/downloads/" + } + ] } diff --git a/composer.lock b/composer.lock index c106ada..1776eef 100644 --- a/composer.lock +++ b/composer.lock @@ -4,50 +4,36 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "563f4dc444beb029414e3e2ff896711d", + "content-hash": "2fec98f8ce3d5c6264b46f3aa825f5d6", "packages": [ { - "name": "guzzlehttp/guzzle", - "version": "6.5.5", + "name": "psr/http-client", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e" + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", - "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", "shasum": "" }, "require": { - "ext-json": "*", - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.6.1", - "php": ">=5.5", - "symfony/polyfill-intl-idn": "^1.17.0" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.1" - }, - "suggest": { - "psr/log": "Required for using the Log middleware" + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.5-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "GuzzleHttp\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] + "Psr\\Http\\Client\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -55,157 +41,77 @@ ], "authors": [ { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "Guzzle is a PHP HTTP client library", - "homepage": "http://guzzlephp.org/", + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", "keywords": [ - "client", - "curl", - "framework", "http", - "http client", - "rest", - "web service" + "http-client", + "psr", + "psr-18" ], "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/6.5" + "source": "https://github.com/php-fig/http-client/tree/master" }, - "time": "2020-06-16T21:01:06+00:00" + "time": "2020-06-29T06:28:15+00:00" }, { - "name": "guzzlehttp/promises", - "version": "1.4.0", + "name": "psr/http-factory", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "60d379c243457e073cff02bc323a2a86cb355631" + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/60d379c243457e073cff02bc323a2a86cb355631", - "reference": "60d379c243457e073cff02bc323a2a86cb355631", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", "shasum": "" }, "require": { - "php": ">=5.5" - }, - "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.1" + "php": ">=7.0.0", + "psr/http-message": "^1.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.4.0" - }, - "time": "2020-09-30T07:37:28+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "1.7.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/53330f47520498c0ae1f61f7e2c90f55690c06a3", - "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0", - "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" - }, - "provide": { - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "ext-zlib": "*", - "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.7-dev" + "Psr\\Http\\Message\\": "src/" } }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Schultze", - "homepage": "https://github.com/Tobion" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "PSR-7 message implementation that also provides common utility methods", + "description": "Common interfaces for PSR-7 HTTP message factories", "keywords": [ + "factory", "http", "message", + "psr", + "psr-17", "psr-7", "request", - "response", - "stream", - "uri", - "url" + "response" ], "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.7.0" + "source": "https://github.com/php-fig/http-factory/tree/master" }, - "time": "2020-09-30T07:37:11+00:00" + "time": "2019-04-30T12:38:16+00:00" }, { "name": "psr/http-message", @@ -260,50 +166,6 @@ }, "time": "2016-08-06T14:39:51+00:00" }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, { "name": "setasign/setapdf-core", "version": "2.35.0.1507", @@ -348,45 +210,119 @@ "email": "support@setasign.com" }, "time": "2020-07-23T00:00:00+00:00" + } + ], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^8.0", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2020-11-10T18:47:58+00:00" }, { - "name": "symfony/polyfill-intl-idn", - "version": "v1.20.0", + "name": "guzzlehttp/guzzle", + "version": "6.5.5", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "3b75acd829741c768bc8b1f84eb33265e7cc5117" + "url": "https://github.com/guzzle/guzzle.git", + "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/3b75acd829741c768bc8b1f84eb33265e7cc5117", - "reference": "3b75acd829741c768bc8b1f84eb33265e7cc5117", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", + "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", "shasum": "" }, "require": { - "php": ">=7.1", - "symfony/polyfill-intl-normalizer": "^1.10", - "symfony/polyfill-php72": "^1.10" + "ext-json": "*", + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.6.1", + "php": ">=5.5", + "symfony/polyfill-intl-idn": "^1.17.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", + "psr/log": "^1.1" }, "suggest": { - "ext-intl": "For best performance" + "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "dev-master": "6.5-dev" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Intl\\Idn\\": "" + "GuzzleHttp\\": "src/" }, "files": [ - "bootstrap.php" + "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -395,86 +331,124 @@ ], "authors": [ { - "name": "Laurent Bassin", - "email": "laurent@bassin.info" - }, - { - "name": "Trevor Rowbotham", - "email": "trevor.rowbotham@pm.me" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" } ], - "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", - "homepage": "https://symfony.com", + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", "keywords": [ - "compatibility", - "idn", - "intl", - "polyfill", - "portable", - "shim" + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.20.0" + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/6.5" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" + "time": "2020-06-16T21:01:06+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "60d379c243457e073cff02bc323a2a86cb355631" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/60d379c243457e073cff02bc323a2a86cb355631", + "reference": "60d379c243457e073cff02bc323a2a86cb355631", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.4 || ^5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" } ], - "time": "2020-10-23T14:02:19+00:00" + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.4.0" + }, + "time": "2020-09-30T07:37:28+00:00" }, { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.20.0", + "name": "guzzlehttp/psr7", + "version": "1.7.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "727d1096295d807c309fb01a851577302394c897" + "url": "https://github.com/guzzle/psr7.git", + "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/727d1096295d807c309fb01a851577302394c897", - "reference": "727d1096295d807c309fb01a851577302394c897", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/53330f47520498c0ae1f61f7e2c90f55690c06a3", + "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=5.4.0", + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-zlib": "*", + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" }, "suggest": { - "ext-intl": "For best performance" + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "dev-master": "1.7-dev" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + "GuzzleHttp\\Psr7\\": "src/" }, "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" + "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -483,77 +457,62 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" } ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", + "description": "PSR-7 message implementation that also provides common utility methods", "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.20.0" + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/1.7.0" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2020-09-30T07:37:11+00:00" }, { - "name": "symfony/polyfill-php72", - "version": "v1.20.0", + "name": "http-interop/http-factory-guzzle", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "cede45fcdfabdd6043b3592e83678e42ec69e930" + "url": "https://github.com/http-interop/http-factory-guzzle.git", + "reference": "34861658efb9899a6618cef03de46e2a52c80fc0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cede45fcdfabdd6043b3592e83678e42ec69e930", - "reference": "cede45fcdfabdd6043b3592e83678e42ec69e930", + "url": "https://api.github.com/repos/http-interop/http-factory-guzzle/zipball/34861658efb9899a6618cef03de46e2a52c80fc0", + "reference": "34861658efb9899a6618cef03de46e2a52c80fc0", "shasum": "" }, "require": { - "php": ">=7.1" + "guzzlehttp/psr7": "^1.4.2", + "psr/http-factory": "^1.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.20-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } + "provide": { + "psr/http-factory-implementation": "^1.0" + }, + "require-dev": { + "http-interop/http-factory-tests": "^0.5", + "phpunit/phpunit": "^6.5" }, + "type": "library", "autoload": { "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - }, - "files": [ - "bootstrap.php" - ] + "Http\\Factory\\Guzzle\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -561,73 +520,52 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", - "homepage": "https://symfony.com", + "description": "An HTTP Factory using Guzzle PSR7", "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" + "factory", + "http", + "psr-17", + "psr-7" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.20.0" + "issues": "https://github.com/http-interop/http-factory-guzzle/issues", + "source": "https://github.com/http-interop/http-factory-guzzle/tree/master" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-23T14:02:19+00:00" - } - ], - "packages-dev": [ + "time": "2018-07-31T19:32:56+00:00" + }, { - "name": "doctrine/instantiator", - "version": "1.4.0", + "name": "mjelamanov/psr18-guzzle", + "version": "1.3.0", "source": { "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" + "url": "https://github.com/mjelamanov/psr18-guzzle.git", + "reference": "e06dbaf74107298a81c7b741b71c26c4b93948dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", + "url": "https://api.github.com/repos/mjelamanov/psr18-guzzle/zipball/e06dbaf74107298a81c7b741b71c26c4b93948dc", + "reference": "e06dbaf74107298a81c7b741b71c26c4b93948dc", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "guzzlehttp/guzzle": "^6.0", + "php": ">=7.0", + "psr/http-client": "^1.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" }, "require-dev": { - "doctrine/coding-standard": "^8.0", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + "Mjelamanov\\GuzzlePsr18\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -636,36 +574,22 @@ ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" + "name": "Mirlan Jelamanov", + "email": "mirlan.jelamanov@gmail.com" } ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "description": "A PSR-18 adapter for guzzle 6 client", "keywords": [ - "constructor", - "instantiate" + "Guzzle", + "http", + "http-client", + "psr-18" ], "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + "issues": "https://github.com/mjelamanov/psr18-guzzle/issues", + "source": "https://github.com/mjelamanov/psr18-guzzle/tree/1.3.0" }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2020-11-10T18:47:58+00:00" + "time": "2020-10-22T08:30:11+00:00" }, { "name": "myclabs/deep-copy", @@ -837,25 +761,25 @@ }, { "name": "phpdocumentor/reflection-common", - "version": "2.2.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/6568f4687e5b41b054365f9ae03fcb1ed5f2069b", + "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-2.x": "2.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -884,43 +808,47 @@ ], "support": { "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/master" }, - "time": "2020-06-27T09:03:43+00:00" + "time": "2020-04-27T09:25:28+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.2.2", + "version": "4.3.4", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" + "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/da3fd972d6bafd628114f7e7e036f45944b62e9c", + "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c", "shasum": "" }, "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" + "php": "^7.0", + "phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0", + "phpdocumentor/type-resolver": "~0.4 || ^1.0.0", + "webmozart/assert": "^1.0" }, "require-dev": { - "mockery/mockery": "~1.3.2" + "doctrine/instantiator": "^1.0.5", + "mockery/mockery": "^1.0", + "phpdocumentor/type-resolver": "0.4.*", + "phpunit/phpunit": "^6.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.x-dev" + "dev-master": "4.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "phpDocumentor\\Reflection\\": [ + "src/" + ] } }, "notification-url": "https://packagist.org/downloads/", @@ -931,44 +859,42 @@ { "name": "Mike van Riel", "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/release/4.x" }, - "time": "2020-09-03T19:13:55+00:00" + "time": "2019-12-28T18:55:12+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.4.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" + "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", + "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", + "php": "^7.1", "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-tokenizer": "*" + "ext-tokenizer": "^7.1", + "mockery/mockery": "~1", + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-1.x": "1.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -989,39 +915,39 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/0.7.2" }, - "time": "2020-09-17T18:55:26+00:00" + "time": "2019-08-22T18:11:29+00:00" }, { "name": "phpspec/prophecy", - "version": "1.12.1", + "version": "v1.10.3", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "8ce87516be71aae9b956f81906aaf0338e0d8a2d" + "reference": "451c3cd1418cf640de218914901e51b064abb093" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/8ce87516be71aae9b956f81906aaf0338e0d8a2d", - "reference": "8ce87516be71aae9b956f81906aaf0338e0d8a2d", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", + "reference": "451c3cd1418cf640de218914901e51b064abb093", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.1", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", + "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" }, "require-dev": { - "phpspec/phpspec": "^6.0", - "phpunit/phpunit": "^8.0 || ^9.0 <9.3" + "phpspec/phpspec": "^2.5 || ^3.2", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11.x-dev" + "dev-master": "1.10.x-dev" } }, "autoload": { @@ -1056,9 +982,9 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.12.1" + "source": "https://github.com/phpspec/prophecy/tree/v1.10.3" }, - "time": "2020-09-29T09:10:42+00:00" + "time": "2020-03-05T15:02:03+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1437,7 +1363,51 @@ "issues": "https://github.com/sebastianbergmann/phpunit/issues", "source": "https://github.com/sebastianbergmann/phpunit/tree/7.5.20" }, - "time": "2020-01-08T08:45:45+00:00" + "time": "2020-01-08T08:45:45+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -2182,25 +2152,272 @@ ], "time": "2020-10-23T14:02:19+00:00" }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "3b75acd829741c768bc8b1f84eb33265e7cc5117" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/3b75acd829741c768bc8b1f84eb33265e7cc5117", + "reference": "3b75acd829741c768bc8b1f84eb33265e7cc5117", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.20-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.20.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-23T14:02:19+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "727d1096295d807c309fb01a851577302394c897" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/727d1096295d807c309fb01a851577302394c897", + "reference": "727d1096295d807c309fb01a851577302394c897", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.20-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.20.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-23T14:02:19+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "cede45fcdfabdd6043b3592e83678e42ec69e930" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cede45fcdfabdd6043b3592e83678e42ec69e930", + "reference": "cede45fcdfabdd6043b3592e83678e42ec69e930", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.20-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php72/tree/v1.20.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-23T14:02:19+00:00" + }, { "name": "theseer/tokenizer", - "version": "1.2.0", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "75a63c33a8577608444246075ea0af0d052e452a" + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", - "reference": "75a63c33a8577608444246075ea0af0d052e452a", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" + "php": "^7.0" }, "type": "library", "autoload": { @@ -2224,13 +2441,7 @@ "issues": "https://github.com/theseer/tokenizer/issues", "source": "https://github.com/theseer/tokenizer/tree/master" }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2020-07-12T23:59:07+00:00" + "time": "2019-06-13T22:48:21+00:00" }, { "name": "webmozart/assert", @@ -2292,7 +2503,8 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^7.1 || ^8.0" + "php": "^7.1 || ^8.0", + "ext-json": "*" }, "platform-dev": [], "plugin-api-version": "2.0.0" diff --git a/src/Client.php b/src/Client.php index 76ef8e2..e21af4b 100644 --- a/src/Client.php +++ b/src/Client.php @@ -1,7 +1,7 @@ options = $options; + $this->httpClient = $httpClient; + $this->requestFactory = $requestFactory; + $this->streamFactory = $streamFactory; $this->apiKey = $apiKey; $this->apiSecret = $apiSecret; - $this->endPoint = $endpoint; + $this->endPoint = rtrim($endpoint, '/'); + } + + /** + * Helper method to handle errors in json_decode + * + * @param string $json + * @param bool $assoc + * @param int $depth + * @param int $options + * @return mixed + * @throws Exception + */ + protected function json_decode(string $json, bool $assoc = true, int $depth = 512, int $options = 0) + { + // Clear json_last_error() + \json_encode(null); - $this->client = new HttpClient(['base_uri' => $this->endPoint]); + $data = @\json_decode($json, $assoc, $depth, $options); + + if (\json_last_error() !== JSON_ERROR_NONE) { + throw new Exception(\sprintf( + 'Unable to decode JSON: %s', + \json_last_error_msg() + )); + } + + return $data; } /** @@ -83,26 +122,29 @@ public function __construct( * * @param bool $force * @return string - * @throws \GuzzleHttp\Exception\GuzzleException + * @throws ClientExceptionInterface If an error happens while processing the request. + * @throws Exception If the login fails. */ - public function login($force = false) + public function login(bool $force = false): string { if ($this->accessToken === null || $force) { - $options = $this->options; - $options['headers'] = [ - 'Content-Type' => 'application/json;charset=utf-8' - ]; - - $options['body'] = \json_encode([ - 'api_key' => $this->apiKey, - 'api_secret' => $this->apiSecret - ]); - - $response = $this->client->request('POST', '/login', $options); - - $result = \json_decode($response->getBody()->getContents()); - - $this->accessToken = $result->access_token; + $request = ( + $this->requestFactory->createRequest('POST', $this->endPoint . '/login') + ->withHeader('Content-Type', 'application/json;charset=utf-8') + ->withBody($this->streamFactory->createStream(\json_encode([ + 'api_key' => $this->apiKey, + 'api_secret' => $this->apiSecret + ]))) + ); + + $response = $this->httpClient->sendRequest($request); + if ($response->getStatusCode() !== 200) { + throw new Exception('Error on /login: ' . $response->getBody()); + } + + $result = $this->json_decode((string) $response->getBody()); + + $this->accessToken = $result['access_token']; } return $this->accessToken; @@ -111,74 +153,99 @@ public function login($force = false) /** * Query remaining quota of a specific type for the calling account. * - * @param $type + * @param string $type * @return int - * @throws \GuzzleHttp\Exception\GuzzleException + * @throws ClientExceptionInterface If an error happens while processing the request. + * @throws Exception */ - public function getQuota($type): int + public function getQuota(string $type): int { if (!\in_array($type, [self::TYPE_SIGNATURES, self::TYPE_TIMESTAMPS], true)) { throw new \InvalidArgumentException(sprintf('Unknow quota type: "%s".', $type)); } - $options = $this->options; - $options['headers'] = ['Authorization' => 'Bearer ' . $this->login()]; + $request = ( + $this->requestFactory->createRequest('GET', $this->endPoint . '/quotas/' . $type) + ->withHeader('Authorization', 'Bearer ' . $this->login()) + ); + + $response = $this->httpClient->sendRequest($request); + if ($response->getStatusCode() !== 200) { + throw new Exception('Error on getQuota(): ' . $response->getBody()); + } - $response = $this->client->request('GET', '/quotas/' . $type, $options); - return (int)\json_decode($response->getBody()->getContents())->value; + return (int) $this->json_decode((string) $response->getBody())['value']; } /** * Query the number of a specific type created by the calling account. * - * @param $type + * @param string $type * @return int - * @throws \GuzzleHttp\Exception\GuzzleException + * @throws ClientExceptionInterface If an error happens while processing the request. + * @throws Exception */ - public function getCount($type): int + public function getCount(string $type): int { if (!\in_array($type, [self::TYPE_SIGNATURES, self::TYPE_TIMESTAMPS, self::TYPE_IDENTITIES], true)) { throw new \InvalidArgumentException(sprintf('Unknow counter type: "%s".', $type)); } - $options = $this->options; - $options['headers'] = ['Authorization' => 'Bearer ' . $this->login()]; + $request = ( + $this->requestFactory->createRequest('GET', $this->endPoint . '/counters/' . $type) + ->withHeader('Authorization', 'Bearer ' . $this->login()) + ); - $response = $this->client->request('GET', '/counters/' . $type, $options); + $response = $this->httpClient->sendRequest($request); + if ($response->getStatusCode() !== 200) { + throw new Exception('Error on getCount(): ' . $response->getBody()); + } - return (int)\json_decode($response->getBody()->getContents())->value; + return (int) $this->json_decode((string) $response->getBody())['value']; } /** * Retrieve the certificate used to sign the identity requests. * * @return string - * @throws \GuzzleHttp\Exception\GuzzleException + * @throws ClientExceptionInterface If an error happens while processing the request. + * @throws Exception */ public function getCertificatePath(): string { - $options = $this->options; - $options['headers'] = ['Authorization' => 'Bearer ' . $this->login()]; - - $response = $this->client->request('GET', '/certificate_path', $options); + $request = ( + $this->requestFactory->createRequest('GET', $this->endPoint . '/certificate_path') + ->withHeader('Authorization', 'Bearer ' . $this->login()) + ); + + $response = $this->httpClient->sendRequest($request); + if ($response->getStatusCode() !== 200) { + throw new Exception('Error on getCertificatePath(): ' . $response->getBody()); + } - return \json_decode($response->getBody()->getContents())->path; + return $this->json_decode((string) $response->getBody())['path']; } /** * Retrieve the validation policy associated with the calling account. * - * @return \stdClass - * @throws \GuzzleHttp\Exception\GuzzleException + * @return array + * @throws ClientExceptionInterface If an error happens while processing the request. + * @throws Exception */ - public function getValidationPolicy(): \stdClass + public function getValidationPolicy(): array { - $options = $this->options; - $options['headers'] = ['Authorization' => 'Bearer ' . $this->login()]; - - $response = $this->client->request('GET', '/validationpolicy', $options); + $request = ( + $this->requestFactory->createRequest('GET', $this->endPoint . '/validationpolicy') + ->withHeader('Authorization', 'Bearer ' . $this->login()) + ); + + $response = $this->httpClient->sendRequest($request); + if ($response->getStatusCode() !== 200) { + throw new Exception('Error on getValidationPolicy(): ' . $response->getBody()); + } - return \json_decode($response->getBody()->getContents()); + return $this->json_decode($response->getBody()->getContents()); } /** @@ -186,23 +253,26 @@ public function getValidationPolicy(): \stdClass * * @param $identityData * @return Identity - * @throws \GuzzleHttp\Exception\GuzzleException + * @throws ClientExceptionInterface If an error happens while processing the request. + * @throws Exception */ public function getIdentity($identityData = null): Identity { - $options = $this->options; - $options['headers'] = [ - 'Content-Type' => 'application/json;charset=utf-8', - 'Authorization' => 'Bearer ' . $this->login() - ]; - - $options['body'] = \json_encode($identityData); - - $response = $this->client->request('POST', '/identity', $options); + $request = ( + $this->requestFactory->createRequest('POST', $this->endPoint . '/identity') + ->withHeader('Authorization', 'Bearer ' . $this->login()) + ->withHeader('Content-Type', 'application/json;charset=utf-8') + ->withBody($this->streamFactory->createStream(\json_encode($identityData))) + ); + + $response = $this->httpClient->sendRequest($request); + if ($response->getStatusCode() !== 200) { + throw new Exception('Error on getIdentity(): ' . $response->getBody()); + } - $data = \json_decode($response->getBody()->getContents()); + $data = $this->json_decode($response->getBody()->getContents()); - return new Identity($data->id, $data->signing_cert, $data->ocsp_response); + return new Identity($data['id'], $data['signing_cert'], $data['ocsp_response']); } /** @@ -210,54 +280,71 @@ public function getIdentity($identityData = null): Identity * * @param Identity $identity * @param string $digest - * @return mixed - * @throws \GuzzleHttp\Exception\GuzzleException + * @return string + * @throws ClientExceptionInterface If an error happens while processing the request. + * @throws Exception */ - public function sign(Identity $identity, $digest) + public function sign(Identity $identity, string $digest): string { - $options = $this->options; - $options['headers'] = [ - 'Authorization' => 'Bearer ' . $this->login() - ]; - - $response = $this->client->request('GET', '/identity/' . $identity->getId() . '/sign/' . $digest, $options); + $request = ( + $this->requestFactory->createRequest( + 'GET', + $this->endPoint . '/identity/' . $identity->getId() . '/sign/' . $digest + ) + ->withHeader('Authorization', 'Bearer ' . $this->login()) + ); + + $response = $this->httpClient->sendRequest($request); + if ($response->getStatusCode() !== 200) { + throw new Exception('Error on sign(): ' . $response->getBody()); + } - return \json_decode($response->getBody()->getContents())->signature; + return $this->json_decode((string) $response->getBody())['signature']; } /** * Retrieve timestamp token for digest * * @param string $digest - * @return mixed - * @throws \GuzzleHttp\Exception\GuzzleException + * @return string + * @throws ClientExceptionInterface If an error happens while processing the request. + * @throws Exception */ - public function timestamp($digest) + public function timestamp(string $digest): string { - $options = $this->options; - $options['headers'] = [ - 'Authorization' => 'Bearer ' . $this->login() - ]; - - $response = $this->client->request('GET', '/timestamp/' . $digest, $options); + $request = ( + $this->requestFactory->createRequest('GET', $this->endPoint . '/timestamp/' . $digest) + ->withHeader('Authorization', 'Bearer ' . $this->login()) + ); + + $response = $this->httpClient->sendRequest($request); + if ($response->getStatusCode() !== 200) { + throw new Exception('Error on timestamp(): ' . $response->getBody()); + } - return \json_decode($response->getBody()->getContents())->token; + return $this->json_decode((string) $response->getBody())['token']; } /** * Query the chain of trust for the certificates issued by the calling account and the revocation info for the * certificates in the chain * - * @return \stdClass - * @throws \GuzzleHttp\Exception\GuzzleException + * @return array + * @throws ClientExceptionInterface If an error happens while processing the request. + * @throws Exception */ - public function getTrustchain(): \stdClass + public function getTrustchain(): array { - $options = $this->options; - $options['headers'] = ['Authorization' => 'Bearer ' . $this->login()]; - - $response = $this->client->request('GET', '/trustchain', $options); + $request = ( + $this->requestFactory->createRequest('GET', $this->endPoint . '/trustchain') + ->withHeader('Authorization', 'Bearer ' . $this->login()) + ); + + $response = $this->httpClient->sendRequest($request); + if ($response->getStatusCode() !== 200) { + throw new Exception('Error on getTrustchain(): ' . $response->getBody()); + } - return \json_decode($response->getBody()->getContents()); + return $this->json_decode((string) $response->getBody()); } } diff --git a/src/Exception.php b/src/Exception.php index ef05d93..5bb38f1 100644 --- a/src/Exception.php +++ b/src/Exception.php @@ -1,7 +1,7 @@ id = $id; $this->signingCert = $signingCert; diff --git a/src/SignatureModule.php b/src/SignatureModule.php index 65bbf64..2e4819b 100644 --- a/src/SignatureModule.php +++ b/src/SignatureModule.php @@ -1,7 +1,7 @@ addRevocationInfo = (bool)$addRevocationInfo; + $this->addRevocationInfo = $addRevocationInfo; } /** @@ -84,18 +86,20 @@ public function setAddRevocationInfo($addRevocationInfo) * * @param \SetaPDF_Core_Reader_FilePath $tmpPath * @return string - * @throws \GuzzleHttp\Exception\GuzzleException + * @throws Exception + * @throws ClientExceptionInterface + * @throws \SetaPDF_Signer_Asn1_Exception * @throws \SetaPDF_Signer_Exception */ public function createSignature(\SetaPDF_Core_Reader_FilePath $tmpPath): string { $this->module->setCertificate($this->identity->getSigningCertificate()); $trustChain = $this->client->getTrustchain(); - $this->module->setExtraCertificates($trustChain->trustchain); + $this->module->setExtraCertificates($trustChain['trustchain']); if ($this->addRevocationInfo) { $this->module->addOcspResponse(\base64_decode($this->identity->getOcspResponse())); - foreach ($trustChain->ocsp_revocation_info as $ocspRevocationInfo) { + foreach ($trustChain['ocsp_revocation_info'] as $ocspRevocationInfo) { $this->module->addOcspResponse(\base64_decode($ocspRevocationInfo)); } } @@ -105,7 +109,7 @@ public function createSignature(\SetaPDF_Core_Reader_FilePath $tmpPath): string $this->module->setSignatureValue(\SetaPDF_Core_Type_HexString::hex2str($signature)); - return (string)$this->module->getCms(); + return (string) $this->module->getCms(); } /** diff --git a/src/TimestampModule.php b/src/TimestampModule.php index 9888f08..d1064ec 100644 --- a/src/TimestampModule.php +++ b/src/TimestampModule.php @@ -1,7 +1,7 @@ client->timestamp($this->getHash($data)); - return \SetaPDF_Signer_Asn1_Element::parse(base64_decode($timestamp)); + return (string) \SetaPDF_Signer_Asn1_Element::parse(base64_decode($timestamp)); } /** diff --git a/src/autoload.php b/src/autoload.php deleted file mode 100644 index 4a2b0ed..0000000 --- a/src/autoload.php +++ /dev/null @@ -1,17 +0,0 @@ - false, + ]); + $httpClient = new \Mjelamanov\GuzzlePsr18\Client($httpClient); + + $module = new Client( + $httpClient, + new RequestFactory(), + new StreamFactory(), + '', + '' + ); + $this->expectException(ClientExceptionInterface::class); $module->login(); } - /** - * @expectedException \GuzzleHttp\Exception\ClientException - * @expectedExceptionMessage 400 Bad Request - */ public function testFailedLogin() { - $client = $this->getClientInstance(null, 'anything', 'but valid'); + $client = $this->getClientInstance('anything', 'but valid'); + $this->expectException(Exception::class); $client->login(); } @@ -64,11 +73,10 @@ public function testForceLogin(Client $client): Client * @param Client $client * @depends testForceLogin * @return Client - * @throws \GuzzleHttp\Exception\GuzzleException */ public function testGetQuotaForSignatures(Client $client): Client { - $this->assertInternalType(IsType::TYPE_INT, $client->getQuota(Client::TYPE_SIGNATURES)); + $this->assertNotEquals(0, $client->getQuota(Client::TYPE_SIGNATURES)); return $client; } @@ -76,23 +84,21 @@ public function testGetQuotaForSignatures(Client $client): Client * @param Client $client * @depends testGetQuotaForSignatures * @return Client - * @throws \GuzzleHttp\Exception\GuzzleException */ public function testGetQuotaForTimestamps(Client $client): Client { - $this->assertInternalType(IsType::TYPE_INT, $client->getQuota(Client::TYPE_TIMESTAMPS)); + $this->assertNotEquals(0, $client->getQuota(Client::TYPE_TIMESTAMPS)); return $client; } /** * @param Client $client * @depends testGetQuotaForTimestamps - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Unknow quota type * @throws \GuzzleHttp\Exception\GuzzleException */ public function testGetQuotaWithInvalidType(Client $client) { + $this->expectException(\InvalidArgumentException::class); $client->getQuota('anything'); } @@ -100,7 +106,6 @@ public function testGetQuotaWithInvalidType(Client $client) * @param Client $client * @depends testForceLogin * @return Client - * @throws \GuzzleHttp\Exception\GuzzleException */ public function testGetCountForSignatures(Client $client): Client { @@ -113,7 +118,6 @@ public function testGetCountForSignatures(Client $client): Client * @param Client $client * @depends testGetCountForSignatures * @return Client - * @throws \GuzzleHttp\Exception\GuzzleException */ public function testGetCountForTimestamps(Client $client): Client { @@ -126,30 +130,26 @@ public function testGetCountForTimestamps(Client $client): Client * @param Client $client * @depends testGetCountForTimestamps * @return Client - * @throws \GuzzleHttp\Exception\GuzzleException */ public function testGetCountForIdentities(Client $client): Client { - $this->assertInternalType(IsType::TYPE_INT, $client->getCount(Client::TYPE_IDENTITIES)); + $this->assertNotEquals(0, $client->getCount(Client::TYPE_IDENTITIES)); return $client; } /** * @param Client $client * @depends testGetCountForIdentities - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Unknow counter type - * @throws \GuzzleHttp\Exception\GuzzleException */ public function testGetCountWithInvalidType(Client $client): void { + $this->expectException(\InvalidArgumentException::class); $client->getCount('anything'); } /** * @param Client $client * @depends testForceLogin - * @throws \GuzzleHttp\Exception\GuzzleException */ public function testGetCertificatePath(Client $client): void { @@ -159,30 +159,27 @@ public function testGetCertificatePath(Client $client): void /** * @param Client $client * @depends testForceLogin - * @throws \GuzzleHttp\Exception\GuzzleException */ public function testGetValidationPolicy(Client $client): void { $validationPolicy = $client->getValidationPolicy(); - $this->assertInstanceOf(\stdClass::class, $validationPolicy); + $this->assertNotEmpty($validationPolicy); } /** * @param Client $client * @depends testForceLogin - * @expectedException \GuzzleHttp\Exception\ClientException - * @throws \GuzzleHttp\Exception\GuzzleException */ public function testGetIdentityWithInvalidData(Client $client): void { + $this->expectException(Exception::class); $client->getIdentity(['testing' => 123]); } /** * @param Client $client * @return array - * @throws \GuzzleHttp\Exception\GuzzleException * @depends testForceLogin */ public function testGetIdentityWithoutArguments(Client $client) @@ -197,31 +194,26 @@ public function testGetIdentityWithoutArguments(Client $client) } /** - * @param Client $client - * @param $identity * @depends testGetIdentityWithoutArguments - * @throws \GuzzleHttp\Exception\GuzzleException - * @expectedException \GuzzleHttp\Exception\ClientException - * @expectedExceptionMessage Malformed digest */ public function testSignWithInvalidHash(array $data) { + /** @var Client $client */ [$client, $identity] = $data; - /** @var Client $client */ + $this->expectException(Exception::class); $client->sign($identity, 'abc'); } /** * @param array $data * @depends testGetIdentityWithoutArguments - * @throws \GuzzleHttp\Exception\GuzzleException */ public function testSign(array $data) { + /** @var Client $client */ [$client, $identity] = $data; - /** @var Client $client */ $signature = $client->sign($identity, \hash_file('sha256', __FILE__)); $this->assertSame(\strlen($signature), 512); } @@ -229,35 +221,32 @@ public function testSign(array $data) /** * @param Client $client * @depends testForceLogin - * @expectedException \GuzzleHttp\Exception\ClientException - * @throws \GuzzleHttp\Exception\GuzzleException */ public function testTimestampWithInvalidHash(Client $client) { + $this->expectException(Exception::class); $client->timestamp('abc'); } /** * @param Client $client - * @throws \GuzzleHttp\Exception\GuzzleException * @depends testForceLogin */ public function testTimestamp(Client $client) { $timestamp = $client->timestamp(\hash_file('sha256', __FILE__)); - $this->assertSame(\strlen($timestamp), 2792); + $this->assertSame(\strlen($timestamp), 2788); } /** * @param Client $client - * @throws \GuzzleHttp\Exception\GuzzleException * @depends testForceLogin */ public function testGetTrustchain(Client $client) { $trustChain = $client->getTrustchain(); - $this->assertTrue(isset($trustChain->trustchain)); - $this->assertTrue(isset($trustChain->ocsp_revocation_info)); + $this->assertTrue(isset($trustChain['trustchain'])); + $this->assertTrue(isset($trustChain['ocsp_revocation_info'])); } } diff --git a/tests/functional/SignatureModuleTest.php b/tests/functional/SignatureModuleTest.php index 2f90745..1bdcebb 100644 --- a/tests/functional/SignatureModuleTest.php +++ b/tests/functional/SignatureModuleTest.php @@ -9,8 +9,7 @@ class SignatureModuleTest extends TestHelper { /** - * @throws \GuzzleHttp\Exception\GuzzleException - * @throws \SetaPDF_Signer_Exception + * @throws \Throwable */ public function testSimpleSignature() { @@ -36,8 +35,7 @@ public function testSimpleSignature() } /** - * @throws \GuzzleHttp\Exception\GuzzleException - * @throws \SetaPDF_Signer_Exception + * @throws \Throwable */ public function testSignatureIncudingTimestamp() { @@ -66,6 +64,9 @@ public function testSignatureIncudingTimestamp() ); } + /** + * @throws \Throwable + */ public function testVisibleSignature() { $writer = new \SetaPDF_Core_Writer_TempFile(); @@ -176,68 +177,15 @@ protected function getSignatureDetails($path, $signatureFieldName) protected function validate($path, $signatureFieldName, $certificate) { - list($tmpFile, $asn1) = $this->getSignatureDetails($path, $signatureFieldName); - - $contentType = $asn1->getChild(0)->getValue(); - $contentType = \SetaPDF_Signer_Asn1_Oid::decode($contentType); - $this->assertEquals('1.2.840.113549.1.7.2', $contentType); - - /** @var \SetaPDF_Signer_Asn1_Element $content */ - $content = $asn1->getChild(1); - $signedData = $content->getChild(0); - - $digestAlgorithms = $signedData->getChild(1); - $hashes = array(); - foreach ($digestAlgorithms->getChildren() AS $algorithm) { - $algorithmOid = \SetaPDF_Signer_Asn1_Oid::decode($algorithm->getChild(0)->getValue()); - $digest = \SetaPDF_Signer_Digest::getByOid($algorithmOid); - $hashes[$digest] = hash_file($digest, $tmpFile->getPath(), true); - } - - // ensure that no eContent is used - $encapContentInfo = $signedData->getChild(2); - $this->assertEquals(1, $encapContentInfo->getChildCount()); - - $signerInfos = $signedData->getChild($signedData->getChildCount() - 1); - // only one SignerInfo - $this->assertEquals(1, $signerInfos->getChildCount()); - $signerInfo = $signerInfos->getChild(0); - // get digest algo and check if it was defined in digestAlgorithms - $digestAlgorithmOid = \SetaPDF_Signer_Asn1_Oid::decode($signerInfo->getChild(2)->getChild(0)->getValue()); - $digest = \SetaPDF_Signer_Digest::getByOid($digestAlgorithmOid); - $this->assertTrue(isset($hashes[$digest])); - - // Check for signed attributes - if ($signerInfo->getChild(3)->getIdent() === "\xA0") { // [0] IMPLICIT - $_signedAttributes = $signerInfo->getChild(3)->getChildren(); - $signedAttributes = array(); - foreach ($_signedAttributes as $attribute) { - $attrType = $attribute->getChild(0)->getValue(); - $attrTypeOid = \SetaPDF_Signer_Asn1_Oid::decode($attrType); - $signedAttributes[$attrTypeOid] = $attribute->getChild(1); - } + $document = \SetaPDF_Core_Document::loadByFilename($path); + $result = \SetaPDF_Signer_ValidationRelatedInfo_IntegrityResult::create($document, $signatureFieldName); - // check for mandatory attributes - $this->assertTrue(isset($signedAttributes['1.2.840.113549.1.9.3'])); // content-type - $this->assertTrue(isset($signedAttributes['1.2.840.113549.1.9.4'])); // message-digest - - // hashes match? - $this->assertEquals($hashes[$digest], $signedAttributes['1.2.840.113549.1.9.4']->getChild(0)->getValue()); - - $data = $signerInfo->getChild(3)->__toString(); - $data[0] = \SetaPDF_Signer_Asn1_Element::SET | \SetaPDF_Signer_Asn1_Element::IS_CONSTRUCTED; - $signatureValue = $signerInfo->getChild(5)->getValue(); - - } else { - $data = file_get_contents($tmpFile->getPath()); - $signatureValue = $signerInfo->getChild(4)->getValue(); - } + $this->assertTrue($result->isValid()); - while (\openssl_error_string()); + $certificate = new \SetaPDF_Signer_X509_Certificate($certificate); + $signingCertificate = $result->getSignedData()->getSigningCertificate(); - $pkey = \openssl_pkey_get_public($certificate); - $res = \openssl_verify($data, $signatureValue, $pkey, \SetaPDF_Signer_Digest::getOpenSslInt($digest)); - $this->assertEquals(1, $res, openssl_error_string()); + $this->assertEquals($certificate->getDigest(), $signingCertificate->getDigest()); return true; } diff --git a/tests/functional/TestHelper.php b/tests/functional/TestHelper.php index c0836c4..273e6db 100644 --- a/tests/functional/TestHelper.php +++ b/tests/functional/TestHelper.php @@ -2,18 +2,21 @@ namespace setasign\SetaPDF\Signer\Module\GlobalSign\Dss\tests\functional; +use Http\Factory\Guzzle\RequestFactory; +use Http\Factory\Guzzle\StreamFactory; use PHPUnit\Framework\TestCase; use setasign\SetaPDF\Signer\Module\GlobalSign\Dss\Client; class TestHelper extends TestCase { - protected function getClientInstance(array $options = null, $apiKey = null, $apiSecret = null) - { + protected function getClientInstance( + $apiKey = null, + $apiSecret = null + ): Client { $privateFolder = __DIR__ . '/../../private'; $credentialsFile = $privateFolder . '/credentials.php'; if (!file_exists($credentialsFile)) { $this->markTestSkipped('No credentials known.'); - return; } $credentials = require($credentialsFile); @@ -22,11 +25,13 @@ protected function getClientInstance(array $options = null, $apiKey = null, $api $apiSecret = $credentials['apiSecret']; } - $options = $options ?? [ + $httpClient = new \GuzzleHttp\Client([ + 'http_errors' => false, 'cert' => $credentials['cert'], 'ssl_key' => $credentials['privateKey'] - ]; + ]); + $httpClient = new \Mjelamanov\GuzzlePsr18\Client($httpClient); - return new Client($options, $apiKey, $apiSecret); + return new Client($httpClient, new RequestFactory(), new StreamFactory(), $apiKey, $apiSecret); } } diff --git a/tests/functional/TimestampModuleTest.php b/tests/functional/TimestampModuleTest.php index c91f60c..f19f579 100644 --- a/tests/functional/TimestampModuleTest.php +++ b/tests/functional/TimestampModuleTest.php @@ -9,8 +9,7 @@ class TimestampModuleTest extends TestHelper { /** - * @throws \GuzzleHttp\Exception\GuzzleException - * @throws \SetaPDF_Signer_Exception + * @throws \Throwable */ public function testSimpleTimestamp() {