Skip to content

Commit

Permalink
Fix CI + fix for lcobucci/jwt:^4.0 (#104)
Browse files Browse the repository at this point in the history
* Fix CI

* Update src/Grant/JwtGrant.php

Co-authored-by: Grégoire Hébert <gregoire@les-tilleuls.coop>

Co-authored-by: Grégoire Hébert <gregoire@les-tilleuls.coop>
  • Loading branch information
vincentchalamon and GregoireHebert committed Nov 27, 2020
1 parent 3092096 commit 9302e22
Show file tree
Hide file tree
Showing 25 changed files with 262 additions and 43 deletions.
40 changes: 24 additions & 16 deletions .github/workflows/ci.yml
Expand Up @@ -16,25 +16,38 @@ jobs:
strategy:
matrix:
php:
- '7.2'
- '7.3'
- '7.4'
- 'rc'
- '8.0'
symfony:
- '4.4.*'
- '5.1.*'
dependency:
- ''
- 'lowest'
- '--prefer-lowest'
include:
- php: '7.4'
dependency: ''
symfony: '5.1.*'
coverage: true
bootable: true
- php: '8.0'
dependency: '--ignore-platform-req=php'
symfony: '4.4.*'
- php: '8.0'
dependency: '--ignore-platform-req=php'
symfony: '5.1.*'
- php: '8.0'
dependency: '--prefer-lowest --ignore-platform-req=php'
symfony: '4.4.*'
- php: '8.0'
dependency: '--prefer-lowest --ignore-platform-req=php'
symfony: '5.1.*'
exclude:
- symfony: '4.4.*'
dependency: 'lowest'
- php: '8.0'
dependency: ''
- php: '8.0'
dependency: '--prefer-lowest'
fail-fast: false
steps:
- name: Checkout
Expand Down Expand Up @@ -63,15 +76,10 @@ jobs:
run: composer config extra.symfony.require "${{ matrix.symfony }}"

- name: Update project dependencies
if: matrix.dependency == ''
run: composer update --no-progress --ansi --prefer-stable ${{ matrix.composer }}

- name: Update project dependencies lowest
if: matrix.dependency == 'lowest'
run: composer update --no-progress --ansi --prefer-lowest --prefer-stable
run: composer update --no-progress --ansi --prefer-stable ${{ matrix.dependency }}

- name: Disable deprecations notices for lowest dependencies
if: matrix.dependency == 'lowest'
if: matrix.dependency != ''
run: echo "SYMFONY_DEPRECATIONS_HELPER=weak" >> $GITHUB_ENV

- name: Bundle is bootable
Expand All @@ -82,10 +90,10 @@ jobs:
composer create-project "symfony/skeleton:${SKELETON_VERSION}" flex
cd flex
composer config extra.symfony.allow-contrib true
composer req --ignore-platform-reqs gheb/docusign-bundle
composer req gheb/docusign-bundle
- name: Run php-cs-fixer tests
if: matrix.php != 'rc'
if: matrix.php != '8.0'
run: php-cs-fixer fix --diff --dry-run

- name: Install chromium
Expand All @@ -94,11 +102,11 @@ jobs:
sudo apt-get install -y --no-install-recommends chromium-browser
- name: Run phpstan tests
if: matrix.dependency == ''
if: matrix.dependency == '' && matrix.php != '7.3' && matrix.php != '8.0'
run: vendor/bin/phpstan analyze

- name: Run phpstan tests lowest
if: matrix.dependency == 'lowest'
if: matrix.dependency != '' && matrix.php != '8.0'
run: vendor/bin/phpstan analyze -c phpstan.neon.lowest.dist

- name: Prepare PHPUnit tests
Expand Down
8 changes: 4 additions & 4 deletions composer.json
Expand Up @@ -26,7 +26,7 @@
"php": ">=7.2",
"ext-SimpleXML": "*",
"docusign/esign-client": "^3.0",
"lcobucci/jwt": "^3.3.1 || ^4.0-dev",
"lcobucci/jwt": "^3.3.1 || ^4.0",
"league/flysystem": "^1.0.8",
"psr/log": "^1.1",
"symfony/config": "^4.4 || ^5.0",
Expand All @@ -46,6 +46,7 @@
"doctrine/annotations": "^1.11",
"league/flysystem-bundle": "^1.2",
"nyholm/symfony-bundle-test": "dev-master",
"phpspec/prophecy": "^1.12",
"phpstan/phpstan": "^0.12.18",
"psr/event-dispatcher": "^1.0",
"symfony/console": "^4.4 || ^5.0",
Expand All @@ -54,7 +55,7 @@
"symfony/dotenv": "^4.4 || ^5.0",
"symfony/monolog-bundle": "^3.5",
"symfony/panther": "^0.6.1",
"symfony/phpunit-bridge": "^4.4 || ^5.0",
"symfony/phpunit-bridge": "^5.1",
"symfony/polyfill-php72": "^1.9",
"symfony/process": "^4.4 || ^5.0",
"symfony/profiler-pack": "^1.0",
Expand All @@ -73,8 +74,7 @@
"autoload-dev": {
"psr-4": {
"DocusignBundle\\Tests\\": "tests/",
"DocusignBundle\\E2e\\": "features/src/",
"PHPUnit\\": "vendor/bin/.phpunit/phpunit/src"
"DocusignBundle\\E2e\\": "features/src/"
}
},
"scripts": {
Expand Down
10 changes: 10 additions & 0 deletions features/bootstrap.php
Expand Up @@ -15,6 +15,16 @@

date_default_timezone_set('UTC');

// PHPUnit's autoloader
if (!file_exists($phpUnitAutoloaderPath = __DIR__.'/../vendor/bin/.phpunit/phpunit/vendor/autoload.php')) {
die('PHPUnit is not installed. Please run vendor/bin/simple-phpunit --version to install it');
}

$phpunitLoader = require $phpUnitAutoloaderPath;
// Don't register the PHPUnit autoloader before the normal autoloader to prevent weird issues
$phpunitLoader->unregister();
$phpunitLoader->register();

require __DIR__.'/../vendor/autoload.php';

// Load cached env vars if the .env.local.php file exists
Expand Down
7 changes: 5 additions & 2 deletions phpstan.neon.dist
Expand Up @@ -7,5 +7,8 @@ parameters:
- '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::useAttributeAsKey\(\)\.#'

# known bug of phpstan
- "#Instantiated class Lcobucci\\\\JWT\\\\Token\\\\Builder not found\\.#"
- "#Instantiated class Lcobucci\\\\Jose\\\\Parsing\\\\Parser not found\\.#"
- "#Cannot cast Lcobucci\\\\JWT\\\\Token\\\\Plain to string\\.#"
- "#Parameter \\#1 \\$issuedAt of method Lcobucci\\\\JWT\\\\Builder::issuedAt\\(\\) expects DateTimeImmutable, int given\\.#"
- "#Parameter \\#1 \\$expiration of method Lcobucci\\\\JWT\\\\Builder::expiresAt\\(\\) expects DateTimeImmutable, int given\\.#"
- "#Cannot instantiate interface Lcobucci\\\\JWT\\\\Builder\\.#"
- "#Cannot instantiate interface Lcobucci\\\\JWT\\\\Signer\\\\Key\\.#"
5 changes: 2 additions & 3 deletions phpstan.neon.lowest.dist
Expand Up @@ -7,6 +7,5 @@ parameters:
- '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::useAttributeAsKey\(\)\.#'

# known bug of phpstan
- "#Class Lcobucci\\\\JWT\\\\Token\\\\Builder not found\\.#"
- "#Instantiated class Lcobucci\\\\JWT\\\\Token\\\\Builder not found\\.#"
- "#Instantiated class Lcobucci\\\\Jose\\\\Parsing\\\\Parser not found\\.#"
- "#Call to static method file\\(\\) on an unknown class Lcobucci\\\\JWT\\\\Signer\\\\Key\\\\LocalFileReference\\.#"
- "#Call to static method forSymmetricSigner\\(\\) on an unknown class Lcobucci\\\\JWT\\\\Configuration\\.#"
4 changes: 2 additions & 2 deletions phpunit.xml.dist
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>

<!-- https://phpunit.de/manual/current/en/appendixes.configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.3/phpunit.xsd" backupGlobals="true" colors="true" bootstrap="features/bootstrap.php">
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.4/phpunit.xsd" backupGlobals="true" colors="true" bootstrap="features/bootstrap.php">
<php>
<ini name="error_reporting" value="-1" />
<env name="APP_ENV" value="test" />
<env name="APP_DEBUG" value="false" />
<env name="KERNEL_DIR" value="features/src/" />
<env name="KERNEL_CLASS" value="DocusignBundle\E2e\Kernel" />
<env name="SYMFONY_PHPUNIT_REMOVE" value="symfony/yaml" />
<env name="SYMFONY_PHPUNIT_VERSION" value="9.4" />
<server name="PANTHER_WEB_SERVER_DIR" value="./features/public/" />
<server name="PANTHER_CHROME_DRIVER_BINARY" value="./tests/config/chromedriver" />
<server name="PANTHER_NO_SANDBOX" value="1" />
Expand Down
58 changes: 42 additions & 16 deletions src/Grant/JwtGrant.php
Expand Up @@ -13,9 +13,10 @@

namespace DocusignBundle\Grant;

use Lcobucci\Jose\Parsing\Parser;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Key\LocalFileReference;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use Lcobucci\JWT\Token\Builder as TokenBuilder;
use Symfony\Component\HttpClient\HttpClient;
Expand Down Expand Up @@ -55,28 +56,53 @@ public function __construct(

public function __invoke(): string
{
// Ensure compatibility with lcobucci/jwt v3 and v4
$v4 = class_exists(TokenBuilder::class);
$time = $v4 ? new \DateTimeImmutable() : time();
/** @var Builder $builder */
$builder = $v4 ? new TokenBuilder(new Parser()) : new Builder();
$token = $builder->issuedBy($this->integrationKey) // iss
->relatedTo($this->userGuid) // sub
->issuedAt($time) // iat
->expiresAt($v4 ? $time->modify("$this->ttl sec") : $time + $this->ttl) // exp
->permittedFor(parse_url($this->accountApiUri, PHP_URL_HOST)) // aud
->withClaim('scope', 'signature impersonation') // scope
->getToken(new Sha256(), new Key("file://$this->privateKey"));
$token = method_exists($token, 'toString') ? $token->toString() : (string) $token;

try {
$response = $this->client->request('POST', $this->accountApiUri, [
'body' => "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=$token",
'body' => sprintf('grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=%s', $this->createToken()),
]);

return $response->toArray()['access_token'] ?? '';
} catch (ExceptionInterface $exception) {
return '';
}
}

/**
* Creates a valid JWT for DocuSign.
*
* @see https://developers.docusign.com/platform/auth/jwt/jwt-get-token/
*/
private function createToken(): string
{
// Ensure compatibility with lcobucci/jwt v3 and v4
if (class_exists(TokenBuilder::class)) {
// lcobucci/jwt v4
$time = new \DateTimeImmutable();
// Need for force seconds to 0, otherwise DocuSign will consider this token as invalid
$time = $time->setTime((int) $time->format('H'), (int) $time->format('i'), 0, 0);
$config = Configuration::forSymmetricSigner(new Sha256(), LocalFileReference::file("file://$this->privateKey"));

return $config
->builder()
->issuedBy($this->integrationKey) // iss
->relatedTo($this->userGuid) // sub
->issuedAt($time) // iat
->expiresAt($time->modify("$this->ttl sec")) // exp
->permittedFor(parse_url($this->accountApiUri, PHP_URL_HOST)) // aud
->withClaim('scope', 'signature impersonation') // scope
->getToken($config->signer(), $config->signingKey())
->toString();
}

$time = time();

return (string) (new Builder())
->issuedBy($this->integrationKey) // iss
->relatedTo($this->userGuid) // sub
->issuedAt($time) // iat
->expiresAt($time + $this->ttl) // exp
->permittedFor(parse_url($this->accountApiUri, PHP_URL_HOST)) // aud
->withClaim('scope', 'signature impersonation') // scope
->getToken(new Sha256(), new Key("file://$this->privateKey"));
}
}
3 changes: 3 additions & 0 deletions tests/Controller/AuthorizationCodeTest.php
Expand Up @@ -17,6 +17,7 @@
use DocusignBundle\EnvelopeBuilderInterface;
use DocusignBundle\Events\AuthorizationCodeEvent;
use DocusignBundle\Exception\MissingMandatoryParameterHttpException;
use DocusignBundle\Tests\ProphecyTrait;
use DocusignBundle\TokenEncoder\TokenEncoderInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
Expand All @@ -28,6 +29,8 @@

class AuthorizationCodeTest extends TestCase
{
use ProphecyTrait;

public function testTheAuthorizationCodeControllerIsValid(): void
{
$tokenEncoderProphecy = $this->prophesize(TokenEncoderInterface::class);
Expand Down
3 changes: 3 additions & 0 deletions tests/Controller/CallbackTest.php
Expand Up @@ -16,6 +16,7 @@
use DocusignBundle\Controller\Callback;
use DocusignBundle\DocusignBundle;
use DocusignBundle\Events\DocumentSignatureCompletedEvent;
use DocusignBundle\Tests\ProphecyTrait;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
Expand All @@ -25,6 +26,8 @@

class CallbackTest extends TestCase
{
use ProphecyTrait;

public function testTheCallbackControllerIsValid(): void
{
$requestProphecy = $this->prophesize(Request::class);
Expand Down
3 changes: 3 additions & 0 deletions tests/Controller/ConsentTest.php
Expand Up @@ -14,6 +14,7 @@
namespace DocusignBundle\Tests\Controller;

use DocusignBundle\Controller\Consent;
use DocusignBundle\Tests\ProphecyTrait;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\ObjectProphecy;
use Symfony\Component\HttpFoundation\RedirectResponse;
Expand All @@ -24,6 +25,8 @@
*/
final class ConsentTest extends TestCase
{
use ProphecyTrait;

public function testItRedirectsToValidUri(): void
{
/** @var Request|ObjectProphecy $requestMock */
Expand Down
3 changes: 3 additions & 0 deletions tests/Controller/SignTest.php
Expand Up @@ -17,6 +17,7 @@
use DocusignBundle\EnvelopeBuilderInterface;
use DocusignBundle\Events\PreSignEvent;
use DocusignBundle\Exception\MissingMandatoryParameterHttpException;
use DocusignBundle\Tests\ProphecyTrait;
use League\Flysystem\FileNotFoundException;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
Expand All @@ -29,6 +30,8 @@

class SignTest extends TestCase
{
use ProphecyTrait;

public function testTheSignControllerRedirectsTheUserToDocusign(): void
{
$eventDispatcherProphecy = $this->prophesize(EventDispatcherInterface::class);
Expand Down
3 changes: 3 additions & 0 deletions tests/Controller/WebhookTest.php
Expand Up @@ -15,6 +15,7 @@

use DocusignBundle\Controller\Webhook;
use DocusignBundle\Events\CompletedEvent;
use DocusignBundle\Tests\ProphecyTrait;
use DocusignBundle\TokenEncoder\TokenEncoderInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
Expand All @@ -27,6 +28,8 @@

class WebhookTest extends TestCase
{
use ProphecyTrait;

private $requestProphecy;
private $queryProphecy;
private $eventDispatcherProphecy;
Expand Down
3 changes: 3 additions & 0 deletions tests/DependencyInjection/DocusignExtensionTest.php
Expand Up @@ -27,6 +27,7 @@
use DocusignBundle\Grant\GrantInterface;
use DocusignBundle\Grant\JwtGrant;
use DocusignBundle\Routing\DocusignLoader;
use DocusignBundle\Tests\ProphecyTrait;
use DocusignBundle\TokenEncoder\TokenEncoder;
use DocusignBundle\TokenEncoder\TokenEncoderInterface;
use DocusignBundle\Translator\TranslatorAwareInterface;
Expand All @@ -46,6 +47,8 @@

class DocusignExtensionTest extends TestCase
{
use ProphecyTrait;

public const DEFAULT_CONFIG = ['docusign' => [
'demo' => false,
'enable_profiler' => false,
Expand Down
2 changes: 2 additions & 0 deletions tests/EnvelopeBuilderTest.php
Expand Up @@ -26,6 +26,8 @@

class EnvelopeBuilderTest extends TestCase
{
use ProphecyTrait;

private $loggerProphecyMock;
private $routerProphecyMock;
private $fileSystemProphecyMock;
Expand Down
3 changes: 3 additions & 0 deletions tests/EnvelopeCreator/CreateDocumentTest.php
Expand Up @@ -16,13 +16,16 @@
use DocuSign\eSign\Model\Document;
use DocusignBundle\EnvelopeBuilderInterface;
use DocusignBundle\EnvelopeCreator\CreateDocument;
use DocusignBundle\Tests\ProphecyTrait;
use League\Flysystem\FileNotFoundException;
use League\Flysystem\FilesystemInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;

class CreateDocumentTest extends TestCase
{
use ProphecyTrait;

private $envelopeBuilderProphecyMock;
private $fileSystemProphecyMock;

Expand Down

0 comments on commit 9302e22

Please sign in to comment.