From 95bf167772a068cf80635bf2831639a065a5ce53 Mon Sep 17 00:00:00 2001 From: dante di domenico Date: Wed, 22 Oct 2025 12:39:55 +0200 Subject: [PATCH 01/14] feat: be 6, phpunit 11 --- .phpunit.cache/test-results | 1 + composer.json | 24 +++---- phpunit.xml.dist | 44 +++++++------ src/Authenticator/AlbAuthenticator.php | 16 ++--- .../Adapter/AwsS3CloudFrontAdapter.php | 10 ++- src/Filesystem/Adapter/S3Adapter.php | 2 +- src/Mailer/Transport/SesTransport.php | 2 +- src/Mailer/Transport/SnsTransport.php | 2 +- .../Authenticator/AlbAuthenticatorTest.php | 66 +++++++++---------- tests/TestCase/AwsConfigTraitTest.php | 12 ++-- .../Adapter/AwsS3CloudFrontAdapterTest.php | 44 ++++--------- .../Filesystem/Adapter/S3AdapterTest.php | 25 +++---- .../Mailer/Transport/SesTransportTest.php | 20 +++--- .../Mailer/Transport/SnsTransportTest.php | 16 +++-- tests/TestCase/PluginTest.php | 13 ++-- tests/bootstrap.php | 15 +++-- 16 files changed, 160 insertions(+), 152 deletions(-) create mode 100644 .phpunit.cache/test-results diff --git a/.phpunit.cache/test-results b/.phpunit.cache/test-results new file mode 100644 index 0000000..c7366e3 --- /dev/null +++ b/.phpunit.cache/test-results @@ -0,0 +1 @@ +{"version":2,"defects":[],"times":{"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticate":0.019,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateMissingCredentials":0.001,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateMalformedToken":0.002,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateMalformedJsonInToken":0.003,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateMissingKid":0.003,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateKeyNotFound":0.011,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateExpiredToken":0.012,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateInvalidSignature":0.025,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateInvalidAlgorithm":0.004,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateMissingSub":0.01,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateIdentify":0.01,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateIdentifyFailure":0.011,"BEdita\\AWS\\Test\\TestCase\\AwsConfigTraitTest::testReformatCredentials with data set \"no username, no password\"":0.002,"BEdita\\AWS\\Test\\TestCase\\AwsConfigTraitTest::testReformatCredentials with data set \"username, no password\"":0.001,"BEdita\\AWS\\Test\\TestCase\\AwsConfigTraitTest::testReformatCredentials with data set \"username, password\"":0.002,"BEdita\\AWS\\Test\\TestCase\\AwsConfigTraitTest::testReformatCredentials with data set \"preserve existing values\"":0.001,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testConstruct":0.122,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testConstructMissingCloudFrontClient":0.007,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testConstructWithDistribution":0.016,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testCopy":0.024,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testCopyCloudFrontNotExistingObject":0.032,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testCopyCloudFrontExistingObject":0.024,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testDelete":0.011,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testDeleteCloudFrontNotExistingObject":0.016,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testDeleteCloudFrontExistingObject":0.027,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testDeleteDir":0.015,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testDeleteDirCloudFront":0.019,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testWrite":0.015,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testWriteCloudFrontNotExistingObject":0.028,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testWriteCloudFrontExistingObject":0.019,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testInitialize with data set \"empty bucket\"":0.001,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testInitialize with data set \"bucket not a string\"":0.001,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testInitialize with data set \"host\"":0.001,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testInitialize with data set \"host, bucket preserved\"":0.001,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testInitialize with data set \"prefix not a string\"":0.001,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testInitialize with data set \"path\"":0.001,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testInitialize with data set \"path, prefix preserved\"":0.001,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testGetClient":0.011,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testBuildAdapter":0.009,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testBuildAdapterCloudFront":0.01,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testGetPublicUrl":0,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testGetPublicUrlDefaultS3Url":0.025,"BEdita\\AWS\\Test\\TestCase\\Mailer\\Transport\\SesTransportTest::testConstruct":0.006,"BEdita\\AWS\\Test\\TestCase\\Mailer\\Transport\\SesTransportTest::testSend with data set \"simple\"":0.005,"BEdita\\AWS\\Test\\TestCase\\Mailer\\Transport\\SnsTransportTest::testConstruct":0.005,"BEdita\\AWS\\Test\\TestCase\\Mailer\\Transport\\SnsTransportTest::testSend with data set \"simple\"":0.004,"BEdita\\AWS\\Test\\TestCase\\Mailer\\Transport\\SnsTransportTest::testSend with data set \"with sender\"":0.005,"BEdita\\AWS\\Test\\TestCase\\Mailer\\Transport\\SnsTransportTest::testSend with data set \"with SMS type\"":0.005,"BEdita\\AWS\\Test\\TestCase\\Mailer\\Transport\\SnsTransportTest::testSend with data set \"with sender and SMS type\"":0.004,"BEdita\\AWS\\Test\\TestCase\\PluginTest::testBootstrap":0.004}} \ No newline at end of file diff --git a/composer.json b/composer.json index 47fe43d..f841d27 100644 --- a/composer.json +++ b/composer.json @@ -6,23 +6,24 @@ "php": ">= 8.3", "ext-openssl": "*", "aws/aws-sdk-php": "^3.222", - "bedita/core": "^5.36", - "cakephp/cakephp": "^4.5", + "bedita/core": "6.x-dev", + "cakephp/cakephp": "^5", "lcobucci/jwt": "^4.2.1", "league/flysystem": "^2.4.3", "league/flysystem-aws-s3-v3": "^2.4.3", "guzzlehttp/guzzle": "^7.4" }, "require-dev": { + "cakephp/authentication": "^3.3", "cakephp/cakephp-codesniffer": "~4.7.0", - "phpunit/phpunit": "^9.6", - "phpstan/phpstan": "~1.10", - "cakephp/authentication": "^2.9", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan-phpunit": "^1.1" + "phpstan/phpstan": "^1.10", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^11.5 || ^12.1" }, "suggest": { - "cakephp/authentication": "^2.9" + "cakephp/authentication": "^3.3" }, "autoload": { "psr-4": { @@ -35,10 +36,11 @@ } }, "scripts": { - "cs-check": "vendor/bin/phpcs", - "cs-fix": "vendor/bin/phpcbf", + "cs-check": "vendor/bin/phpcs --colors -p --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests", + "cs-fix": "vendor/bin/phpcbf --colors --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests", "stan": "vendor/bin/phpstan analyse", - "test": "vendor/bin/phpunit --colors=always" + "test": "vendor/bin/phpunit --colors=always", + "coverage": "vendor/bin/phpunit --colors=always --coverage-html coverage" }, "config": { "allow-plugins": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 96ac958..633780b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -10,24 +10,28 @@ ~ ~ See LICENSE.LGPL or for more details. --> - - - - src/ - - - - - - - - - - tests/TestCase/ - - - - - - + + + + ./src/ + + + + + + + + + + ./tests/TestCase/ + + diff --git a/src/Authenticator/AlbAuthenticator.php b/src/Authenticator/AlbAuthenticator.php index 0358609..ada914f 100644 --- a/src/Authenticator/AlbAuthenticator.php +++ b/src/Authenticator/AlbAuthenticator.php @@ -19,9 +19,9 @@ use Authentication\Authenticator\Result; use Authentication\Authenticator\ResultInterface; use Authentication\Authenticator\TokenAuthenticator; -use Authentication\Identifier\IdentifierInterface; +use Authentication\Identifier\JwtSubjectIdentifier; use Cake\Cache\Cache; -use Cake\I18n\FrozenTime; +use Cake\I18n\DateTime; use Exception; use GuzzleHttp\Client; use GuzzleHttp\RequestOptions; @@ -60,11 +60,11 @@ class AlbAuthenticator extends TokenAuthenticator * * @var array */ - protected $_defaultConfig = [ + protected array $_defaultConfig = [ 'header' => 'x-amzn-oidc-data', 'returnPayload' => true, 'fields' => [ - IdentifierInterface::CREDENTIAL_JWT_SUBJECT => IdentifierInterface::CREDENTIAL_JWT_SUBJECT, + JwtSubjectIdentifier::CREDENTIAL_JWT_SUBJECT => JwtSubjectIdentifier::CREDENTIAL_JWT_SUBJECT, ], 'publicKeyEndpoint' => null, 'region' => null, @@ -114,7 +114,7 @@ public function authenticate(ServerRequestInterface $request): ResultInterface return new Result(null, ResultInterface::FAILURE_CREDENTIALS_INVALID); } - if (empty($result[IdentifierInterface::CREDENTIAL_JWT_SUBJECT])) { + if (empty($result[JwtSubjectIdentifier::CREDENTIAL_JWT_SUBJECT])) { return new Result(null, ResultInterface::FAILURE_CREDENTIALS_MISSING); } @@ -189,7 +189,7 @@ function () use ($keyId): string { protected function decodeToken(string $token): ?array { $jwt = (new TokenParser(new class implements Decoder { - /** @inheritdoc */ + /** @inheritDoc */ public function jsonDecode(string $json) { try { @@ -199,7 +199,7 @@ public function jsonDecode(string $json) } } - /** @inheritdoc */ + /** @inheritDoc */ public function base64UrlDecode(string $data): string { return SodiumBase64Polyfill::base642bin( @@ -216,7 +216,7 @@ public function base64UrlDecode(string $data): string (new Validator())->assert( $jwt, new SignedWith(Sha256::create(), $this->getKey($kid)), - new LooseValidAt(new FrozenClock(FrozenTime::now())), + new LooseValidAt(new FrozenClock(DateTime::now())), ); return $jwt->claims()->all(); diff --git a/src/Filesystem/Adapter/AwsS3CloudFrontAdapter.php b/src/Filesystem/Adapter/AwsS3CloudFrontAdapter.php index 6c5fe68..6d7a5d6 100644 --- a/src/Filesystem/Adapter/AwsS3CloudFrontAdapter.php +++ b/src/Filesystem/Adapter/AwsS3CloudFrontAdapter.php @@ -58,8 +58,14 @@ class AwsS3CloudFrontAdapter extends AwsS3V3Adapter * @param bool $streamReads Whether reads should be streamed. * @param \Aws\CloudFront\CloudFrontClient|null $cloudfrontClient CloudFront client instance, or `null`. */ - public function __construct(S3ClientInterface $client, string $bucket, string $prefix = '', array $options = [], $streamReads = true, ?CloudFrontClient $cloudfrontClient = null) - { + public function __construct( + S3ClientInterface $client, + string $bucket, + string $prefix = '', + array $options = [], + $streamReads = true, + ?CloudFrontClient $cloudfrontClient = null + ) { parent::__construct($client, $bucket, $prefix, null, null, $options, $streamReads); if (!empty($options['distributionId']) && $cloudfrontClient === null) { diff --git a/src/Filesystem/Adapter/S3Adapter.php b/src/Filesystem/Adapter/S3Adapter.php index 359683d..b018394 100644 --- a/src/Filesystem/Adapter/S3Adapter.php +++ b/src/Filesystem/Adapter/S3Adapter.php @@ -34,7 +34,7 @@ class S3Adapter extends FilesystemAdapter /** * @inheritDoc */ - protected $_defaultConfig = [ + protected array $_defaultConfig = [ 'region' => null, 'bucket' => null, 'version' => 'latest', diff --git a/src/Mailer/Transport/SesTransport.php b/src/Mailer/Transport/SesTransport.php index ce372a1..709e7f8 100644 --- a/src/Mailer/Transport/SesTransport.php +++ b/src/Mailer/Transport/SesTransport.php @@ -39,7 +39,7 @@ class SesTransport extends AbstractTransport /** * @inheritDoc */ - protected $_defaultConfig = [ + protected array $_defaultConfig = [ 'region' => null, 'version' => 'latest', ]; diff --git a/src/Mailer/Transport/SnsTransport.php b/src/Mailer/Transport/SnsTransport.php index 337cc83..e410ae1 100644 --- a/src/Mailer/Transport/SnsTransport.php +++ b/src/Mailer/Transport/SnsTransport.php @@ -32,7 +32,7 @@ class SnsTransport extends AbstractTransport /** * @inheritDoc */ - protected $_defaultConfig = [ + protected array $_defaultConfig = [ 'region' => null, 'version' => 'latest', 'smsType' => null, diff --git a/tests/TestCase/Authenticator/AlbAuthenticatorTest.php b/tests/TestCase/Authenticator/AlbAuthenticatorTest.php index 3154e58..545399e 100644 --- a/tests/TestCase/Authenticator/AlbAuthenticatorTest.php +++ b/tests/TestCase/Authenticator/AlbAuthenticatorTest.php @@ -19,10 +19,10 @@ use ArrayObject; use Authentication\Authenticator\ResultInterface; use Authentication\Identifier\CallbackIdentifier; -use Authentication\Identifier\IdentifierInterface; +use Authentication\Identifier\JwtSubjectIdentifier; use BEdita\AWS\Authenticator\AlbAuthenticator; use Cake\Http\ServerRequest; -use Cake\I18n\FrozenTime; +use Cake\I18n\DateTime; use Cake\Utility\Text; use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Handler\MockHandler; @@ -37,13 +37,13 @@ use Lcobucci\JWT\Signer\Key\InMemory; use Lcobucci\JWT\Signer\None; use Lcobucci\JWT\Token\Builder; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; /** * Test {@see \BEdita\AWS\Authenticator\AlbAuthenticator}. - * - * @covers \BEdita\AWS\Authenticator\AlbAuthenticator */ +#[CoversClass(AlbAuthenticator::class)] class AlbAuthenticatorTest extends TestCase { /** @@ -148,9 +148,9 @@ public function testAuthenticate(): void ); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) - ->issuedAt(FrozenTime::now()) - ->canOnlyBeUsedAfter(FrozenTime::now()) - ->expiresAt(FrozenTime::now()->addMinute()) + ->issuedAt(DateTime::now()) + ->canOnlyBeUsedAfter(DateTime::now()) + ->expiresAt(DateTime::now()->addMinutes(1)) ->withHeader('kid', $this->keyId) ->relatedTo('gustavo@example.com') ->getToken(Sha256::create(), $this->privateKey) @@ -288,9 +288,9 @@ public function testAuthenticateMissingKid(): void ); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) - ->issuedAt(FrozenTime::now()->subDay()) - ->canOnlyBeUsedAfter(FrozenTime::now()->subDay()) - ->expiresAt(FrozenTime::now()->subDay()->addMinute()) + ->issuedAt(DateTime::now()->subDays(1)) + ->canOnlyBeUsedAfter(DateTime::now()->subDays(1)) + ->expiresAt(DateTime::now()->subDays(1)->addMinutes(1)) ->relatedTo('gustavo@example.com') ->getToken(Sha256::create(), $this->privateKey) ->toString(); @@ -329,9 +329,9 @@ public function testAuthenticateKeyNotFound(): void ); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) - ->issuedAt(FrozenTime::now()->subDay()) - ->canOnlyBeUsedAfter(FrozenTime::now()->subDay()) - ->expiresAt(FrozenTime::now()->subDay()->addMinute()) + ->issuedAt(DateTime::now()->subDays(1)) + ->canOnlyBeUsedAfter(DateTime::now()->subDays(1)) + ->expiresAt(DateTime::now()->subDays(1)->addMinutes(1)) ->withHeader('kid', $this->keyId) ->relatedTo('gustavo@example.com') ->getToken(Sha256::create(), $this->privateKey) @@ -372,9 +372,9 @@ public function testAuthenticateExpiredToken(): void ); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) - ->issuedAt(FrozenTime::now()->subDay()) - ->canOnlyBeUsedAfter(FrozenTime::now()->subDay()) - ->expiresAt(FrozenTime::now()->subDay()->addMinute()) + ->issuedAt(DateTime::now()->subDays(1)) + ->canOnlyBeUsedAfter(DateTime::now()->subDays(1)) + ->expiresAt(DateTime::now()->subDays(1)->addMinutes(1)) ->withHeader('kid', $this->keyId) ->relatedTo('gustavo@example.com') ->getToken(Sha256::create(), $this->privateKey) @@ -418,9 +418,9 @@ public function testAuthenticateInvalidSignature(): void $privateKey = InMemory::plainText($privateKey); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) - ->issuedAt(FrozenTime::now()) - ->canOnlyBeUsedAfter(FrozenTime::now()) - ->expiresAt(FrozenTime::now()->addMinute()) + ->issuedAt(DateTime::now()) + ->canOnlyBeUsedAfter(DateTime::now()) + ->expiresAt(DateTime::now()->addMinutes(1)) ->withHeader('kid', $this->keyId) ->relatedTo('gustavo@example.com') ->getToken(Sha256::create(), $privateKey) @@ -459,9 +459,9 @@ public function testAuthenticateInvalidAlgorithm(): void ); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) - ->issuedAt(FrozenTime::now()) - ->canOnlyBeUsedAfter(FrozenTime::now()) - ->expiresAt(FrozenTime::now()->addMinute()) + ->issuedAt(DateTime::now()) + ->canOnlyBeUsedAfter(DateTime::now()) + ->expiresAt(DateTime::now()->addMinutes(1)) ->withHeader('kid', $this->keyId) ->relatedTo('gustavo@example.com') ->getToken(new None(), InMemory::plainText('key')) @@ -500,9 +500,9 @@ public function testAuthenticateMissingSub(): void ); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) - ->issuedAt(FrozenTime::now()) - ->canOnlyBeUsedAfter(FrozenTime::now()) - ->expiresAt(FrozenTime::now()->addMinute()) + ->issuedAt(DateTime::now()) + ->canOnlyBeUsedAfter(DateTime::now()) + ->expiresAt(DateTime::now()->addMinutes(1)) ->withHeader('kid', $this->keyId) ->getToken(Sha256::create(), $this->privateKey) ->toString(); @@ -540,7 +540,7 @@ public function testAuthenticateIdentify(): void [ 'returnPayload' => false, 'fields' => [ - 'email' => IdentifierInterface::CREDENTIAL_JWT_SUBJECT, + 'email' => JwtSubjectIdentifier::CREDENTIAL_JWT_SUBJECT, ], 'region' => 'eu-south-1', 'guzzleClient' => ['handler' => $this->handler], @@ -548,9 +548,9 @@ public function testAuthenticateIdentify(): void ); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) - ->issuedAt(FrozenTime::now()) - ->canOnlyBeUsedAfter(FrozenTime::now()) - ->expiresAt(FrozenTime::now()->addMinute()) + ->issuedAt(DateTime::now()) + ->canOnlyBeUsedAfter(DateTime::now()) + ->expiresAt(DateTime::now()->addMinutes(1)) ->withHeader('kid', $this->keyId) ->relatedTo('gustavo@example.com') ->getToken(Sha256::create(), $this->privateKey) @@ -596,7 +596,7 @@ public function testAuthenticateIdentifyFailure(): void [ 'returnPayload' => false, 'fields' => [ - 'email' => IdentifierInterface::CREDENTIAL_JWT_SUBJECT, + 'email' => JwtSubjectIdentifier::CREDENTIAL_JWT_SUBJECT, ], 'region' => 'eu-south-1', 'guzzleClient' => ['handler' => $this->handler], @@ -604,9 +604,9 @@ public function testAuthenticateIdentifyFailure(): void ); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) - ->issuedAt(FrozenTime::now()) - ->canOnlyBeUsedAfter(FrozenTime::now()) - ->expiresAt(FrozenTime::now()->addMinute()) + ->issuedAt(DateTime::now()) + ->canOnlyBeUsedAfter(DateTime::now()) + ->expiresAt(DateTime::now()->addMinutes(1)) ->withHeader('kid', $this->keyId) ->relatedTo('gustavo@example.com') ->getToken(Sha256::create(), $this->privateKey) diff --git a/tests/TestCase/AwsConfigTraitTest.php b/tests/TestCase/AwsConfigTraitTest.php index 4f51f49..04fb5fa 100644 --- a/tests/TestCase/AwsConfigTraitTest.php +++ b/tests/TestCase/AwsConfigTraitTest.php @@ -16,13 +16,16 @@ namespace BEdita\AWS\Test\TestCase; use BEdita\AWS\AwsConfigTrait; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; /** * Test {@see \BEdita\AWS\AwsConfigTrait}. - * - * @coversDefaultClass \BEdita\AWS\AwsConfigTrait */ +#[CoversClass(AwsConfigTrait::class)] +#[CoversMethod(AwsConfigTrait::class, 'reformatCredentials')] class AwsConfigTraitTest extends TestCase { /** @@ -30,7 +33,7 @@ class AwsConfigTraitTest extends TestCase * * @return array[] */ - public function reformatCredentialsProvider(): array + public static function reformatCredentialsProvider(): array { return [ 'no username, no password' => [ @@ -116,9 +119,8 @@ public function reformatCredentialsProvider(): array * @param array $expected Expected result. * @param array $config Input configuration. * @return void - * @dataProvider reformatCredentialsProvider() - * @covers ::reformatCredentials() */ + #[DataProvider('reformatCredentialsProvider')] public function testReformatCredentials(array $expected, array $config): void { $subject = new class { diff --git a/tests/TestCase/Filesystem/Adapter/AwsS3CloudFrontAdapterTest.php b/tests/TestCase/Filesystem/Adapter/AwsS3CloudFrontAdapterTest.php index 46e24c8..4a83f75 100644 --- a/tests/TestCase/Filesystem/Adapter/AwsS3CloudFrontAdapterTest.php +++ b/tests/TestCase/Filesystem/Adapter/AwsS3CloudFrontAdapterTest.php @@ -24,13 +24,25 @@ use DomainException; use InvalidArgumentException; use League\Flysystem\Config; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; use PHPUnit\Framework\TestCase; /** * Test {@see \BEdita\AWS\Filesystem\Adapter\AwsS3CloudFrontAdapter}. - * - * @coversDefaultClass \BEdita\AWS\Filesystem\Adapter\AwsS3CloudFrontAdapter */ +#[CoversClass(AwsS3CloudFrontAdapter::class)] +#[CoversMethod(AwsS3CloudFrontAdapter::class, '__construct')] +#[CoversMethod(AwsS3CloudFrontAdapter::class, 'applyCloudFrontPathPrefix')] +#[CoversMethod(AwsS3CloudFrontAdapter::class, 'copy')] +#[CoversMethod(AwsS3CloudFrontAdapter::class, 'createCloudFrontInvalidation')] +#[CoversMethod(AwsS3CloudFrontAdapter::class, 'delete')] +#[CoversMethod(AwsS3CloudFrontAdapter::class, 'deleteDirectory')] +#[CoversMethod(AwsS3CloudFrontAdapter::class, 'getCloudFrontClient')] +#[CoversMethod(AwsS3CloudFrontAdapter::class, 'getDistributionId')] +#[CoversMethod(AwsS3CloudFrontAdapter::class, 'hasCloudFrontConfig')] +#[CoversMethod(AwsS3CloudFrontAdapter::class, 'write')] + class AwsS3CloudFrontAdapterTest extends TestCase { /** @@ -77,10 +89,6 @@ protected static function cloudFrontClientFactory(?callable $handler = null): Cl * methods. * * @return void - * @covers ::__construct() - * @covers ::getCloudFrontClient() - * @covers ::hasCloudFrontConfig() - * @covers ::getDistributionId() */ public function testConstruct(): void { @@ -96,7 +104,6 @@ public function testConstruct(): void * Test {@see AwsS3CloudFrontAdapter} constructor when CloudFront client is missing. * * @return void - * @covers ::__construct() */ public function testConstructMissingCloudFrontClient(): void { @@ -112,10 +119,6 @@ public function testConstructMissingCloudFrontClient(): void * methods with a distribution ID. * * @return void - * @covers ::__construct() - * @covers ::getCloudFrontClient() - * @covers ::hasCloudFrontConfig() - * @covers ::getDistributionId() */ public function testConstructWithDistribution(): void { @@ -133,7 +136,6 @@ public function testConstructWithDistribution(): void * Test {@see AwsS3CloudFrontAdapter::copy()} method. * * @return void - * @covers ::copy() */ public function testCopy(): void { @@ -173,7 +175,6 @@ public function testCopy(): void * Test {@see AwsS3CloudFrontAdapter::copy()} method with CloudFront config set to a new destination. * * @return void - * @covers ::copy() */ public function testCopyCloudFrontNotExistingObject(): void { @@ -224,9 +225,6 @@ public function testCopyCloudFrontNotExistingObject(): void * Test {@see AwsS3CloudFrontAdapter::copy()} method with CloudFront config set to an existing destination. * * @return void - * @covers ::copy() - * @covers ::applyCloudFrontPathPrefix() - * @covers ::createCloudFrontInvalidation() */ public function testCopyCloudFrontExistingObject(): void { @@ -285,7 +283,6 @@ public function testCopyCloudFrontExistingObject(): void * Test {@see AwsS3CloudFrontAdapter::delete()} method. * * @return void - * @covers ::delete() */ public function testDelete(): void { @@ -313,7 +310,6 @@ public function testDelete(): void * Test {@see AwsS3CloudFrontAdapter::delete()} method with CloudFront config set to a new destination. * * @return void - * @covers ::delete() */ public function testDeleteCloudFrontNotExistingObject(): void { @@ -353,9 +349,6 @@ public function testDeleteCloudFrontNotExistingObject(): void * Test {@see AwsS3CloudFrontAdapter::delete()} method with CloudFront config set to an existing destination. * * @return void - * @covers ::delete() - * @covers ::applyCloudFrontPathPrefix() - * @covers ::createCloudFrontInvalidation() */ public function testDeleteCloudFrontExistingObject(): void { @@ -412,7 +405,6 @@ public function testDeleteCloudFrontExistingObject(): void * Test {@see AwsS3CloudFrontAdapter::deleteDirectory()} method. * * @return void - * @covers ::deleteDirectory() */ public function testDeleteDir(): void { @@ -446,9 +438,6 @@ public function testDeleteDir(): void * Test {@see AwsS3CloudFrontAdapter::deleteDirectory()} method with CloudFront config set. * * @return void - * @covers ::deleteDirectory() - * @covers ::applyCloudFrontPathPrefix() - * @covers ::createCloudFrontInvalidation() */ public function testDeleteDirCloudFront(): void { @@ -497,7 +486,6 @@ public function testDeleteDirCloudFront(): void * Test {@see AwsS3CloudFrontAdapter::write()} method. * * @return void - * @covers ::write() */ public function testWrite(): void { @@ -526,7 +514,6 @@ public function testWrite(): void * Test {@see AwsS3CloudFrontAdapter::write()} method with CloudFront config set to a new destination. * * @return void - * @covers ::write() */ public function testWriteCloudFrontNotExistingObject(): void { @@ -567,9 +554,6 @@ public function testWriteCloudFrontNotExistingObject(): void * Test {@see AwsS3CloudFrontAdapter::write()} method with CloudFront config set to an existing destination. * * @return void - * @covers ::write() - * @covers ::applyCloudFrontPathPrefix() - * @covers ::createCloudFrontInvalidation() */ public function testWriteCloudFrontExistingObject(): void { diff --git a/tests/TestCase/Filesystem/Adapter/S3AdapterTest.php b/tests/TestCase/Filesystem/Adapter/S3AdapterTest.php index 2f338b3..942a73a 100644 --- a/tests/TestCase/Filesystem/Adapter/S3AdapterTest.php +++ b/tests/TestCase/Filesystem/Adapter/S3AdapterTest.php @@ -21,13 +21,22 @@ use BEdita\AWS\Filesystem\Adapter\S3Adapter; use Exception; use InvalidArgumentException; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; /** * Test {@see \BEdita\AWS\Filesystem\Adapter\S3Adapter} - * - * @coversDefaultClass \BEdita\AWS\Filesystem\Adapter\S3Adapter */ +#[CoversClass(S3Adapter::class)] +#[CoversMethod(S3Adapter::class, 'buildAdapter')] +#[CoversMethod(S3Adapter::class, 'getClient')] +#[CoversMethod(S3Adapter::class, 'getCloudFrontClient')] +#[CoversMethod(S3Adapter::class, 'getPublicUrl')] +#[CoversMethod(S3Adapter::class, 'initialize')] +#[CoversMethod(S3Adapter::class, 'reformatConfig')] +#[CoversMethod(S3Adapter::class, 'send')] class S3AdapterTest extends TestCase { /** @@ -35,7 +44,7 @@ class S3AdapterTest extends TestCase * * @return array */ - public function initializeProvider(): array + public static function initializeProvider(): array { return [ 'empty bucket' => [ @@ -174,10 +183,8 @@ public function initializeProvider(): array * @param array|\Exception $expected Expected outcome. * @param array $config Adapter configuration. * @return void - * @dataProvider initializeProvider() - * @covers ::initialize() - * @covers ::reformatConfig() */ + #[DataProvider('initializeProvider')] public function testInitialize($expected, array $config): void { if ($expected instanceof Exception) { @@ -196,8 +203,6 @@ public function testInitialize($expected, array $config): void * Test {@see S3Adapter::getClient()} and {@see S3Adapter::getCloudFrontClient()} methods. * * @return void - * @covers ::getClient() - * @covers ::getCloudFrontClient() */ public function testGetClient(): void { @@ -248,7 +253,6 @@ public function getCloudFrontClient(): CloudFrontClient * Test {@see S3Adapter::buildAdapter()} method. * * @return void - * @covers ::buildAdapter() */ public function testBuildAdapter(): void { @@ -276,7 +280,6 @@ public function testBuildAdapter(): void * Test {@see S3Adapter::buildAdapter()} method with a CloudFront distribution. * * @return void - * @covers ::buildAdapter() */ public function testBuildAdapterCloudFront(): void { @@ -304,7 +307,6 @@ public function testBuildAdapterCloudFront(): void * Test {@see S3Adapter::getPublicUrl()} method. * * @return void - * @covers ::getPublicUrl() */ public function testGetPublicUrl(): void { @@ -331,7 +333,6 @@ public function testGetPublicUrl(): void * Test {@see S3Adapter::getPublicUrl()} method falling back to default AWS S3 URL.. * * @return void - * @covers ::getPublicUrl() */ public function testGetPublicUrlDefaultS3Url(): void { diff --git a/tests/TestCase/Mailer/Transport/SesTransportTest.php b/tests/TestCase/Mailer/Transport/SesTransportTest.php index b6587b4..56fb86a 100644 --- a/tests/TestCase/Mailer/Transport/SesTransportTest.php +++ b/tests/TestCase/Mailer/Transport/SesTransportTest.php @@ -19,24 +19,27 @@ use Aws\Result; use Aws\Ses\SesClient; use BEdita\AWS\Mailer\Transport\SesTransport; -use Cake\I18n\FrozenTime; +use Cake\I18n\DateTime; use Cake\Mailer\Message; use Cake\Utility\Text; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; /** * Test {@see \BEdita\AWS\Mailer\Transport\SesTransport}. - * - * @coversDefaultClass \BEdita\AWS\Mailer\Transport\SesTransport */ +#[CoversClass(SesTransport::class)] +#[CoversMethod(SesTransport::class, '__construct')] +#[CoversMethod(SesTransport::class, 'getClient')] +#[CoversMethod(SesTransport::class, 'send')] class SesTransportTest extends TestCase { /** * Test {@see SesTransport} constructor and {@see SesTransport::getClient()} methods. * * @return void - * @covers ::__construct() - * @covers ::getClient() */ public function testConstruct(): void { @@ -81,10 +84,10 @@ public function getClient(): SesClient * * @return array */ - public function sendProvider(): array + public static function sendProvider(): array { $messageId = sprintf('<%s@example.com>', Text::uuid()); - $now = FrozenTime::now(); + $now = DateTime::now(); return [ 'simple' => [ @@ -122,9 +125,8 @@ public function sendProvider(): array * @param array $config Client configuration. * @param \Cake\Mailer\Message $email Email message to send. * @return void - * @dataProvider sendProvider() - * @covers ::send() */ + #[DataProvider('sendProvider')] public function testSend(string $expectedHeaders, string $expectedMessage, array $config, Message $email): void { $invocations = 0; diff --git a/tests/TestCase/Mailer/Transport/SnsTransportTest.php b/tests/TestCase/Mailer/Transport/SnsTransportTest.php index c8ebca7..281635f 100644 --- a/tests/TestCase/Mailer/Transport/SnsTransportTest.php +++ b/tests/TestCase/Mailer/Transport/SnsTransportTest.php @@ -20,21 +20,24 @@ use Aws\Sns\SnsClient; use BEdita\AWS\Mailer\Transport\SnsTransport; use Cake\Mailer\Message; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; /** * Test {@see \BEdita\AWS\Mailer\Transport\SnsTransport}. - * - * @coversDefaultClass \BEdita\AWS\Mailer\Transport\SnsTransport */ +#[CoversClass(SnsTransport::class)] +#[CoversMethod(SnsTransport::class, '__construct')] +#[CoversMethod(SnsTransport::class, 'getClient')] +#[CoversMethod(SnsTransport::class, 'send')] class SnsTransportTest extends TestCase { /** * Test {@see SnsTransport} constructor and {@see SnsTransport::getClient()} methods. * * @return void - * @covers ::__construct() - * @covers ::getClient() */ public function testConstruct(): void { @@ -80,7 +83,7 @@ public function getClient(): SnsClient * * @return array */ - public function sendProvider(): array + public static function sendProvider(): array { return [ 'simple' => [ @@ -157,9 +160,8 @@ public function sendProvider(): array * @param array $config Client configuration. * @param \Cake\Mailer\Message $email Email to send. * @return void - * @dataProvider sendProvider() - * @covers ::send() */ + #[DataProvider('sendProvider')] public function testSend(array $expected, array $expectedPayload, array $config, Message $email): void { $invocations = 0; diff --git a/tests/TestCase/PluginTest.php b/tests/TestCase/PluginTest.php index 9e53114..32214f5 100644 --- a/tests/TestCase/PluginTest.php +++ b/tests/TestCase/PluginTest.php @@ -17,16 +17,18 @@ use BEdita\AWS\Plugin; use BEdita\Core\Filesystem\FilesystemRegistry; -use BEdita\Core\Mailer\Email; use Cake\Http\BaseApplication; use Cake\Http\MiddlewareQueue; +use Cake\Mailer\Mailer; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; use PHPUnit\Framework\TestCase; /** * Test {@see \BEdita\AWS\Plugin}. - * - * @coversDefaultClass \BEdita\AWS\Plugin */ +#[CoversClass(Plugin::class)] +#[CoversMethod(Plugin::class, 'bootstrap')] class PluginTest extends TestCase { /** @@ -50,7 +52,6 @@ protected function setUp(): void * Test {@see Plugin::bootstrap()} method. * * @return void - * @covers ::bootstrap() */ public function testBootstrap(): void { @@ -61,7 +62,7 @@ public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue } }; - $mailers = Email::getDsnClassMap(); + $mailers = Mailer::getDsnClassMap(); static::assertArrayNotHasKey('ses', $mailers); static::assertArrayNotHasKey('sns', $mailers); @@ -70,7 +71,7 @@ public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue $this->plugin->bootstrap($app); - $mailers = Email::getDsnClassMap(); + $mailers = Mailer::getDsnClassMap(); static::assertArrayHasKey('ses', $mailers); static::assertArrayHasKey('sns', $mailers); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 38fa40d..858c2a8 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -26,7 +26,6 @@ use Cake\Cache\Engine\NullEngine; use Cake\Core\Configure; use Cake\Datasource\ConnectionManager; -use Cake\I18n\FrozenTime; use Cake\Log\Engine\ConsoleLog; use Cake\Log\Log; use Cake\ORM\TableRegistry; @@ -47,16 +46,21 @@ unset($findRoot); chdir($root); -require_once 'vendor/cakephp/cakephp/src/basics.php'; -require_once 'vendor/autoload.php'; +require dirname(__DIR__) . '/vendor/autoload.php'; + +define('ROOT', dirname(__DIR__)); +define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'vendor' . DS . 'cakephp' . DS . 'cakephp'); +define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS); +define('CAKE', CORE_PATH . 'src' . DS); + +require CORE_PATH . 'config' . DS . 'bootstrap.php'; +require CAKE . 'functions.php'; -define('ROOT', $root . DS . 'tests' . DS); define('APP', ROOT . 'TestApp' . DS); define('TMP', sys_get_temp_dir() . DS); define('LOGS', ROOT . DS . 'logs' . DS); define('CONFIG', ROOT . DS . 'config' . DS); define('CACHE', TMP . 'cache' . DS); -define('CORE_PATH', $root . DS . 'vendor' . DS . 'cakephp' . DS . 'cakephp' . DS); Configure::write('debug', true); Configure::write('App', [ @@ -99,7 +103,6 @@ Router::reload(); Security::setSalt('BEDITA'); -FrozenTime::setTestNow('2022-01-01T00:00:00+01:00'); // clear all before running tests TableRegistry::getTableLocator()->clear(); From 1bbec9d4a4a16637709054f2c55fb8d5813cabed Mon Sep 17 00:00:00 2001 From: dante di domenico Date: Wed, 22 Oct 2025 13:01:26 +0200 Subject: [PATCH 02/14] fix: use phpunit 11 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f841d27..0cdf870 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "phpstan/extension-installer": "^1.0", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^11.5 || ^12.1" + "phpunit/phpunit": "^11.5" }, "suggest": { "cakephp/authentication": "^3.3" From cfee7ef3d412e8b9d93f37e74c9674a3ceb4754c Mon Sep 17 00:00:00 2001 From: dante di domenico Date: Wed, 22 Oct 2025 13:06:03 +0200 Subject: [PATCH 03/14] chore fix: drop covers method send --- tests/TestCase/Filesystem/Adapter/S3AdapterTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/TestCase/Filesystem/Adapter/S3AdapterTest.php b/tests/TestCase/Filesystem/Adapter/S3AdapterTest.php index 942a73a..4462e2e 100644 --- a/tests/TestCase/Filesystem/Adapter/S3AdapterTest.php +++ b/tests/TestCase/Filesystem/Adapter/S3AdapterTest.php @@ -36,7 +36,6 @@ #[CoversMethod(S3Adapter::class, 'getPublicUrl')] #[CoversMethod(S3Adapter::class, 'initialize')] #[CoversMethod(S3Adapter::class, 'reformatConfig')] -#[CoversMethod(S3Adapter::class, 'send')] class S3AdapterTest extends TestCase { /** From b74b84440096ba58c2b9fbcbf151eae509ade1d9 Mon Sep 17 00:00:00 2001 From: dante di domenico Date: Wed, 22 Oct 2025 14:29:32 +0200 Subject: [PATCH 04/14] chore fix: stan ignore errors --- phpstan.neon.dist | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 181ca55..8169527 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -4,6 +4,8 @@ parameters: paths: - src - tests - phpVersion: 70400 level: 8 - checkMissingIterableValueType: false + ignoreErrors: + - '#type has no value type specified in iterable type array#' + - '#has parameter .* with no value type specified in iterable type array#' + - '#has parameter .* with generic interface .* but does not specify its types#' From dc2cfe3fd488639e304eb33a57454a865ae86ec3 Mon Sep 17 00:00:00 2001 From: dante di domenico Date: Wed, 22 Oct 2025 14:30:09 +0200 Subject: [PATCH 05/14] fix: replace deprecated Sha256::create with new object --- src/Authenticator/AlbAuthenticator.php | 2 +- .../Authenticator/AlbAuthenticatorTest.php | 47 +++++++++++++++---- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/Authenticator/AlbAuthenticator.php b/src/Authenticator/AlbAuthenticator.php index ada914f..22a71df 100644 --- a/src/Authenticator/AlbAuthenticator.php +++ b/src/Authenticator/AlbAuthenticator.php @@ -215,7 +215,7 @@ public function base64UrlDecode(string $data): string (new Validator())->assert( $jwt, - new SignedWith(Sha256::create(), $this->getKey($kid)), + new SignedWith(new Sha256(), $this->getKey($kid)), new LooseValidAt(new FrozenClock(DateTime::now())), ); diff --git a/tests/TestCase/Authenticator/AlbAuthenticatorTest.php b/tests/TestCase/Authenticator/AlbAuthenticatorTest.php index 545399e..7f1907f 100644 --- a/tests/TestCase/Authenticator/AlbAuthenticatorTest.php +++ b/tests/TestCase/Authenticator/AlbAuthenticatorTest.php @@ -32,6 +32,7 @@ use GuzzleHttp\Psr7\Response; use Lcobucci\JWT\Encoding\ChainedFormatter; use Lcobucci\JWT\Encoding\JoseEncoder; +use Lcobucci\JWT\Signer; use Lcobucci\JWT\Signer\Ecdsa\Sha256; use Lcobucci\JWT\Signer\Key; use Lcobucci\JWT\Signer\Key\InMemory; @@ -147,13 +148,14 @@ public function testAuthenticate(): void ['region' => 'eu-south-1', 'guzzleClient' => ['handler' => $this->handler]] ); + $ecdsa = new Sha256(); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()) ->canOnlyBeUsedAfter(DateTime::now()) ->expiresAt(DateTime::now()->addMinutes(1)) ->withHeader('kid', $this->keyId) ->relatedTo('gustavo@example.com') - ->getToken(Sha256::create(), $this->privateKey) + ->getToken($ecdsa, $this->privateKey) ->toString(); $result = $authenticator->authenticate( @@ -256,7 +258,8 @@ public function testAuthenticateMalformedJsonInToken(): void $encoder->base64UrlEncode($encoder->jsonEncode(['typ' => 'JWT', 'alg' => 'ES256', 'kid' => $this->keyId])), $encoder->base64UrlEncode('NOT A JSON'), ); - $token .= sprintf('.%s', $encoder->base64UrlEncode(Sha256::create()->sign($token, $this->privateKey))); + $ecdsa = new Sha256(); + $token .= sprintf('.%s', $encoder->base64UrlEncode($ecdsa->sign($token, $this->privateKey))); $result = $authenticator->authenticate( new ServerRequest(['environment' => ['HTTP_X_AMZN_OIDC_DATA' => $token]]) @@ -287,12 +290,13 @@ public function testAuthenticateMissingKid(): void ['region' => 'eu-south-1', 'guzzleClient' => ['handler' => $this->handler]] ); + $ecdsa = new Sha256(); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()->subDays(1)) ->canOnlyBeUsedAfter(DateTime::now()->subDays(1)) ->expiresAt(DateTime::now()->subDays(1)->addMinutes(1)) ->relatedTo('gustavo@example.com') - ->getToken(Sha256::create(), $this->privateKey) + ->getToken($ecdsa, $this->privateKey) ->toString(); $result = $authenticator->authenticate( @@ -328,13 +332,14 @@ public function testAuthenticateKeyNotFound(): void ['region' => 'eu-south-1', 'guzzleClient' => ['handler' => $handler]] ); + $ecdsa = new Sha256(); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()->subDays(1)) ->canOnlyBeUsedAfter(DateTime::now()->subDays(1)) ->expiresAt(DateTime::now()->subDays(1)->addMinutes(1)) ->withHeader('kid', $this->keyId) ->relatedTo('gustavo@example.com') - ->getToken(Sha256::create(), $this->privateKey) + ->getToken($ecdsa, $this->privateKey) ->toString(); $result = $authenticator->authenticate( @@ -371,13 +376,14 @@ public function testAuthenticateExpiredToken(): void ['region' => 'eu-south-1', 'guzzleClient' => ['handler' => $this->handler]] ); + $ecdsa = new Sha256(); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()->subDays(1)) ->canOnlyBeUsedAfter(DateTime::now()->subDays(1)) ->expiresAt(DateTime::now()->subDays(1)->addMinutes(1)) ->withHeader('kid', $this->keyId) ->relatedTo('gustavo@example.com') - ->getToken(Sha256::create(), $this->privateKey) + ->getToken($ecdsa, $this->privateKey) ->toString(); $result = $authenticator->authenticate( @@ -417,13 +423,14 @@ public function testAuthenticateInvalidSignature(): void assert($privateKey !== ''); $privateKey = InMemory::plainText($privateKey); + $ecdsa = new Sha256(); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()) ->canOnlyBeUsedAfter(DateTime::now()) ->expiresAt(DateTime::now()->addMinutes(1)) ->withHeader('kid', $this->keyId) ->relatedTo('gustavo@example.com') - ->getToken(Sha256::create(), $privateKey) + ->getToken($ecdsa, $privateKey) ->toString(); $result = $authenticator->authenticate( @@ -458,13 +465,30 @@ public function testAuthenticateInvalidAlgorithm(): void ['region' => 'eu-south-1', 'guzzleClient' => ['handler' => $this->handler]] ); + $none = new class () implements Signer + { + public function algorithmId(): string + { + return 'none'; + } + + public function sign(string $payload, Key $key): string + { + return ''; + } + + public function verify(string $expected, string $payload, Key $key): bool + { + return $expected === ''; + } + }; $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()) ->canOnlyBeUsedAfter(DateTime::now()) ->expiresAt(DateTime::now()->addMinutes(1)) ->withHeader('kid', $this->keyId) ->relatedTo('gustavo@example.com') - ->getToken(new None(), InMemory::plainText('key')) + ->getToken($none, InMemory::plainText('key')) ->toString(); $result = $authenticator->authenticate( @@ -499,12 +523,13 @@ public function testAuthenticateMissingSub(): void ['region' => 'eu-south-1', 'guzzleClient' => ['handler' => $this->handler]] ); + $ecdsa = new Sha256(); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()) ->canOnlyBeUsedAfter(DateTime::now()) ->expiresAt(DateTime::now()->addMinutes(1)) ->withHeader('kid', $this->keyId) - ->getToken(Sha256::create(), $this->privateKey) + ->getToken($ecdsa, $this->privateKey) ->toString(); $result = $authenticator->authenticate( @@ -547,13 +572,14 @@ public function testAuthenticateIdentify(): void ] ); + $ecdsa = new Sha256(); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()) ->canOnlyBeUsedAfter(DateTime::now()) ->expiresAt(DateTime::now()->addMinutes(1)) ->withHeader('kid', $this->keyId) ->relatedTo('gustavo@example.com') - ->getToken(Sha256::create(), $this->privateKey) + ->getToken($ecdsa, $this->privateKey) ->toString(); $result = $authenticator->authenticate( @@ -603,13 +629,14 @@ public function testAuthenticateIdentifyFailure(): void ] ); + $ecdsa = new Sha256(); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()) ->canOnlyBeUsedAfter(DateTime::now()) ->expiresAt(DateTime::now()->addMinutes(1)) ->withHeader('kid', $this->keyId) ->relatedTo('gustavo@example.com') - ->getToken(Sha256::create(), $this->privateKey) + ->getToken($ecdsa, $this->privateKey) ->toString(); $result = $authenticator->authenticate( From f6c831f2885018a376e0f6d5291ac19da6aa699c Mon Sep 17 00:00:00 2001 From: dante di domenico Date: Wed, 22 Oct 2025 14:31:59 +0200 Subject: [PATCH 06/14] chore fix: phpcs --- tests/TestCase/Authenticator/AlbAuthenticatorTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/TestCase/Authenticator/AlbAuthenticatorTest.php b/tests/TestCase/Authenticator/AlbAuthenticatorTest.php index 7f1907f..c9ddf42 100644 --- a/tests/TestCase/Authenticator/AlbAuthenticatorTest.php +++ b/tests/TestCase/Authenticator/AlbAuthenticatorTest.php @@ -36,7 +36,6 @@ use Lcobucci\JWT\Signer\Ecdsa\Sha256; use Lcobucci\JWT\Signer\Key; use Lcobucci\JWT\Signer\Key\InMemory; -use Lcobucci\JWT\Signer\None; use Lcobucci\JWT\Token\Builder; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; From 063fd8d9211889071f56d74b98284efb36893204 Mon Sep 17 00:00:00 2001 From: dante di domenico Date: Wed, 22 Oct 2025 14:41:17 +0200 Subject: [PATCH 07/14] fix: sha256 param --- src/Authenticator/AlbAuthenticator.php | 5 +++-- .../Authenticator/AlbAuthenticatorTest.php | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Authenticator/AlbAuthenticator.php b/src/Authenticator/AlbAuthenticator.php index 22a71df..e6c5451 100644 --- a/src/Authenticator/AlbAuthenticator.php +++ b/src/Authenticator/AlbAuthenticator.php @@ -29,6 +29,7 @@ use Lcobucci\Clock\FrozenClock; use Lcobucci\JWT\Decoder; use Lcobucci\JWT\Encoding\CannotDecodeContent; +use Lcobucci\JWT\Signer\Ecdsa\MultibyteStringConverter; use Lcobucci\JWT\Signer\Ecdsa\Sha256; use Lcobucci\JWT\Signer\Key; use Lcobucci\JWT\SodiumBase64Polyfill; @@ -212,10 +213,10 @@ public function base64UrlDecode(string $data): string if (empty($kid) || !is_string($kid) || !$jwt instanceof UnencryptedToken) { return null; } - + $ecdsa = new Sha256(new MultibyteStringConverter()); (new Validator())->assert( $jwt, - new SignedWith(new Sha256(), $this->getKey($kid)), + new SignedWith($ecdsa, $this->getKey($kid)), new LooseValidAt(new FrozenClock(DateTime::now())), ); diff --git a/tests/TestCase/Authenticator/AlbAuthenticatorTest.php b/tests/TestCase/Authenticator/AlbAuthenticatorTest.php index c9ddf42..2eb22c1 100644 --- a/tests/TestCase/Authenticator/AlbAuthenticatorTest.php +++ b/tests/TestCase/Authenticator/AlbAuthenticatorTest.php @@ -33,6 +33,7 @@ use Lcobucci\JWT\Encoding\ChainedFormatter; use Lcobucci\JWT\Encoding\JoseEncoder; use Lcobucci\JWT\Signer; +use Lcobucci\JWT\Signer\Ecdsa\MultibyteStringConverter; use Lcobucci\JWT\Signer\Ecdsa\Sha256; use Lcobucci\JWT\Signer\Key; use Lcobucci\JWT\Signer\Key\InMemory; @@ -147,7 +148,7 @@ public function testAuthenticate(): void ['region' => 'eu-south-1', 'guzzleClient' => ['handler' => $this->handler]] ); - $ecdsa = new Sha256(); + $ecdsa = new Sha256(new MultibyteStringConverter()); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()) ->canOnlyBeUsedAfter(DateTime::now()) @@ -257,7 +258,7 @@ public function testAuthenticateMalformedJsonInToken(): void $encoder->base64UrlEncode($encoder->jsonEncode(['typ' => 'JWT', 'alg' => 'ES256', 'kid' => $this->keyId])), $encoder->base64UrlEncode('NOT A JSON'), ); - $ecdsa = new Sha256(); + $ecdsa = new Sha256(new MultibyteStringConverter()); $token .= sprintf('.%s', $encoder->base64UrlEncode($ecdsa->sign($token, $this->privateKey))); $result = $authenticator->authenticate( @@ -289,7 +290,7 @@ public function testAuthenticateMissingKid(): void ['region' => 'eu-south-1', 'guzzleClient' => ['handler' => $this->handler]] ); - $ecdsa = new Sha256(); + $ecdsa = new Sha256(new MultibyteStringConverter()); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()->subDays(1)) ->canOnlyBeUsedAfter(DateTime::now()->subDays(1)) @@ -331,7 +332,7 @@ public function testAuthenticateKeyNotFound(): void ['region' => 'eu-south-1', 'guzzleClient' => ['handler' => $handler]] ); - $ecdsa = new Sha256(); + $ecdsa = new Sha256(new MultibyteStringConverter()); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()->subDays(1)) ->canOnlyBeUsedAfter(DateTime::now()->subDays(1)) @@ -375,7 +376,7 @@ public function testAuthenticateExpiredToken(): void ['region' => 'eu-south-1', 'guzzleClient' => ['handler' => $this->handler]] ); - $ecdsa = new Sha256(); + $ecdsa = new Sha256(new MultibyteStringConverter()); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()->subDays(1)) ->canOnlyBeUsedAfter(DateTime::now()->subDays(1)) @@ -422,7 +423,7 @@ public function testAuthenticateInvalidSignature(): void assert($privateKey !== ''); $privateKey = InMemory::plainText($privateKey); - $ecdsa = new Sha256(); + $ecdsa = new Sha256(new MultibyteStringConverter()); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()) ->canOnlyBeUsedAfter(DateTime::now()) @@ -522,7 +523,7 @@ public function testAuthenticateMissingSub(): void ['region' => 'eu-south-1', 'guzzleClient' => ['handler' => $this->handler]] ); - $ecdsa = new Sha256(); + $ecdsa = new Sha256(new MultibyteStringConverter()); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()) ->canOnlyBeUsedAfter(DateTime::now()) @@ -571,7 +572,7 @@ public function testAuthenticateIdentify(): void ] ); - $ecdsa = new Sha256(); + $ecdsa = new Sha256(new MultibyteStringConverter()); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()) ->canOnlyBeUsedAfter(DateTime::now()) @@ -628,7 +629,7 @@ public function testAuthenticateIdentifyFailure(): void ] ); - $ecdsa = new Sha256(); + $ecdsa = new Sha256(new MultibyteStringConverter()); $token = (new Builder(new JoseEncoder(), ChainedFormatter::default())) ->issuedAt(DateTime::now()) ->canOnlyBeUsedAfter(DateTime::now()) From 6dab08ed669a03746efbf3a6adc5af4e7fd58f4c Mon Sep 17 00:00:00 2001 From: dante di domenico Date: Wed, 22 Oct 2025 14:42:48 +0200 Subject: [PATCH 08/14] fix: remove .phpunit.cache folder --- .phpunit.cache/test-results | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .phpunit.cache/test-results diff --git a/.phpunit.cache/test-results b/.phpunit.cache/test-results deleted file mode 100644 index c7366e3..0000000 --- a/.phpunit.cache/test-results +++ /dev/null @@ -1 +0,0 @@ -{"version":2,"defects":[],"times":{"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticate":0.019,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateMissingCredentials":0.001,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateMalformedToken":0.002,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateMalformedJsonInToken":0.003,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateMissingKid":0.003,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateKeyNotFound":0.011,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateExpiredToken":0.012,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateInvalidSignature":0.025,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateInvalidAlgorithm":0.004,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateMissingSub":0.01,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateIdentify":0.01,"BEdita\\AWS\\Test\\TestCase\\Authenticator\\AlbAuthenticatorTest::testAuthenticateIdentifyFailure":0.011,"BEdita\\AWS\\Test\\TestCase\\AwsConfigTraitTest::testReformatCredentials with data set \"no username, no password\"":0.002,"BEdita\\AWS\\Test\\TestCase\\AwsConfigTraitTest::testReformatCredentials with data set \"username, no password\"":0.001,"BEdita\\AWS\\Test\\TestCase\\AwsConfigTraitTest::testReformatCredentials with data set \"username, password\"":0.002,"BEdita\\AWS\\Test\\TestCase\\AwsConfigTraitTest::testReformatCredentials with data set \"preserve existing values\"":0.001,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testConstruct":0.122,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testConstructMissingCloudFrontClient":0.007,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testConstructWithDistribution":0.016,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testCopy":0.024,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testCopyCloudFrontNotExistingObject":0.032,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testCopyCloudFrontExistingObject":0.024,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testDelete":0.011,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testDeleteCloudFrontNotExistingObject":0.016,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testDeleteCloudFrontExistingObject":0.027,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testDeleteDir":0.015,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testDeleteDirCloudFront":0.019,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testWrite":0.015,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testWriteCloudFrontNotExistingObject":0.028,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\AwsS3CloudFrontAdapterTest::testWriteCloudFrontExistingObject":0.019,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testInitialize with data set \"empty bucket\"":0.001,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testInitialize with data set \"bucket not a string\"":0.001,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testInitialize with data set \"host\"":0.001,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testInitialize with data set \"host, bucket preserved\"":0.001,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testInitialize with data set \"prefix not a string\"":0.001,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testInitialize with data set \"path\"":0.001,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testInitialize with data set \"path, prefix preserved\"":0.001,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testGetClient":0.011,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testBuildAdapter":0.009,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testBuildAdapterCloudFront":0.01,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testGetPublicUrl":0,"BEdita\\AWS\\Test\\TestCase\\Filesystem\\Adapter\\S3AdapterTest::testGetPublicUrlDefaultS3Url":0.025,"BEdita\\AWS\\Test\\TestCase\\Mailer\\Transport\\SesTransportTest::testConstruct":0.006,"BEdita\\AWS\\Test\\TestCase\\Mailer\\Transport\\SesTransportTest::testSend with data set \"simple\"":0.005,"BEdita\\AWS\\Test\\TestCase\\Mailer\\Transport\\SnsTransportTest::testConstruct":0.005,"BEdita\\AWS\\Test\\TestCase\\Mailer\\Transport\\SnsTransportTest::testSend with data set \"simple\"":0.004,"BEdita\\AWS\\Test\\TestCase\\Mailer\\Transport\\SnsTransportTest::testSend with data set \"with sender\"":0.005,"BEdita\\AWS\\Test\\TestCase\\Mailer\\Transport\\SnsTransportTest::testSend with data set \"with SMS type\"":0.005,"BEdita\\AWS\\Test\\TestCase\\Mailer\\Transport\\SnsTransportTest::testSend with data set \"with sender and SMS type\"":0.004,"BEdita\\AWS\\Test\\TestCase\\PluginTest::testBootstrap":0.004}} \ No newline at end of file From 6630ac475d6fd0831a2c0550cb422d90521a12eb Mon Sep 17 00:00:00 2001 From: dante di domenico Date: Wed, 22 Oct 2025 14:43:13 +0200 Subject: [PATCH 09/14] chore fix: add .phpunit.cache to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index bf832c4..574a1a5 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ /plugins /tmp .phpunit.result.cache +.phpunit.cache phinx.yml From b3d9a1f5e90effbd171f3167285b30960e2b825a Mon Sep 17 00:00:00 2001 From: dante di domenico Date: Fri, 24 Oct 2025 11:47:15 +0200 Subject: [PATCH 10/14] fix: update release and php workflows --- .github/workflows/php.yml | 6 ++++++ .github/workflows/release.yml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 73785e7..f32adf1 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -6,11 +6,17 @@ on: - '**/*.php' - '.github/workflows/php.yml' - 'composer.json' + - 'phpcs.xml.dist' + - 'phpstan.neon.dist' + - 'phpunit.xml.dist' push: paths: - '**/*.php' - '.github/workflows/php.yml' - 'composer.json' + - 'phpcs.xml.dist' + - 'phpstan.neon.dist' + - 'phpunit.xml.dist' jobs: cs: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7a95e94..70bb2ad 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,5 +17,5 @@ jobs: uses: bedita/github-workflows/.github/workflows/release.yml@v2 with: main_branch: 'master' - dist_branches: '["master"]' + dist_branches: '["4.x", "master"]' version_bump: ${{ inputs.releaseType }} From ae7847432165e85ceafb7a0bf6b2cedffb464fb1 Mon Sep 17 00:00:00 2001 From: dante di domenico Date: Thu, 13 Nov 2025 09:58:01 +0100 Subject: [PATCH 11/14] feat: upgrade lib deps --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 0cdf870..8bc6f65 100644 --- a/composer.json +++ b/composer.json @@ -9,8 +9,8 @@ "bedita/core": "6.x-dev", "cakephp/cakephp": "^5", "lcobucci/jwt": "^4.2.1", - "league/flysystem": "^2.4.3", - "league/flysystem-aws-s3-v3": "^2.4.3", + "league/flysystem": "^3.30.2", + "league/flysystem-aws-s3-v3": "^3.30.1", "guzzlehttp/guzzle": "^7.4" }, "require-dev": { From 1acd9e0f59670826b6a0bbcc20265d98bff3b7d6 Mon Sep 17 00:00:00 2001 From: dante di domenico Date: Thu, 13 Nov 2025 10:01:37 +0100 Subject: [PATCH 12/14] feat: php 8.4 in php github workflow --- .github/workflows/php.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index f32adf1..fd614b6 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -22,12 +22,12 @@ jobs: cs: uses: bedita/github-workflows/.github/workflows/php-cs.yml@v2 with: - php_versions: '["8.3"]' + php_versions: '["8.4"]' stan: uses: bedita/github-workflows/.github/workflows/php-stan.yml@v2 with: - php_versions: '["8.3"]' + php_versions: '["8.4"]' unit: name: 'Run unit tests' @@ -39,6 +39,7 @@ jobs: matrix: php: - '8.3' + - '8.4' env: PHP_VERSION: '${{ matrix.php }}' From f3ad4e4baec29052c6c08d6edf7fef3c66734a7e Mon Sep 17 00:00:00 2001 From: dante di domenico Date: Thu, 13 Nov 2025 10:19:40 +0100 Subject: [PATCH 13/14] fix: handle exception on fileExists --- .../Adapter/AwsS3CloudFrontAdapter.php | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Filesystem/Adapter/AwsS3CloudFrontAdapter.php b/src/Filesystem/Adapter/AwsS3CloudFrontAdapter.php index 6d7a5d6..2941f20 100644 --- a/src/Filesystem/Adapter/AwsS3CloudFrontAdapter.php +++ b/src/Filesystem/Adapter/AwsS3CloudFrontAdapter.php @@ -19,6 +19,7 @@ use Aws\CloudFront\Exception\CloudFrontException; use Aws\S3\S3ClientInterface; use DomainException; +use Exception; use League\Flysystem\AwsS3V3\AwsS3V3Adapter; use League\Flysystem\Config; @@ -106,12 +107,30 @@ public function hasCloudFrontConfig(): bool return $this->cloudfrontClient !== null; } + /** + * Check whether a file exists. + * + * @param string $path The path to check. + * @return bool + */ + protected function exists(string $path): bool + { + $result = false; + try { + $result = $this->hasCloudFrontConfig() && $this->fileExists($path); + } catch (Exception $e) { + // Ignore exceptions + } + + return $result; + } + /** * @inheritDoc */ public function copy(string $source, string $destination, Config $config): void { - $existed = $this->hasCloudFrontConfig() && $this->fileExists($destination); + $existed = $this->exists($destination); parent::copy($source, $destination, $config); if ($existed) { $this->createCloudFrontInvalidation($destination); @@ -123,7 +142,7 @@ public function copy(string $source, string $destination, Config $config): void */ public function delete(string $path): void { - $existed = $this->hasCloudFrontConfig() && $this->fileExists($path); + $existed = $this->exists($path); parent::delete($path); if ($existed) { $this->createCloudFrontInvalidation($path); @@ -144,7 +163,7 @@ public function deleteDirectory(string $path): void */ public function write(string $path, string $contents, Config $config): void { - $existed = $this->hasCloudFrontConfig() && $this->fileExists($path); + $existed = $this->exists($path); parent::write($path, $contents, $config); if ($existed) { $this->createCloudFrontInvalidation($path); From ee8cd5357d17f67f86980f4fcea56ed791d2cc1f Mon Sep 17 00:00:00 2001 From: dante di domenico Date: Mon, 24 Nov 2025 16:44:19 +0100 Subject: [PATCH 14/14] feat: bedita core 6.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8bc6f65..fc14e02 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "php": ">= 8.3", "ext-openssl": "*", "aws/aws-sdk-php": "^3.222", - "bedita/core": "6.x-dev", + "bedita/core": "^6.0", "cakephp/cakephp": "^5", "lcobucci/jwt": "^4.2.1", "league/flysystem": "^3.30.2",