diff --git a/composer.lock b/composer.lock index aa8773a..1d2867f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,25 +4,25 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0934509931d20fd913f0862cd8aaeea7", + "content-hash": "fde257651cc530ab11eff5a843283379", "packages": [ { "name": "psr/http-client", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/http-client.git", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31", + "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31", "shasum": "" }, "require": { "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { @@ -42,7 +42,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for HTTP clients", @@ -54,27 +54,27 @@ "psr-18" ], "support": { - "source": "https://github.com/php-fig/http-client/tree/master" + "source": "https://github.com/php-fig/http-client/tree/1.0.2" }, - "time": "2020-06-29T06:28:15+00:00" + "time": "2023-04-10T20:12:12+00:00" }, { "name": "psr/http-factory", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/http-factory.git", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + "reference": "e616d01114759c4c489f93b099585439f795fe35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", + "reference": "e616d01114759c4c489f93b099585439f795fe35", "shasum": "" }, "require": { "php": ">=7.0.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { @@ -94,7 +94,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interfaces for PSR-7 HTTP message factories", @@ -109,31 +109,31 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-factory/tree/master" + "source": "https://github.com/php-fig/http-factory/tree/1.0.2" }, - "time": "2019-04-30T12:38:16+00:00" + "time": "2023-04-10T20:10:41+00:00" }, { "name": "psr/http-message", - "version": "1.0.1", + "version": "1.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { @@ -162,9 +162,9 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/master" + "source": "https://github.com/php-fig/http-message/tree/1.1" }, - "time": "2016-08-06T14:39:51+00:00" + "time": "2023-04-04T09:50:52+00:00" }, { "name": "setasign/setapdf-core", @@ -414,16 +414,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "1.9.0", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "e98e3e6d4f86621a9b75f623996e6bbdeb4b9318" + "reference": "e4490cabc77465aaee90b20cfc9a770f8c04be6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/e98e3e6d4f86621a9b75f623996e6bbdeb4b9318", - "reference": "e98e3e6d4f86621a9b75f623996e6bbdeb4b9318", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/e4490cabc77465aaee90b20cfc9a770f8c04be6b", + "reference": "e4490cabc77465aaee90b20cfc9a770f8c04be6b", "shasum": "" }, "require": { @@ -442,11 +442,6 @@ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.9-dev" - } - }, "autoload": { "files": [ "src/functions_include.php" @@ -504,7 +499,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.9.0" + "source": "https://github.com/guzzle/psr7/tree/1.9.1" }, "funding": [ { @@ -520,24 +515,25 @@ "type": "tidelift" } ], - "time": "2022-06-20T21:43:03+00:00" + "time": "2023-04-17T16:00:37+00:00" }, { "name": "http-interop/http-factory-guzzle", - "version": "1.1.1", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/http-interop/http-factory-guzzle.git", - "reference": "6e1efa1e020bf1c47cf0f13654e8ef9efb1463b3" + "reference": "8f06e92b95405216b237521cc64c804dd44c4a81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/http-interop/http-factory-guzzle/zipball/6e1efa1e020bf1c47cf0f13654e8ef9efb1463b3", - "reference": "6e1efa1e020bf1c47cf0f13654e8ef9efb1463b3", + "url": "https://api.github.com/repos/http-interop/http-factory-guzzle/zipball/8f06e92b95405216b237521cc64c804dd44c4a81", + "reference": "8f06e92b95405216b237521cc64c804dd44c4a81", "shasum": "" }, "require": { - "guzzlehttp/psr7": "^1.4.2||^2.0", + "guzzlehttp/psr7": "^1.7||^2.0", + "php": ">=7.3", "psr/http-factory": "^1.0" }, "provide": { @@ -545,7 +541,10 @@ }, "require-dev": { "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "guzzlehttp/psr7": "Includes an HTTP factory starting in version 2.0" }, "type": "library", "autoload": { @@ -572,22 +571,22 @@ ], "support": { "issues": "https://github.com/http-interop/http-factory-guzzle/issues", - "source": "https://github.com/http-interop/http-factory-guzzle/tree/1.1.1" + "source": "https://github.com/http-interop/http-factory-guzzle/tree/1.2.0" }, - "time": "2021-07-23T15:14:50+00:00" + "time": "2021-07-21T13:50:14+00:00" }, { "name": "league/oauth2-client", - "version": "2.6.1", + "version": "2.7.0", "source": { "type": "git", "url": "https://github.com/thephpleague/oauth2-client.git", - "reference": "2334c249907190c132364f5dae0287ab8666aa19" + "reference": "160d6274b03562ebeb55ed18399281d8118b76c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/2334c249907190c132364f5dae0287ab8666aa19", - "reference": "2334c249907190c132364f5dae0287ab8666aa19", + "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/160d6274b03562ebeb55ed18399281d8118b76c8", + "reference": "160d6274b03562ebeb55ed18399281d8118b76c8", "shasum": "" }, "require": { @@ -642,9 +641,9 @@ ], "support": { "issues": "https://github.com/thephpleague/oauth2-client/issues", - "source": "https://github.com/thephpleague/oauth2-client/tree/2.6.1" + "source": "https://github.com/thephpleague/oauth2-client/tree/2.7.0" }, - "time": "2021-12-22T16:42:49+00:00" + "time": "2023-04-16T18:19:15+00:00" }, { "name": "mjelamanov/psr18-guzzle", @@ -1162,5 +1161,5 @@ "ext-json": "*" }, "platform-dev": [], - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } diff --git a/examples/appearance-demo.php b/examples/appearance-demo.php index 2ff2fad..4bb3240 100644 --- a/examples/appearance-demo.php +++ b/examples/appearance-demo.php @@ -3,6 +3,7 @@ declare(strict_types=1); use setasign\SetaPDF\Signer\Module\CSC\Client; +use setasign\SetaPDF\Signer\Module\CSC\ClientException; use setasign\SetaPDF\Signer\Module\CSC\Module; ini_set('display_errors', '1'); @@ -108,7 +109,19 @@ $appearance = new SetaPDF_Signer_Signature_Appearance_Dynamic($module); $signer->setAppearance($appearance); -$signer->sign($module); +try { + $signer->sign($module); +} catch (ClientException $e) { + echo 'An error occured:'; + echo $e->getMessage() . ': ' . $e->getResponse()->getBody(); + echo '
restart'; + die(); +} catch (\Exception $e) { + echo 'An error occured:'; + echo $e->getMessage(); + echo '
restart'; + die(); +} echo 'download | restart
'; diff --git a/examples/assets/SSL.com.dev-ca.cer b/examples/assets/SSL.com.dev-ca.cer new file mode 100644 index 0000000..3b145b7 Binary files /dev/null and b/examples/assets/SSL.com.dev-ca.cer differ diff --git a/examples/async-demo.php b/examples/async-demo.php index 49a5de5..ae2814a 100644 --- a/examples/async-demo.php +++ b/examples/async-demo.php @@ -2,6 +2,8 @@ declare(strict_types=1); +use League\OAuth2\Client\OptionProvider\OptionProviderInterface; +use League\OAuth2\Client\Provider\AbstractProvider; use League\OAuth2\Client\Provider\GenericProvider; use SetaPDF_Signer_Digest as Digest; use setasign\SetaPDF\Signer\Module\CSC\Client; @@ -26,7 +28,7 @@ $settings = require 'settings.php'; $apiUri = $settings['apiUri']; -$fileToSign = __DIR__ . '/Laboratory-Report.pdf'; +$fileToSign = __DIR__ . '/assets/Laboratory-Report.pdf'; $resultPath = 'signed.pdf'; @@ -56,7 +58,23 @@ 'urlAuthorize' => $oauth2Urls['urlAuthorize'], 'urlAccessToken' => $oauth2Urls['urlAccessToken'], 'urlResourceOwnerDetails' => $oauth2Urls['urlResourceOwnerDetails'], -]); +] /* + // If your OAuth endpoint requires a JSON document instead of a form-encoded document pass following "optionProvider" + // to the GenericProvider constructor: + , [ + 'optionProvider' => new class implements OptionProviderInterface { + public function getAccessTokenOptions($method, array $params) + { + $options = ['headers' => ['content-type' => 'application/json']]; + + if ($method === AbstractProvider::METHOD_POST) { + $options['body'] = json_encode($params); + } + + return $options; + } + } +]*/); $action = $_GET['action'] ?? 'preview'; // if the oauth request was unsuccessful, return to preSign diff --git a/examples/demo.php b/examples/demo.php index dad294b..ff2a8cd 100644 --- a/examples/demo.php +++ b/examples/demo.php @@ -3,6 +3,7 @@ declare(strict_types=1); use setasign\SetaPDF\Signer\Module\CSC\Client; +use setasign\SetaPDF\Signer\Module\CSC\ClientException; use setasign\SetaPDF\Signer\Module\CSC\Module; ini_set('display_errors', '1'); @@ -93,8 +94,19 @@ // create the signer instance $signer = new SetaPDF_Signer($document); - -$signer->sign($module); +try { + $signer->sign($module); +} catch (ClientException $e) { + echo 'An error occured:'; + echo $e->getMessage() . ': ' . $e->getResponse()->getBody(); + echo '
restart'; + die(); +} catch (\Exception $e) { + echo 'An error occured:'; + echo $e->getMessage(); + echo '
restart'; + die(); +} echo 'download | restart
'; diff --git a/examples/generate-token.php b/examples/generate-token.php index e06544c..f413c68 100644 --- a/examples/generate-token.php +++ b/examples/generate-token.php @@ -1,5 +1,7 @@ $oauth2Urls['urlAuthorize'], 'urlAccessToken' => $oauth2Urls['urlAccessToken'], 'urlResourceOwnerDetails' => $oauth2Urls['urlResourceOwnerDetails'], -]); +] +/* + // If your OAuth endpoint requires a JSON document instead of a form-encoded document pass following "optionProvider" + // to the GenericProvider constructor: + , [ + 'optionProvider' => new class implements OptionProviderInterface { + public function getAccessTokenOptions($method, array $params) + { + $options = ['headers' => ['content-type' => 'application/json']]; + + if ($method === AbstractProvider::METHOD_POST) { + $options['body'] = json_encode($params); + } + + return $options; + } + } +]*/ +); session_start(); diff --git a/examples/ltv-demo.php b/examples/ltv-demo.php index 5af12b6..92badee 100644 --- a/examples/ltv-demo.php +++ b/examples/ltv-demo.php @@ -3,6 +3,7 @@ declare(strict_types=1); use setasign\SetaPDF\Signer\Module\CSC\Client; +use setasign\SetaPDF\Signer\Module\CSC\ClientException; use setasign\SetaPDF\Signer\Module\CSC\Module; ini_set('display_errors', '1'); @@ -26,6 +27,7 @@ $timestampingUrl = 'http://ts.ssl.com'; $trustedCertificatesPath = __DIR__ . '/assets/SSL.com.ca-bundle'; +$otherTrustedCertificatePaths = [__DIR__ . '/assets/SSL.com.dev-ca.cer']; // to create or update your access token you have to call generate-token.php first if (!isset($_SESSION['accessToken']['access_token'])) { @@ -82,6 +84,11 @@ // create a collection of trusted certificats: $trustedCertificates = new SetaPDF_Signer_X509_Collection($certificates[count($certificates) - 1]); $trustedCertificates->add(SetaPDF_Signer_Pem::extractFromFile($trustedCertificatesPath)); +// sadly not all CSC API implementations return the full chain (in our tests e.g. SSL.com), so we have to +// add a trusted root on our own: +foreach ($otherTrustedCertificatePaths as $otherTrustedCertificatePath) { + $trustedCertificates->addFromFile($otherTrustedCertificatePath); +} // create a collector instance $collector = new SetaPDF_Signer_ValidationRelatedInfo_Collector($trustedCertificates); @@ -128,7 +135,19 @@ // ...this is needed to add validation related information later $signer->setSignatureFieldName($signatureField->getQualifiedName()); -$signer->sign($module); +try { + $signer->sign($module); +} catch (ClientException $e) { + echo 'An error occured:'; + echo $e->getMessage() . ': ' . $e->getResponse()->getBody(); + echo '
restart'; + die(); +} catch (\Exception $e) { + echo 'An error occured:'; + echo $e->getMessage(); + echo '
restart'; + die(); +} // create a new instance $document = SetaPDF_Core_Document::loadByFilename($tmpWriter->getPath(), $writer); diff --git a/src/Client.php b/src/Client.php index 41e21c7..98f0d1b 100644 --- a/src/Client.php +++ b/src/Client.php @@ -5,7 +5,6 @@ namespace setasign\SetaPDF\Signer\Module\CSC; use BadMethodCallException; -use Exception; use Psr\Http\Client\ClientExceptionInterface; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; @@ -98,7 +97,7 @@ public function getOauth2Info(): array * @param int $depth * @param int $options * @return mixed - * @throws Exception + * @throws ClientException */ protected function json_decode(string $json, bool $assoc = true, int $depth = 512, int $options = 0) { @@ -108,7 +107,7 @@ protected function json_decode(string $json, bool $assoc = true, int $depth = 51 $data = @\json_decode($json, $assoc, $depth, $options); if (\json_last_error() !== JSON_ERROR_NONE) { - throw new Exception(\sprintf( + throw new \InvalidArgumentException(\sprintf( 'Unable to decode JSON: %s', \json_last_error_msg() )); @@ -123,7 +122,7 @@ protected function json_decode(string $json, bool $assoc = true, int $depth = 51 * @param array $inputData * @return array * @throws ClientExceptionInterface - * @throws Exception + * @throws ClientException */ public function call(string $path, ?string $accessToken = null, array $inputData = []): array { @@ -144,7 +143,8 @@ public function call(string $path, ?string $accessToken = null, array $inputData $response = $this->httpClient->sendRequest($request); if ($response->getStatusCode() !== 200) { - throw new Exception('Error on ' . $path . ': ' . $response->getBody()); + // $body in the message is kept for BC, don't rely on it but use $e->getData() instead! + throw new ClientException('Error on ' . $path . ': ' . $response->getBody(), $response); } return $this->json_decode((string) $response->getBody()); @@ -183,7 +183,7 @@ public function info(?string $lang = null): array * @param string|null $pageToken * @param string|null $clientData * @return array - * @throws ClientExceptionInterface + * @throws ClientExceptionInterface|ClientException * @see CSC API 11.4 /credentials/list */ public function credentialsList( @@ -222,7 +222,7 @@ public function credentialsList( * @param string|null $lang * @param string|null $clientData * @return array - * @throws ClientExceptionInterface + * @throws ClientExceptionInterface|ClientException * @see CSC API 11.5 /credentials/info */ public function credentialsInfo( @@ -268,7 +268,7 @@ public function credentialsInfo( * @param string $credentialID * @param string|null $clientData * @return array - * @throws ClientExceptionInterface + * @throws ClientExceptionInterface|ClientException * @see CSC API 11.8 /credentials/sendOTP */ public function credentialsSendOTP(string $accessToken, string $credentialID, ?string $clientData = null): array @@ -297,7 +297,7 @@ public function credentialsSendOTP(string $accessToken, string $credentialID, ?s * @param string|null $description * @param string|null $clientData * @return array - * @throws ClientExceptionInterface + * @throws ClientExceptionInterface|ClientException * @see CSC API 11.6 /credentials/authorize */ public function credentialsAuthorize( @@ -343,7 +343,7 @@ public function credentialsAuthorize( * @param string|null $signAlgoParams * @param string|null $clientData * @return array - * @throws ClientExceptionInterface + * @throws ClientExceptionInterface|ClientException * @see CSC API 11.9 /signatures/signHash */ public function signaturesSignHash( diff --git a/src/ClientException.php b/src/ClientException.php new file mode 100644 index 0000000..ad3fb88 --- /dev/null +++ b/src/ClientException.php @@ -0,0 +1,30 @@ +response = $response; + } + + public function getResponse() + { + return $this->response; + } +} diff --git a/src/Exception.php b/src/Exception.php deleted file mode 100644 index fbd3f79..0000000 --- a/src/Exception.php +++ /dev/null @@ -1,15 +0,0 @@ -