From 40a0c8a2f8cead0fe02a60118800ccf26ec14bbd Mon Sep 17 00:00:00 2001 From: Sam Remis Date: Tue, 3 Aug 2021 17:08:45 -0400 Subject: [PATCH 01/17] Revert "IMDS dualstack support (#2277)" This reverts commit 754fd2d121a61e826122a7dea012e23955af7451. --- src/Credentials/CredentialProvider.php | 9 +- src/Credentials/EcsCredentialProvider.php | 14 +- src/Credentials/InstanceProfileProvider.php | 109 +------- src/functions.php | 54 ---- .../InstanceProfileProviderTest.php | 261 ------------------ tests/FunctionsTest.php | 55 ---- 6 files changed, 21 insertions(+), 481 deletions(-) diff --git a/src/Credentials/CredentialProvider.php b/src/Credentials/CredentialProvider.php index 5bf3d675e3..0a88eeab37 100644 --- a/src/Credentials/CredentialProvider.php +++ b/src/Credentials/CredentialProvider.php @@ -110,8 +110,13 @@ public static function defaultProvider(array $config = []) ); } - $shouldUseEcsCredentialsProvider = - \Aws\get_environment_variable(EcsCredentialProvider::ENV_URI); + $shouldUseEcsCredentialsProvider = getenv(EcsCredentialProvider::ENV_URI); + // getenv() is not thread safe - fall back to $_SERVER + if ($shouldUseEcsCredentialsProvider === false) { + $shouldUseEcsCredentialsProvider = isset($_SERVER[EcsCredentialProvider::ENV_URI]) + ? $_SERVER[EcsCredentialProvider::ENV_URI] + : false; + } if (!empty($shouldUseEcsCredentialsProvider)) { $defaultChain['ecs'] = self::ecsCredentials($config); diff --git a/src/Credentials/EcsCredentialProvider.php b/src/Credentials/EcsCredentialProvider.php index 5d4ee89f30..0f377f2d98 100644 --- a/src/Credentials/EcsCredentialProvider.php +++ b/src/Credentials/EcsCredentialProvider.php @@ -31,9 +31,12 @@ class EcsCredentialProvider */ public function __construct(array $config = []) { - $timeout = \Aws\get_environment_variable(self::ENV_TIMEOUT) ?: 1.0; + $timeout = getenv(self::ENV_TIMEOUT); + if (!$timeout) { - $timeout = isset($config['timeout']) ? $config['timeout'] : 1.0; + $timeout = isset($_SERVER[self::ENV_TIMEOUT]) + ? $_SERVER[self::ENV_TIMEOUT] + : (isset($config['timeout']) ? $config['timeout'] : 1.0); } $this->timeout = (float) $timeout; @@ -81,7 +84,12 @@ public function __invoke() */ private function getEcsUri() { - $credsUri = \Aws\get_environment_variable(self::ENV_URI) ?: ''; + $credsUri = getenv(self::ENV_URI); + + if ($credsUri === false) { + $credsUri = isset($_SERVER[self::ENV_URI]) ? $_SERVER[self::ENV_URI] : ''; + } + return self::SERVER_URI . $credsUri; } diff --git a/src/Credentials/InstanceProfileProvider.php b/src/Credentials/InstanceProfileProvider.php index f5f868051f..647d2f7a9e 100644 --- a/src/Credentials/InstanceProfileProvider.php +++ b/src/Credentials/InstanceProfileProvider.php @@ -6,32 +6,23 @@ use Aws\Sdk; use GuzzleHttp\Exception\TransferException; use GuzzleHttp\Promise; +use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Promise\PromiseInterface; use Psr\Http\Message\ResponseInterface; -use InvalidArgumentException as IAE; /** * Credential provider that provides credentials from the EC2 metadata service. */ class InstanceProfileProvider { - const V4_SERVER_URI = '169.254.169.254'; - const V6_SERVER_URI = '[fd00:ec2::254]'; + const SERVER_URI = 'http://169.254.169.254/latest/'; const CRED_PATH = 'meta-data/iam/security-credentials/'; const TOKEN_PATH = 'api/token'; const ENV_DISABLE = 'AWS_EC2_METADATA_DISABLED'; const ENV_TIMEOUT = 'AWS_METADATA_SERVICE_TIMEOUT'; const ENV_RETRIES = 'AWS_METADATA_SERVICE_NUM_ATTEMPTS'; - const ENV_ENDPOINT = 'AWS_EC2_METADATA_SERVICE_ENDPOINT'; - const ENV_CONFIG_FILE = 'AWS_CONFIG_FILE'; - const ENV_ENDPOINT_MODE = 'AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE'; - - const CONFIG_ENDPOINT_MODE = 'ec2_metadata_service_endpoint_mode'; - const CONFIG_ENDPOINT = 'ec2_metadata_service_endpoint'; - - public static $supporedIpVersions = ['IPv4', 'IPv6']; /** @var string */ private $profile; @@ -39,15 +30,6 @@ class InstanceProfileProvider /** @var callable */ private $client; - /** @var string */ - private $endpoint; - - /** @var string */ - private $endpoint_mode; - - /** @var string */ - private $configFile; - /** @var int */ private $retries; @@ -66,8 +48,6 @@ class InstanceProfileProvider * - timeout: Connection timeout, in seconds. * - profile: Optional EC2 profile name, if known. * - retries: Optional number of retries to be attempted. - * - endpoint: Optional endpoint to use for fetching metadata info - * - endpoint_mode: Optional specification to force IPv4 or IPv6; defaults to IPv4 * * @param array $config Configuration options. */ @@ -80,11 +60,6 @@ public function __construct(array $config = []) $this->client = isset($config['client']) ? $config['client'] // internal use only : \Aws\default_http_handler(); - $configFile = - \Aws\get_environment_variable(InstanceProfileProvider::ENV_CONFIG_FILE); - $this->configFile = $configFile !== false ? $configFile : null; - $this->applyEndpointMode($config); - $this->applyEndpoint($config); } /** @@ -223,12 +198,7 @@ private function request($url, $method = 'GET', $headers = []) } $fn = $this->client; - $request = new Request( - $method, - $this->endpoint . $url, - ['force_ip_resolve' => $this->endpoint_mode] - ); - + $request = new Request($method, self::SERVER_URI . $url); $userAgent = 'aws-sdk-php/' . Sdk::VERSION; if (defined('HHVM_VERSION')) { $userAgent .= ' HHVM/' . HHVM_VERSION; @@ -304,77 +274,4 @@ private function decodeResult($response) return $result; } - - /** - * @param $endpoint_mode - */ - private function applyEndpointMode($config) - { - $endpoint_mode = isset($config['endpoint_mode']) - ? $config['endpoint_mode'] - : false; - if ($endpoint_mode == false) { - $endpoint_mode = - \Aws\get_environment_variable(InstanceProfileProvider::ENV_ENDPOINT_MODE); - } - if ($endpoint_mode == false) { - $endpoint_mode = - \Aws\get_config_variable( - InstanceProfileProvider::CONFIG_ENDPOINT_MODE, - $this->configFile - ); - } - - if ($endpoint_mode !== false) { - if (!in_array($endpoint_mode, self::$supporedIpVersions)) { - throw new IAE( - "Invalid input for endpoint_mode provided. Valid inputs include: " - . implode(', ', self::$supporedIpVersions) - ); - } - $this->endpoint_mode = str_replace('IP', '', $endpoint_mode); - } else { - $this->endpoint_mode = 'v4'; - } - } - - /** - * @param array $config - * @param $configFile - * @return array - */ - private function getEndpointMode(array $config, $configFile) - { - - return $config; - } - - /** - * @param array $config - * @param $configFile - */ - private function applyEndpoint(array $config) - { - $endpoint = isset($config['endpoint']) ? $config['endpoint'] : false; - if ($endpoint === false) { - $endpoint = - \Aws\get_environment_variable(InstanceProfileProvider::ENV_ENDPOINT); - } - if ($endpoint === false) { - $endpoint = - \Aws\get_config_variable( - InstanceProfileProvider::CONFIG_ENDPOINT, - $this->configFile - ); - } - if ($endpoint !== false) { - $this->endpoint = 'http://' . $endpoint . '/latest/'; - } else { - $endpoint_uri = - $this->endpoint_mode == 'v4' - ? self::V4_SERVER_URI - : self::V6_SERVER_URI; - $this->endpoint = 'http://' . $endpoint_uri . '/latest/'; - } - } } diff --git a/src/functions.php b/src/functions.php index d497c0ada9..6d3907cd05 100644 --- a/src/functions.php +++ b/src/functions.php @@ -519,57 +519,3 @@ function is_valid_epoch($input) } return false; } - - -/** - * Gets an environment variable in a thread safe way - * - * @param $variableName - * @return string|bool - */ -function get_environment_variable($variableName) -{ - $environmentVariable = getenv($variableName); - // getenv() is not thread safe - fall back to $_SERVER - if ($environmentVariable === false) { - $environmentVariable = isset($_SERVER[$variableName]) - ? $_SERVER[$variableName] - : false; - } - return $environmentVariable; -} - -/** - * Gets a config variable - * - * @param $variableName - * @return string|bool - */ -function get_config_variable($variableName, $filePath = '/.aws/config', $profileName = null) -{ - - if (!isset($filePath)) { - if ($homeDir = getenv('HOME')) { - $filePath = $homeDir; - } else { - // Get the HOMEDRIVE and HOMEPATH values for Windows hosts - $homeDrive = getenv('HOMEDRIVE'); - $homePath = getenv('HOMEPATH'); - $filePath = ($homeDrive && $homePath) ? $homeDrive . $homePath : null; - } - $filePath .= '/.aws/config'; - } - - if (!file_exists($filePath)) { - return false; - } - - if ($profileName == null) { - $profileName = get_environment_variable('AWS_PROFILE') ?: 'default'; - } - - $profileData = \Aws\parse_ini_file($filePath, true, INI_SCANNER_RAW); - return isset($profileData[$profileName][$variableName]) - ? $profileData[$profileName][$variableName] - : false; -} diff --git a/tests/Credentials/InstanceProfileProviderTest.php b/tests/Credentials/InstanceProfileProviderTest.php index 351081fa23..0bde3ca3da 100644 --- a/tests/Credentials/InstanceProfileProviderTest.php +++ b/tests/Credentials/InstanceProfileProviderTest.php @@ -8,11 +8,9 @@ use Aws\Sdk; use GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Promise; -use GuzzleHttp\Promise\FulfilledPromise; use GuzzleHttp\Psr7; use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Psr7\Response; -use GuzzleHttp\Psr7\Uri; use PHPUnit\Framework\TestCase; use Psr\Http\Message\RequestInterface; @@ -966,263 +964,4 @@ public function testRetriesEnvVarIsUsed() $this->assertNull($c->getSecurityToken()); $this->assertSame($t, $c->getExpiration()); } - - public function testAppliesDefaultEndpoint(){ - $client = function (RequestInterface $req) { - $this->assertSame( - '169.254.169.254', - $req->getUri()->getHost() - ); - $expiry = time() + 1000; - $creds = ['foo_key', 'baz_secret', 'qux_token', "@{$expiry}"]; - return new FulfilledPromise(new Response( - 200, - [], - Psr7\Utils::streamFor( - json_encode(call_user_func_array([$this, 'getCredentialArray'], $creds)) - ) - )); - }; - $provider = new InstanceProfileProvider([ - 'client' => $client, - 'retries' => 5, - ]); - $provider()->wait(); - } - - public function testAppliesCustomEndpoint(){ - $client = function (RequestInterface $req) { - $this->assertSame( - '[fd00:ec2::254]', - $req->getUri()->getHost() - ); - $expiry = time() + 1000; - $creds = ['foo_key', 'baz_secret', 'qux_token', "@{$expiry}"]; - return new FulfilledPromise(new Response( - 200, - [], - Psr7\Utils::streamFor( - json_encode(call_user_func_array([$this, 'getCredentialArray'], $creds)) - ) - )); - }; - $provider = new InstanceProfileProvider([ - 'client' => $client, - 'endpoint' => '[fd00:ec2::254]', - 'retries' => 5, - ]); - $provider()->wait(); - } - - public function testAppliesDefaultIpVersion(){ - $client = function (RequestInterface $req) { - $this->assertNotEmpty( - $req->getHeader('force_ip_resolve') - ); - $this->assertSame( - '169.254.169.254', - $req->getUri()->getHost() - ); - $this->assertSame( - 'v4', - $req->getHeader('force_ip_resolve')[0] - ); - $expiry = time() + 1000; - $creds = ['foo_key', 'baz_secret', 'qux_token', "@{$expiry}"]; - return new FulfilledPromise(new Response( - 200, - [], - Psr7\Utils::streamFor( - json_encode(call_user_func_array([$this, 'getCredentialArray'], $creds)) - ) - )); - }; - $provider = new InstanceProfileProvider([ - 'client' => $client, - 'retries' => 5, - ]); - $provider()->wait(); - } - - public function testAppliesCustomIpVersion(){ - $client = function (RequestInterface $req) { - $this->assertNotEmpty( - $req->getHeader('force_ip_resolve') - ); - $this->assertSame( - '[fd00:ec2::254]', - $req->getUri()->getHost() - ); - $this->assertSame( - 'v6', - $req->getHeader('force_ip_resolve')[0] - ); - $expiry = time() + 1000; - $creds = ['foo_key', 'baz_secret', 'qux_token', "@{$expiry}"]; - return new FulfilledPromise(new Response( - 200, - [], - Psr7\Utils::streamFor( - json_encode(call_user_func_array([$this, 'getCredentialArray'], $creds)) - ) - )); - }; - $provider = new InstanceProfileProvider([ - 'client' => $client, - 'endpoint_mode' => 'IPv6', - 'retries' => 5, - ]); - $provider()->wait(); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Invalid input for endpoint_mode provided. Valid inputs include: IPv4, IPv6 - */ - public function testFailsOnBadCustomIpVersion(){ - - $client = function (RequestInterface $req) { - $expiry = time() + 1000; - $creds = ['foo_key', 'baz_secret', 'qux_token', "@{$expiry}"]; - return new FulfilledPromise(new Response( - 200, - [], - Psr7\Utils::streamFor( - json_encode(call_user_func_array([$this, 'getCredentialArray'], $creds)) - ) - )); - }; - $provider = new InstanceProfileProvider([ - 'client' => $client, - 'endpoint_mode' => 'IPvInvalid', - 'retries' => 5, - ]); - $provider()->wait(); - } - - public function testPrioitizesClientConfigFirst(){ - $fileContents = <<assertSame( - 'endpoint.com', - $req->getUri()->getHost() - ); - $this->assertSame( - 'v6', - $req->getHeader('force_ip_resolve')[0] - ); - $expiry = time() + 1000; - $creds = ['foo_key', 'baz_secret', 'qux_token', "@{$expiry}"]; - return new FulfilledPromise(new Response( - 200, - [], - Psr7\Utils::streamFor( - json_encode(call_user_func_array([$this, 'getCredentialArray'], $creds)) - ) - )); - }; - $provider = new InstanceProfileProvider([ - 'client' => $client, - 'endpoint_mode' => 'IPv6', - 'endpoint' => 'endpoint.com', - 'retries' => 5, - ]); - $provider()->wait(); - - putenv("AWS_CONFIG_FILE"); - putenv("AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE"); - putenv("AWS_EC2_METADATA_SERVICE_ENDPOINT"); - unlink($tmpFile); - } - - public function testPrioitizesEnvironmentSecond(){ - $fileContents = <<assertSame( - 'endpoint-env.com', - $req->getUri()->getHost() - ); - $this->assertSame( - 'v6', - $req->getHeader('force_ip_resolve')[0] - ); - $expiry = time() + 1000; - $creds = ['foo_key', 'baz_secret', 'qux_token', "@{$expiry}"]; - return new FulfilledPromise(new Response( - 200, - [], - Psr7\Utils::streamFor( - json_encode(call_user_func_array([$this, 'getCredentialArray'], $creds)) - ) - )); - }; - $provider = new InstanceProfileProvider([ - 'client' => $client, - 'retries' => 5, - ]); - $provider()->wait(); - - putenv("AWS_CONFIG_FILE"); - putenv("AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE"); - putenv("AWS_EC2_METADATA_SERVICE_ENDPOINT"); - unlink($tmpFile); - } - - public function testPrioitizesConfigFileThird(){ - $fileContents = <<assertSame( - 'config-file-endpoint.com', - $req->getUri()->getHost() - ); - $this->assertSame( - 'v6', - $req->getHeader('force_ip_resolve')[0] - ); - $expiry = time() + 1000; - $creds = ['foo_key', 'baz_secret', 'qux_token', "@{$expiry}"]; - return new FulfilledPromise(new Response( - 200, - [], - Psr7\Utils::streamFor( - json_encode(call_user_func_array([$this, 'getCredentialArray'], $creds)) - ) - )); - }; - $provider = new InstanceProfileProvider([ - 'client' => $client, - 'retries' => 5, - ]); - $provider()->wait(); - - putenv("AWS_CONFIG_FILE"); - unlink($tmpFile); - } } diff --git a/tests/FunctionsTest.php b/tests/FunctionsTest.php index 01af946b40..7d4c5d8ba7 100644 --- a/tests/FunctionsTest.php +++ b/tests/FunctionsTest.php @@ -470,59 +470,4 @@ public function getIniFileTestCases() ], ]; } - - public function testGetEnvironmentVariableFromEnv() - { - putenv("FOO=BAR"); - $this->assertEquals( - "BAR", - Aws\get_environment_variable("FOO") - ); - putenv("FOO"); - } - - public function testGetEnvironmentVariableFromServer() - { - $_SERVER['FOO'] = 'BAR'; - $this->assertEquals( - "BAR", - Aws\get_environment_variable("FOO") - ); - unset($_SERVER['FOO']); - } - - public function testGetConfigVariableWithDefaultProfie() { - $fileContents = <<assertEquals( - "bar", - Aws\get_config_variable('foo_key', $tmpFile) - ); - unlink($tmpFile); - } - public function testGetConfigVariableWithCustomProfie() { - $fileContents = <<assertEquals( - "bar-custom", - Aws\get_config_variable('foo_key', $tmpFile, 'custom') - ); - unlink($tmpFile); - } } From 08dfebf6bbe53fbd90a8f1ac90b20aefafe206d4 Mon Sep 17 00:00:00 2001 From: Sam Remis Date: Fri, 4 Feb 2022 13:42:56 -0500 Subject: [PATCH 02/17] Revert "Enhancement: S3 StreamWrapper error handling (#2372)" This reverts commit fbb71b729b177a65473f9d2c240ccdbe9ecc4fdf. --- .../enhancement-streamwrapper.json | 7 ---- src/S3/StreamWrapper.php | 13 +------- tests/S3/StreamWrapperTest.php | 33 ------------------- 3 files changed, 1 insertion(+), 52 deletions(-) delete mode 100644 .changes/nextrelease/enhancement-streamwrapper.json diff --git a/.changes/nextrelease/enhancement-streamwrapper.json b/.changes/nextrelease/enhancement-streamwrapper.json deleted file mode 100644 index 1b55bd3ffd..0000000000 --- a/.changes/nextrelease/enhancement-streamwrapper.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - { - "type": "enhancement", - "category": "S3", - "description": "Improved error handling for failed writes and appends on unclosed streams." - } -] diff --git a/src/S3/StreamWrapper.php b/src/S3/StreamWrapper.php index 6f7ff7f212..731b2cf7e7 100644 --- a/src/S3/StreamWrapper.php +++ b/src/S3/StreamWrapper.php @@ -130,10 +130,7 @@ public static function register( public function stream_close() { - if (!$this->isFlushed - && empty($this->body->getSize()) - && $this->mode !== 'r' - ) { + if ($this->body->getSize() === 0 && !($this->isFlushed)) { $this->stream_flush(); } $this->body = $this->cache = null; @@ -166,14 +163,6 @@ public function stream_eof() public function stream_flush() { - // Check if stream body size has been - // calculated via a flush or close - if($this->body->getSize() === null && $this->mode !== 'r') { - return $this->triggerError( - "Unable to determine stream size. Did you forget to close or flush the stream?" - ); - } - $this->isFlushed = true; if ($this->mode == 'r') { return false; diff --git a/tests/S3/StreamWrapperTest.php b/tests/S3/StreamWrapperTest.php index 94a10fd54d..e7c8adba83 100644 --- a/tests/S3/StreamWrapperTest.php +++ b/tests/S3/StreamWrapperTest.php @@ -1026,37 +1026,4 @@ public function testStatDataIsClearedOnWriteUsingCustomProtocol() stream_wrapper_unregister('foo'); } - - public function contentProvider() - { - return [ - ['foo'], - [''] - ]; - } - - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage Unable to determine stream size. Did you forget to close or flush the stream? - * @dataProvider contentProvider - */ - public function testTriggersErrorOnNoFlushOrClose($content) - { - $stream = $this->getMockBuilder(Psr7\Stream::class) - ->disableOriginalConstructor() - ->getMock(); - $stream->expects($this->any()) - ->method('getSize') - ->willReturn(null); - - $this->addMockResults( - $this->client, - [ - new Result(['Body' => $stream]), - ] - ); - - $stream = fopen('s3://bucket/key', 'a'); - fwrite($stream, $content); - } } From de1fb7214e3f5ee239496a34a4d7fb05c0892015 Mon Sep 17 00:00:00 2001 From: SamRemis Date: Tue, 27 Sep 2022 13:25:24 -0400 Subject: [PATCH 03/17] Update StreamWrapperTest.php --- tests/S3/StreamWrapperTest.php | 168 +++++++++++++++++---------------- 1 file changed, 87 insertions(+), 81 deletions(-) diff --git a/tests/S3/StreamWrapperTest.php b/tests/S3/StreamWrapperTest.php index 2ccf8651de..ae858a46bf 100644 --- a/tests/S3/StreamWrapperTest.php +++ b/tests/S3/StreamWrapperTest.php @@ -11,7 +11,7 @@ use Aws\S3\StreamWrapper; use Aws\Test\UsesServiceTrait; use GuzzleHttp\Psr7; -use PHPUnit\Framework\TestCase; +use Yoast\PHPUnitPolyfills\TestCases\TestCase; use Psr\Http\Message\RequestInterface; /** @@ -27,7 +27,7 @@ class StreamWrapperTest extends TestCase /** @var LruArrayCache */ private $cache; - public function setUp() + public function set_up() { // use a fresh LRU cache for each test. $this->cache = new LruArrayCache(); @@ -36,7 +36,7 @@ public function setUp() $this->client->registerStreamWrapper(); } - public function tearDown() + public function tear_down() { stream_wrapper_unregister('s3'); $this->client = null; @@ -49,43 +49,36 @@ public function testRegistersStreamWrapperOnlyOnce() StreamWrapper::register($this->client); } - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage Cannot open a bucket - */ public function testCannotOpenBuckets() { + $this->expectExceptionMessage("Cannot open a bucket"); + $this->expectWarning(); fopen('s3://bucket', 'r'); } - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage Mode not supported - */ public function testSupportsOnlyReadWriteXA() { + $this->expectExceptionMessage("Mode not supported"); + $this->expectWarning(); fopen('s3://bucket/key', 'c'); } - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage s3://bucket/key already exists on Amazon S3 - */ public function testValidatesXMode() { + $this->expectExceptionMessage("s3://bucket/key already exists on Amazon S3"); + $this->expectWarning(); $this->addMockResults($this->client, [new Result()]); fopen('s3://bucket/key', 'x'); } - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage Bucket parameter parsed as ARN and failed with: Provided ARN was not a valid S3 access point ARN - */ public function testValidatesArn() { + $this->expectExceptionMessage("Bucket parameter parsed as ARN and failed with: Provided ARN was not a valid S3 access point ARN"); + $this->expectWarning(); fopen('s3://arn:aws:s3:us-east-1:123456789012:foo:myaccess/test_key', 'r'); } + /** @doesNotPerformAssertions */ public function testSuccessfulXMode() { $this->addMockResults( @@ -285,12 +278,10 @@ public function testCanWriteEmptyFileToStream() $this->assertSame('', (string) $cmd['Body']); } - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage 403 Forbidden - */ public function testTriggersErrorInsteadOfExceptionWhenWriteFlushFails() { + $this->expectExceptionMessage("403 Forbidden"); + $this->expectWarning(); $this->addMockResults($this->client, [ function ($cmd, $req) { return new S3Exception('403 Forbidden', $cmd); } ]); @@ -356,12 +347,10 @@ public function testCanUnlinkFiles() $this->assertSame('bucket.s3.amazonaws.com', $entries[0]['request']->getUri()->getHost()); } - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage 403 Forbidden - */ public function testThrowsErrorsWhenUnlinkFails() { + $this->expectExceptionMessage("403 Forbidden"); + $this->expectWarning(); $this->addMockResults($this->client, [ function ($cmd, $r) { return new S3Exception('403 Forbidden', $cmd); }, ]); @@ -373,22 +362,18 @@ public function testCreatingBucketWithNoBucketReturnsFalse() $this->assertFalse(mkdir('s3://')); } - /** - * @expectedExceptionMessage Bucket already exists: s3://already-existing-bucket - * @expectedException \PHPUnit\Framework\Error\Warning - */ public function testCreatingAlreadyExistingBucketRaisesError() { + $this->expectWarning(); + $this->expectExceptionMessage("Bucket already exists: s3://already-existing-bucket"); $this->addMockResults($this->client, [new Result()]); mkdir('s3://already-existing-bucket'); } - /** - * @expectedExceptionMessage Subfolder already exists: s3://already-existing-bucket/key - * @expectedException \PHPUnit\Framework\Error\Warning - */ public function testCreatingAlreadyExistingBucketForKeyRaisesError() { + $this->expectWarning(); + $this->expectExceptionMessage("Subfolder already exists: s3://already-existing-bucket/key"); $this->addMockResults($this->client, [new Result()]); mkdir('s3://already-existing-bucket/key'); } @@ -441,24 +426,20 @@ function ($cmd, $r) { return new S3Exception('404', $cmd); }, $entries = $history->toArray(); $this->assertSame('HEAD', $entries[0]['request']->getMethod()); $this->assertSame('PUT', $entries[1]['request']->getMethod()); - $this->assertContains('public-read', $entries[1]['request']->getHeaderLine('x-amz-acl')); + $this->assertStringContainsString('public-read', $entries[1]['request']->getHeaderLine('x-amz-acl')); } - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage specify a bucket - */ public function testCannotDeleteS3() { + $this->expectExceptionMessage("specify a bucket"); + $this->expectWarning(); rmdir('s3://'); } - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage 403 Forbidden - */ public function testRmDirWithExceptionTriggersError() { + $this->expectExceptionMessage("403 Forbidden"); + $this->expectWarning(); $this->addMockResults($this->client, [ function ($cmd, $r) { return new S3Exception('403 Forbidden', $cmd); }, ]); @@ -498,7 +479,7 @@ public function testCanDeleteObjectWithRmDir($path) $this->assertCount(1, $history); $entries = $history->toArray(); $this->assertSame('GET', $entries[0]['request']->getMethod()); - $this->assertContains('prefix=object%2F', $entries[0]['request']->getUri()->getQuery()); + $this->assertStringContainsString('prefix=object%2F', $entries[0]['request']->getUri()->getQuery()); } public function testCanDeleteNestedFolderWithRmDir() @@ -518,37 +499,31 @@ public function testCanDeleteNestedFolderWithRmDir() $this->assertCount(2, $history); $entries = $history->toArray(); $this->assertSame('GET', $entries[0]['request']->getMethod()); - $this->assertContains('prefix=bar%2F', $entries[0]['request']->getUri()->getQuery()); + $this->assertStringContainsString('prefix=bar%2F', $entries[0]['request']->getUri()->getQuery()); $this->assertSame('DELETE', $entries[1]['request']->getMethod()); $this->assertSame('/bar/', $entries[1]['request']->getUri()->getPath()); - $this->assertContains('foo', $entries[1]['request']->getUri()->getHost()); + $this->assertStringContainsString('foo', $entries[1]['request']->getUri()->getHost()); } - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage rename(): Cannot rename a file across wrapper types - */ public function testRenameEnsuresProtocolsMatch() { + $this->expectExceptionMessage("rename(): Cannot rename a file across wrapper types"); + $this->expectWarning(); StreamWrapper::register($this->client, 'baz'); rename('s3://foo/bar', 'baz://qux/quux'); } - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage The Amazon S3 stream wrapper only supports copying objects - */ public function testRenameEnsuresKeyIsSet() { + $this->expectExceptionMessage("The Amazon S3 stream wrapper only supports copying objects"); + $this->expectWarning(); rename('s3://foo/bar', 's3://baz'); } - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage Forbidden - */ public function testRenameWithExceptionThrowsError() { + $this->expectExceptionMessage("Forbidden"); + $this->expectWarning(); $this->addMockResults($this->client, [ function ($cmd, $r) { return new S3Exception('403 Forbidden', $cmd); }, ]); @@ -646,12 +621,10 @@ public function testCanPullStatDataFromCache() $this->assertSame(123, filesize('s3://foo/bar')); } - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage Forbidden - */ public function testFailingStatTriggersError() { + $this->expectExceptionMessage("Forbidden"); + $this->expectWarning(); // Sends one request for HeadObject, then another for ListObjects $this->addMockResults($this->client, [ function ($cmd, $r) { return new S3Exception('403 Forbidden', $cmd); }, @@ -661,12 +634,10 @@ function ($cmd, $r) { return new S3Exception('403 Forbidden', $cmd); } stat('s3://bucket/key'); } - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage File or directory not found: s3://bucket - */ public function testBucketNotFoundTriggersError() { + $this->expectExceptionMessage("File or directory not found: s3://bucket"); + $this->expectWarning(); $this->addMockResults($this->client, [ function ($cmd, $r) { return new S3Exception('404', $cmd); }, ]); @@ -708,12 +679,10 @@ function ($cmd, $r) { return new S3Exception('404', $cmd); }, $this->assertSame(0040777, $stat['mode']); } - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage File or directory not found: s3://bucket/prefix - */ public function testCannotStatPrefixWithNoResults() { + $this->expectExceptionMessage("File or directory not found: s3://bucket/prefix"); + $this->expectWarning(); $this->addMockResults($this->client, [ function ($cmd, $r) { return new S3Exception('404', $cmd); }, new Result() @@ -783,12 +752,16 @@ public function testDeterminesIfFileOrDir($uri, $queue, $result) $this->assertCount(count($queue), $history); } - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage cannot represent a stream of type user-space - */ public function testStreamCastIsNotPossible() { + if (PHP_VERSION_ID < 80000) { + $this->expectExceptionMessage("cannot represent a stream of type user-space"); + $this->expectWarning(); + } else { + $this->expectExceptionMessage('No stream arrays were passed'); + $this->expectException(\ValueError::class); + } + $this->addMockResults($this->client, [ new Result(['Body' => Psr7\Utils::streamFor('')]) ]); @@ -798,12 +771,10 @@ public function testStreamCastIsNotPossible() stream_select($read, $write, $except, 0); } - /** - * @expectedException \PHPUnit\Framework\Error\Warning - * @expectedExceptionMessage No client in stream context - */ public function testEnsuresClientIsSet() { + $this->expectExceptionMessage("No client in stream context"); + $this->expectWarning(); fopen('s3://bucket/key', 'r', false, stream_context_create([ 's3' => ['client' => null] ])); @@ -822,7 +793,7 @@ public function testDoesNotErrorOnFileExists() $this->addMockResults($this->client, [ function ($cmd, $r) { return new S3Exception('404', $cmd); }, ]); - $this->assertFileNotExists('s3://bucket/key'); + $this->assertFileDoesNotExist('s3://bucket/key'); } public function testProvidesDirectoriesForS3() @@ -889,7 +860,7 @@ public function testProvidesDirectoriesForS3() $dir = 's3://bucket/key/'; $r = opendir($dir); - $this->assertInternalType('resource', $r); + $this->assertIsResource($r); $files = []; while (($file = readdir($r)) !== false) { @@ -907,6 +878,7 @@ public function testProvidesDirectoriesForS3() closedir($r); } + /** @doesNotPerformAssertions */ public function testCanSetDelimiterStreamContext() { $this->addMockResults($this->client, [ @@ -1026,4 +998,38 @@ public function testStatDataIsClearedOnWriteUsingCustomProtocol() stream_wrapper_unregister('foo'); } + + public function contentProvider() + { + return [ + ['foo'], + [''] + ]; + } + + /** + * @dataProvider contentProvider + */ + public function testTriggersErrorOnNoFlushOrClose($content) + { + $this->expectWarning(); + $this->expectWarning("Unable to determine stream size. Did you forget to close or flush the stream?"); + + $stream = $this->getMockBuilder(Psr7\Stream::class) + ->disableOriginalConstructor() + ->getMock(); + $stream->expects($this->any()) + ->method('getSize') + ->willReturn(null); + + $this->addMockResults( + $this->client, + [ + new Result(['Body' => $stream]), + ] + ); + + $stream = fopen('s3://bucket/key', 'a'); + fwrite($stream, $content); + } } From e0685c1f9d63ab89e45a73fbb6a2e1b55357c672 Mon Sep 17 00:00:00 2001 From: SamRemis Date: Thu, 22 Dec 2022 08:13:50 -0500 Subject: [PATCH 04/17] Revert "bugfix: add MD5 header for PutObject and UploadPart by default (#2603)" This reverts commit 2c33b5dd5d7ef68b07ef6dd5069fe15796ed6f95. --- src/S3/ApplyChecksumMiddleware.php | 17 +++++++++-------- tests/S3/ApplyChecksumMiddlewareTest.php | 17 ++--------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/src/S3/ApplyChecksumMiddleware.php b/src/S3/ApplyChecksumMiddleware.php index 023c37c6cf..9279d22811 100644 --- a/src/S3/ApplyChecksumMiddleware.php +++ b/src/S3/ApplyChecksumMiddleware.php @@ -17,8 +17,7 @@ class ApplyChecksumMiddleware { use CalculatesChecksumTrait; - - private static $sha256AndMD5 = [ + private static $sha256 = [ 'PutObject', 'UploadPart', ]; @@ -92,25 +91,27 @@ public function __invoke( if (!empty($checksumInfo)) { //if the checksum member is absent, check if it's required - $checksumRequired = isset($checksumInfo['requestChecksumRequired']) - ? $checksumInfo['requestChecksumRequired'] - : null; - if ((!empty($checksumRequired) || in_array($name, self::$sha256AndMD5)) - && !$request->hasHeader('Content-MD5')) { + $checksumRequired = isset($checksumInfo['requestChecksumRequired']) + ? $checksumInfo['requestChecksumRequired'] + : null; + if (!empty($checksumRequired) && !$request->hasHeader('Content-MD5')) { // Set the content MD5 header for operations that require it. $request = $request->withHeader( 'Content-MD5', base64_encode(Psr7\Utils::hash($body, 'md5', true)) ); + return $next($command, $request); } } - if (in_array($name, self::$sha256AndMD5) && $command['ContentSHA256']) { + + if (in_array($name, self::$sha256) && $command['ContentSHA256']) { // Set the content hash header if provided in the parameters. $request = $request->withHeader( 'X-Amz-Content-Sha256', $command['ContentSHA256'] ); } + return $next($command, $request); } } diff --git a/tests/S3/ApplyChecksumMiddlewareTest.php b/tests/S3/ApplyChecksumMiddlewareTest.php index 1938b9422a..c91a68ffee 100644 --- a/tests/S3/ApplyChecksumMiddlewareTest.php +++ b/tests/S3/ApplyChecksumMiddlewareTest.php @@ -20,7 +20,8 @@ class ApplyChecksumMiddlewareTest extends TestCase public function testAddsContentMd5AsAppropriate($operation, $args, $md5Added, $md5Value) { $s3 = $this->getTestClient( - 's3' + 's3', + ['api_provider' => ApiProvider::filesystem(__DIR__ . '/fixtures')] ); $this->addMockResults($s3, [[]]); $command = $s3->getCommand($operation, $args); @@ -66,20 +67,6 @@ public function getContentMd5UseCases() false, null, ], - // Test MD5 added for operations which conditionally require it - [ - 'PutObject', - ['Bucket' => 'foo', 'Key' => 'foo', 'Body' => 'test'], - true, - 'CY9rzUYh03PK3k6DJie09g==' - ], - // Test MD5 added for operations which conditionally require it - [ - 'UploadPart', - ['Bucket' => 'foo', 'Key' => 'foo', 'Body' => 'test', 'PartNumber' => 1, 'UploadId' => 1], - true, - 'CY9rzUYh03PK3k6DJie09g==' - ] ]; } From c68225861b05ff3d58dbf9478ca0d98b38920601 Mon Sep 17 00:00:00 2001 From: SamRemis Date: Wed, 29 Mar 2023 13:50:28 -0400 Subject: [PATCH 05/17] initial commit --- .changes/nextrelease/bugfix-sso-provider.json | 12 ++ src/Credentials/CredentialProvider.php | 201 ++++++++++-------- src/Token/SsoTokenProvider.php | 2 +- 3 files changed, 131 insertions(+), 84 deletions(-) create mode 100644 .changes/nextrelease/bugfix-sso-provider.json diff --git a/.changes/nextrelease/bugfix-sso-provider.json b/.changes/nextrelease/bugfix-sso-provider.json new file mode 100644 index 0000000000..52d9eb2eb5 --- /dev/null +++ b/.changes/nextrelease/bugfix-sso-provider.json @@ -0,0 +1,12 @@ +[ + { + "type": "bugfix", + "category": "Credentials", + "description": "Fixes a bug with the new SSO login flow where the wrong index was being looked at for the SSO client provider region" + }, + { + "type": "enhancement", + "category": "Token", + "description": "Removes an instance of the utf8_decode function that was deprecated as of 8.2" + } +] \ No newline at end of file diff --git a/src/Credentials/CredentialProvider.php b/src/Credentials/CredentialProvider.php index a00cd9660a..0744523bc5 100644 --- a/src/Credentials/CredentialProvider.php +++ b/src/Credentials/CredentialProvider.php @@ -343,84 +343,10 @@ public static function sso($ssoProfileName = 'default', } if (!empty($ssoProfile['sso_session'])) { - if (empty($config['ssoOidcClient'])) { - $sessionName = $ssoProfile['sso_session']; - if (empty($profiles['sso-session ' . $sessionName])) { - return self::reject( - "Could not find sso-session {$sessionName} in {$filename}" - ); - } - $ssoSession = $profiles['sso-session ' . $ssoProfile['sso_session']]; - $ssoOidcClient = new Aws\SSOOIDC\SSOOIDCClient([ - 'region' => $ssoSession['sso_region'], - 'version' => '2019-06-10', - 'credentials' => false - ]); - } else { - $ssoOidcClient = $config['ssoClient']; - } - - $tokenPromise = new Aws\Token\SsoTokenProvider( - $ssoProfileName, - $filename, - $ssoOidcClient - ); - $token = $tokenPromise()->wait(); - $ssoCredentials = CredentialProvider::getCredentialsFromSsoService( - $ssoProfile, - $token->getToken(), - $config - ); - $expiration = $ssoCredentials['expiration']; - + return CredentialProvider::getSsoCredentials($profiles, $ssoProfileName, $filename, $config); } else { - if (empty($ssoProfile['sso_start_url']) - || empty($ssoProfile['sso_region']) - || empty($ssoProfile['sso_account_id']) - || empty($ssoProfile['sso_role_name']) - ) { - return self::reject( - "Profile {$ssoProfileName} in {$filename} must contain the following keys: " - . "sso_start_url, sso_region, sso_account_id, and sso_role_name." - ); - } - $tokenLocation = self::getHomeDir() - . '/.aws/sso/cache/' - . sha1($ssoProfile['sso_start_url']) - . ".json"; - - if (!@is_readable($tokenLocation)) { - return self::reject("Unable to read token file at $tokenLocation"); - } - $tokenData = json_decode(file_get_contents($tokenLocation), true); - if (empty($tokenData['accessToken']) || empty($tokenData['expiresAt'])) { - return self::reject( - "Token file at {$tokenLocation} must contain an access token and an expiration" - ); - } - try { - $expiration = (new DateTimeResult($tokenData['expiresAt']))->getTimestamp(); - } catch (\Exception $e) { - return self::reject("Cached SSO credentials returned an invalid expiration"); - } - $now = time(); - if ($expiration < $now) { - return self::reject("Cached SSO credentials returned expired credentials"); - } - $ssoCredentials = CredentialProvider::getCredentialsFromSsoService( - $ssoProfile, - $tokenData['accessToken'], - $config - ); + return CredentialProvider::getSsoCredentialsLegacy($profiles, $ssoProfileName, $filename, $config); } - return Promise\Create::promiseFor( - new Credentials( - $ssoCredentials['accessKeyId'], - $ssoCredentials['secretAccessKey'], - $ssoCredentials['sessionToken'], - $expiration - ) - ); }; } @@ -607,8 +533,8 @@ public static function ini($profile = null, $filename = null, array $config = [] if (empty($data[$profile]['aws_session_token'])) { $data[$profile]['aws_session_token'] = isset($data[$profile]['aws_security_token']) - ? $data[$profile]['aws_security_token'] - : null; + ? $data[$profile]['aws_security_token'] + : null; } return Promise\Create::promiseFor( @@ -921,9 +847,9 @@ public static function shouldUseEcs() //Check for relative uri. if not, then full uri. //fall back to server for each as getenv is not thread-safe. return !empty(getenv(EcsCredentialProvider::ENV_URI)) - || !empty($_SERVER[EcsCredentialProvider::ENV_URI]) - || !empty(getenv(EcsCredentialProvider::ENV_FULL_URI)) - || !empty($_SERVER[EcsCredentialProvider::ENV_FULL_URI]); + || !empty($_SERVER[EcsCredentialProvider::ENV_URI]) + || !empty(getenv(EcsCredentialProvider::ENV_FULL_URI)) + || !empty($_SERVER[EcsCredentialProvider::ENV_FULL_URI]); } /** @@ -932,11 +858,11 @@ public static function shouldUseEcs() * @param array $config * @return array|null */ - private static function getCredentialsFromSsoService($ssoProfile, $accessToken, $config) + private static function getCredentialsFromSsoService($ssoProfile, $clientRegion, $accessToken, $config) { if (empty($config['ssoClient'])) { $ssoClient = new Aws\SSO\SSOClient([ - 'region' => $ssoProfile['sso_region'], + 'region' => $clientRegion, 'version' => '2019-06-10', 'credentials' => false ]); @@ -952,4 +878,113 @@ private static function getCredentialsFromSsoService($ssoProfile, $accessToken, $ssoCredentials = $ssoResponse['roleCredentials']; return $ssoCredentials; } + + /** + * @param $ssoProfile + * @param $profiles + * @param $filename + * @param $ssoProfileName + * @return Promise\FulfilledPromise|Promise\Promise|Promise\PromiseInterface|Promise\RejectedPromise + */ + private static function getSsoCredentials($profiles, $ssoProfileName, $filename, $config) + { + if (empty($config['ssoOidcClient'])) { + $ssoProfile = $profiles[$ssoProfileName]; + $sessionName = $ssoProfile['sso_session']; + if (empty($profiles['sso-session ' . $sessionName])) { + return self::reject( + "Could not find sso-session {$sessionName} in {$filename}" + ); + } + $ssoSession = $profiles['sso-session ' . $ssoProfile['sso_session']]; + $ssoOidcClient = new Aws\SSOOIDC\SSOOIDCClient([ + 'region' => $ssoSession['sso_region'], + 'version' => '2019-06-10', + 'credentials' => false + ]); + } else { + $ssoOidcClient = $config['ssoClient']; + } + + $tokenPromise = new Aws\Token\SsoTokenProvider( + $ssoProfileName, + $filename, + $ssoOidcClient + ); + $token = $tokenPromise()->wait(); + $ssoCredentials = CredentialProvider::getCredentialsFromSsoService( + $ssoProfile, + $ssoSession['sso_region'], + $token->getToken(), + $config + ); + $expiration = $ssoCredentials['expiration']; + return Promise\Create::promiseFor( + new Credentials( + $ssoCredentials['accessKeyId'], + $ssoCredentials['secretAccessKey'], + $ssoCredentials['sessionToken'], + $expiration + ) + ); + } + + /** + * @param $ssoProfile + * @param $ssoProfileName + * @param $filename + * @param $config + * @return Promise\FulfilledPromise|Promise\Promise|Promise\PromiseInterface|Promise\RejectedPromise + */ + private static function getSsoCredentialsLegacy($profiles, $ssoProfileName, $filename, $config) + { + $ssoProfile = $profiles['ssoProfileName']; + if (empty($ssoProfile['sso_start_url']) + || empty($ssoProfile['sso_region']) + || empty($ssoProfile['sso_account_id']) + || empty($ssoProfile['sso_role_name']) + ) { + return self::reject( + "Profile {$ssoProfileName} in {$filename} must contain the following keys: " + . "sso_start_url, sso_region, sso_account_id, and sso_role_name." + ); + } + $tokenLocation = self::getHomeDir() + . '/.aws/sso/cache/' + . sha1($ssoProfile['sso_start_url']) + . ".json"; + + if (!@is_readable($tokenLocation)) { + return self::reject("Unable to read token file at $tokenLocation"); + } + $tokenData = json_decode(file_get_contents($tokenLocation), true); + if (empty($tokenData['accessToken']) || empty($tokenData['expiresAt'])) { + return self::reject( + "Token file at {$tokenLocation} must contain an access token and an expiration" + ); + } + try { + $expiration = (new DateTimeResult($tokenData['expiresAt']))->getTimestamp(); + } catch (\Exception $e) { + return self::reject("Cached SSO credentials returned an invalid expiration"); + } + $now = time(); + if ($expiration < $now) { + return self::reject("Cached SSO credentials returned expired credentials"); + } + $ssoCredentials = CredentialProvider::getCredentialsFromSsoService( + $ssoProfile, + $ssoProfile['sso_region'], + $tokenData['accessToken'], + $config + ); + return Promise\Create::promiseFor( + new Credentials( + $ssoCredentials['accessKeyId'], + $ssoCredentials['secretAccessKey'], + $ssoCredentials['sessionToken'], + $expiration + ) + ); + } } diff --git a/src/Token/SsoTokenProvider.php b/src/Token/SsoTokenProvider.php index 99927daa85..ee37264833 100644 --- a/src/Token/SsoTokenProvider.php +++ b/src/Token/SsoTokenProvider.php @@ -158,7 +158,7 @@ public static function getTokenLocation($sso_session) { return self::getHomeDir() . '/.aws/sso/cache/' - . utf8_encode(sha1($sso_session)) + . mb_convert_encoding(sha1($sso_session), 'UTF-8') . ".json"; } From 00a62977677c1bbbc701cc204574b2d2b6fb3ce1 Mon Sep 17 00:00:00 2001 From: SamRemis Date: Wed, 29 Mar 2023 13:53:04 -0400 Subject: [PATCH 06/17] Revert "initial commit" This reverts commit c68225861b05ff3d58dbf9478ca0d98b38920601. --- .changes/nextrelease/bugfix-sso-provider.json | 12 -- src/Credentials/CredentialProvider.php | 201 ++++++++---------- src/Token/SsoTokenProvider.php | 2 +- 3 files changed, 84 insertions(+), 131 deletions(-) delete mode 100644 .changes/nextrelease/bugfix-sso-provider.json diff --git a/.changes/nextrelease/bugfix-sso-provider.json b/.changes/nextrelease/bugfix-sso-provider.json deleted file mode 100644 index 52d9eb2eb5..0000000000 --- a/.changes/nextrelease/bugfix-sso-provider.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - { - "type": "bugfix", - "category": "Credentials", - "description": "Fixes a bug with the new SSO login flow where the wrong index was being looked at for the SSO client provider region" - }, - { - "type": "enhancement", - "category": "Token", - "description": "Removes an instance of the utf8_decode function that was deprecated as of 8.2" - } -] \ No newline at end of file diff --git a/src/Credentials/CredentialProvider.php b/src/Credentials/CredentialProvider.php index 0744523bc5..a00cd9660a 100644 --- a/src/Credentials/CredentialProvider.php +++ b/src/Credentials/CredentialProvider.php @@ -343,10 +343,84 @@ public static function sso($ssoProfileName = 'default', } if (!empty($ssoProfile['sso_session'])) { - return CredentialProvider::getSsoCredentials($profiles, $ssoProfileName, $filename, $config); + if (empty($config['ssoOidcClient'])) { + $sessionName = $ssoProfile['sso_session']; + if (empty($profiles['sso-session ' . $sessionName])) { + return self::reject( + "Could not find sso-session {$sessionName} in {$filename}" + ); + } + $ssoSession = $profiles['sso-session ' . $ssoProfile['sso_session']]; + $ssoOidcClient = new Aws\SSOOIDC\SSOOIDCClient([ + 'region' => $ssoSession['sso_region'], + 'version' => '2019-06-10', + 'credentials' => false + ]); + } else { + $ssoOidcClient = $config['ssoClient']; + } + + $tokenPromise = new Aws\Token\SsoTokenProvider( + $ssoProfileName, + $filename, + $ssoOidcClient + ); + $token = $tokenPromise()->wait(); + $ssoCredentials = CredentialProvider::getCredentialsFromSsoService( + $ssoProfile, + $token->getToken(), + $config + ); + $expiration = $ssoCredentials['expiration']; + } else { - return CredentialProvider::getSsoCredentialsLegacy($profiles, $ssoProfileName, $filename, $config); + if (empty($ssoProfile['sso_start_url']) + || empty($ssoProfile['sso_region']) + || empty($ssoProfile['sso_account_id']) + || empty($ssoProfile['sso_role_name']) + ) { + return self::reject( + "Profile {$ssoProfileName} in {$filename} must contain the following keys: " + . "sso_start_url, sso_region, sso_account_id, and sso_role_name." + ); + } + $tokenLocation = self::getHomeDir() + . '/.aws/sso/cache/' + . sha1($ssoProfile['sso_start_url']) + . ".json"; + + if (!@is_readable($tokenLocation)) { + return self::reject("Unable to read token file at $tokenLocation"); + } + $tokenData = json_decode(file_get_contents($tokenLocation), true); + if (empty($tokenData['accessToken']) || empty($tokenData['expiresAt'])) { + return self::reject( + "Token file at {$tokenLocation} must contain an access token and an expiration" + ); + } + try { + $expiration = (new DateTimeResult($tokenData['expiresAt']))->getTimestamp(); + } catch (\Exception $e) { + return self::reject("Cached SSO credentials returned an invalid expiration"); + } + $now = time(); + if ($expiration < $now) { + return self::reject("Cached SSO credentials returned expired credentials"); + } + $ssoCredentials = CredentialProvider::getCredentialsFromSsoService( + $ssoProfile, + $tokenData['accessToken'], + $config + ); } + return Promise\Create::promiseFor( + new Credentials( + $ssoCredentials['accessKeyId'], + $ssoCredentials['secretAccessKey'], + $ssoCredentials['sessionToken'], + $expiration + ) + ); }; } @@ -533,8 +607,8 @@ public static function ini($profile = null, $filename = null, array $config = [] if (empty($data[$profile]['aws_session_token'])) { $data[$profile]['aws_session_token'] = isset($data[$profile]['aws_security_token']) - ? $data[$profile]['aws_security_token'] - : null; + ? $data[$profile]['aws_security_token'] + : null; } return Promise\Create::promiseFor( @@ -847,9 +921,9 @@ public static function shouldUseEcs() //Check for relative uri. if not, then full uri. //fall back to server for each as getenv is not thread-safe. return !empty(getenv(EcsCredentialProvider::ENV_URI)) - || !empty($_SERVER[EcsCredentialProvider::ENV_URI]) - || !empty(getenv(EcsCredentialProvider::ENV_FULL_URI)) - || !empty($_SERVER[EcsCredentialProvider::ENV_FULL_URI]); + || !empty($_SERVER[EcsCredentialProvider::ENV_URI]) + || !empty(getenv(EcsCredentialProvider::ENV_FULL_URI)) + || !empty($_SERVER[EcsCredentialProvider::ENV_FULL_URI]); } /** @@ -858,11 +932,11 @@ public static function shouldUseEcs() * @param array $config * @return array|null */ - private static function getCredentialsFromSsoService($ssoProfile, $clientRegion, $accessToken, $config) + private static function getCredentialsFromSsoService($ssoProfile, $accessToken, $config) { if (empty($config['ssoClient'])) { $ssoClient = new Aws\SSO\SSOClient([ - 'region' => $clientRegion, + 'region' => $ssoProfile['sso_region'], 'version' => '2019-06-10', 'credentials' => false ]); @@ -878,113 +952,4 @@ private static function getCredentialsFromSsoService($ssoProfile, $clientRegion, $ssoCredentials = $ssoResponse['roleCredentials']; return $ssoCredentials; } - - /** - * @param $ssoProfile - * @param $profiles - * @param $filename - * @param $ssoProfileName - * @return Promise\FulfilledPromise|Promise\Promise|Promise\PromiseInterface|Promise\RejectedPromise - */ - private static function getSsoCredentials($profiles, $ssoProfileName, $filename, $config) - { - if (empty($config['ssoOidcClient'])) { - $ssoProfile = $profiles[$ssoProfileName]; - $sessionName = $ssoProfile['sso_session']; - if (empty($profiles['sso-session ' . $sessionName])) { - return self::reject( - "Could not find sso-session {$sessionName} in {$filename}" - ); - } - $ssoSession = $profiles['sso-session ' . $ssoProfile['sso_session']]; - $ssoOidcClient = new Aws\SSOOIDC\SSOOIDCClient([ - 'region' => $ssoSession['sso_region'], - 'version' => '2019-06-10', - 'credentials' => false - ]); - } else { - $ssoOidcClient = $config['ssoClient']; - } - - $tokenPromise = new Aws\Token\SsoTokenProvider( - $ssoProfileName, - $filename, - $ssoOidcClient - ); - $token = $tokenPromise()->wait(); - $ssoCredentials = CredentialProvider::getCredentialsFromSsoService( - $ssoProfile, - $ssoSession['sso_region'], - $token->getToken(), - $config - ); - $expiration = $ssoCredentials['expiration']; - return Promise\Create::promiseFor( - new Credentials( - $ssoCredentials['accessKeyId'], - $ssoCredentials['secretAccessKey'], - $ssoCredentials['sessionToken'], - $expiration - ) - ); - } - - /** - * @param $ssoProfile - * @param $ssoProfileName - * @param $filename - * @param $config - * @return Promise\FulfilledPromise|Promise\Promise|Promise\PromiseInterface|Promise\RejectedPromise - */ - private static function getSsoCredentialsLegacy($profiles, $ssoProfileName, $filename, $config) - { - $ssoProfile = $profiles['ssoProfileName']; - if (empty($ssoProfile['sso_start_url']) - || empty($ssoProfile['sso_region']) - || empty($ssoProfile['sso_account_id']) - || empty($ssoProfile['sso_role_name']) - ) { - return self::reject( - "Profile {$ssoProfileName} in {$filename} must contain the following keys: " - . "sso_start_url, sso_region, sso_account_id, and sso_role_name." - ); - } - $tokenLocation = self::getHomeDir() - . '/.aws/sso/cache/' - . sha1($ssoProfile['sso_start_url']) - . ".json"; - - if (!@is_readable($tokenLocation)) { - return self::reject("Unable to read token file at $tokenLocation"); - } - $tokenData = json_decode(file_get_contents($tokenLocation), true); - if (empty($tokenData['accessToken']) || empty($tokenData['expiresAt'])) { - return self::reject( - "Token file at {$tokenLocation} must contain an access token and an expiration" - ); - } - try { - $expiration = (new DateTimeResult($tokenData['expiresAt']))->getTimestamp(); - } catch (\Exception $e) { - return self::reject("Cached SSO credentials returned an invalid expiration"); - } - $now = time(); - if ($expiration < $now) { - return self::reject("Cached SSO credentials returned expired credentials"); - } - $ssoCredentials = CredentialProvider::getCredentialsFromSsoService( - $ssoProfile, - $ssoProfile['sso_region'], - $tokenData['accessToken'], - $config - ); - return Promise\Create::promiseFor( - new Credentials( - $ssoCredentials['accessKeyId'], - $ssoCredentials['secretAccessKey'], - $ssoCredentials['sessionToken'], - $expiration - ) - ); - } } diff --git a/src/Token/SsoTokenProvider.php b/src/Token/SsoTokenProvider.php index ee37264833..99927daa85 100644 --- a/src/Token/SsoTokenProvider.php +++ b/src/Token/SsoTokenProvider.php @@ -158,7 +158,7 @@ public static function getTokenLocation($sso_session) { return self::getHomeDir() . '/.aws/sso/cache/' - . mb_convert_encoding(sha1($sso_session), 'UTF-8') + . utf8_encode(sha1($sso_session)) . ".json"; } From 1e5788ce7c570fc9a01b37e5bfd03644376ff60a Mon Sep 17 00:00:00 2001 From: SamRemis Date: Wed, 29 Mar 2023 13:53:28 -0400 Subject: [PATCH 07/17] Revert "Revert "initial commit"" This reverts commit 00a62977677c1bbbc701cc204574b2d2b6fb3ce1. --- .changes/nextrelease/bugfix-sso-provider.json | 12 ++ src/Credentials/CredentialProvider.php | 201 ++++++++++-------- src/Token/SsoTokenProvider.php | 2 +- 3 files changed, 131 insertions(+), 84 deletions(-) create mode 100644 .changes/nextrelease/bugfix-sso-provider.json diff --git a/.changes/nextrelease/bugfix-sso-provider.json b/.changes/nextrelease/bugfix-sso-provider.json new file mode 100644 index 0000000000..52d9eb2eb5 --- /dev/null +++ b/.changes/nextrelease/bugfix-sso-provider.json @@ -0,0 +1,12 @@ +[ + { + "type": "bugfix", + "category": "Credentials", + "description": "Fixes a bug with the new SSO login flow where the wrong index was being looked at for the SSO client provider region" + }, + { + "type": "enhancement", + "category": "Token", + "description": "Removes an instance of the utf8_decode function that was deprecated as of 8.2" + } +] \ No newline at end of file diff --git a/src/Credentials/CredentialProvider.php b/src/Credentials/CredentialProvider.php index a00cd9660a..0744523bc5 100644 --- a/src/Credentials/CredentialProvider.php +++ b/src/Credentials/CredentialProvider.php @@ -343,84 +343,10 @@ public static function sso($ssoProfileName = 'default', } if (!empty($ssoProfile['sso_session'])) { - if (empty($config['ssoOidcClient'])) { - $sessionName = $ssoProfile['sso_session']; - if (empty($profiles['sso-session ' . $sessionName])) { - return self::reject( - "Could not find sso-session {$sessionName} in {$filename}" - ); - } - $ssoSession = $profiles['sso-session ' . $ssoProfile['sso_session']]; - $ssoOidcClient = new Aws\SSOOIDC\SSOOIDCClient([ - 'region' => $ssoSession['sso_region'], - 'version' => '2019-06-10', - 'credentials' => false - ]); - } else { - $ssoOidcClient = $config['ssoClient']; - } - - $tokenPromise = new Aws\Token\SsoTokenProvider( - $ssoProfileName, - $filename, - $ssoOidcClient - ); - $token = $tokenPromise()->wait(); - $ssoCredentials = CredentialProvider::getCredentialsFromSsoService( - $ssoProfile, - $token->getToken(), - $config - ); - $expiration = $ssoCredentials['expiration']; - + return CredentialProvider::getSsoCredentials($profiles, $ssoProfileName, $filename, $config); } else { - if (empty($ssoProfile['sso_start_url']) - || empty($ssoProfile['sso_region']) - || empty($ssoProfile['sso_account_id']) - || empty($ssoProfile['sso_role_name']) - ) { - return self::reject( - "Profile {$ssoProfileName} in {$filename} must contain the following keys: " - . "sso_start_url, sso_region, sso_account_id, and sso_role_name." - ); - } - $tokenLocation = self::getHomeDir() - . '/.aws/sso/cache/' - . sha1($ssoProfile['sso_start_url']) - . ".json"; - - if (!@is_readable($tokenLocation)) { - return self::reject("Unable to read token file at $tokenLocation"); - } - $tokenData = json_decode(file_get_contents($tokenLocation), true); - if (empty($tokenData['accessToken']) || empty($tokenData['expiresAt'])) { - return self::reject( - "Token file at {$tokenLocation} must contain an access token and an expiration" - ); - } - try { - $expiration = (new DateTimeResult($tokenData['expiresAt']))->getTimestamp(); - } catch (\Exception $e) { - return self::reject("Cached SSO credentials returned an invalid expiration"); - } - $now = time(); - if ($expiration < $now) { - return self::reject("Cached SSO credentials returned expired credentials"); - } - $ssoCredentials = CredentialProvider::getCredentialsFromSsoService( - $ssoProfile, - $tokenData['accessToken'], - $config - ); + return CredentialProvider::getSsoCredentialsLegacy($profiles, $ssoProfileName, $filename, $config); } - return Promise\Create::promiseFor( - new Credentials( - $ssoCredentials['accessKeyId'], - $ssoCredentials['secretAccessKey'], - $ssoCredentials['sessionToken'], - $expiration - ) - ); }; } @@ -607,8 +533,8 @@ public static function ini($profile = null, $filename = null, array $config = [] if (empty($data[$profile]['aws_session_token'])) { $data[$profile]['aws_session_token'] = isset($data[$profile]['aws_security_token']) - ? $data[$profile]['aws_security_token'] - : null; + ? $data[$profile]['aws_security_token'] + : null; } return Promise\Create::promiseFor( @@ -921,9 +847,9 @@ public static function shouldUseEcs() //Check for relative uri. if not, then full uri. //fall back to server for each as getenv is not thread-safe. return !empty(getenv(EcsCredentialProvider::ENV_URI)) - || !empty($_SERVER[EcsCredentialProvider::ENV_URI]) - || !empty(getenv(EcsCredentialProvider::ENV_FULL_URI)) - || !empty($_SERVER[EcsCredentialProvider::ENV_FULL_URI]); + || !empty($_SERVER[EcsCredentialProvider::ENV_URI]) + || !empty(getenv(EcsCredentialProvider::ENV_FULL_URI)) + || !empty($_SERVER[EcsCredentialProvider::ENV_FULL_URI]); } /** @@ -932,11 +858,11 @@ public static function shouldUseEcs() * @param array $config * @return array|null */ - private static function getCredentialsFromSsoService($ssoProfile, $accessToken, $config) + private static function getCredentialsFromSsoService($ssoProfile, $clientRegion, $accessToken, $config) { if (empty($config['ssoClient'])) { $ssoClient = new Aws\SSO\SSOClient([ - 'region' => $ssoProfile['sso_region'], + 'region' => $clientRegion, 'version' => '2019-06-10', 'credentials' => false ]); @@ -952,4 +878,113 @@ private static function getCredentialsFromSsoService($ssoProfile, $accessToken, $ssoCredentials = $ssoResponse['roleCredentials']; return $ssoCredentials; } + + /** + * @param $ssoProfile + * @param $profiles + * @param $filename + * @param $ssoProfileName + * @return Promise\FulfilledPromise|Promise\Promise|Promise\PromiseInterface|Promise\RejectedPromise + */ + private static function getSsoCredentials($profiles, $ssoProfileName, $filename, $config) + { + if (empty($config['ssoOidcClient'])) { + $ssoProfile = $profiles[$ssoProfileName]; + $sessionName = $ssoProfile['sso_session']; + if (empty($profiles['sso-session ' . $sessionName])) { + return self::reject( + "Could not find sso-session {$sessionName} in {$filename}" + ); + } + $ssoSession = $profiles['sso-session ' . $ssoProfile['sso_session']]; + $ssoOidcClient = new Aws\SSOOIDC\SSOOIDCClient([ + 'region' => $ssoSession['sso_region'], + 'version' => '2019-06-10', + 'credentials' => false + ]); + } else { + $ssoOidcClient = $config['ssoClient']; + } + + $tokenPromise = new Aws\Token\SsoTokenProvider( + $ssoProfileName, + $filename, + $ssoOidcClient + ); + $token = $tokenPromise()->wait(); + $ssoCredentials = CredentialProvider::getCredentialsFromSsoService( + $ssoProfile, + $ssoSession['sso_region'], + $token->getToken(), + $config + ); + $expiration = $ssoCredentials['expiration']; + return Promise\Create::promiseFor( + new Credentials( + $ssoCredentials['accessKeyId'], + $ssoCredentials['secretAccessKey'], + $ssoCredentials['sessionToken'], + $expiration + ) + ); + } + + /** + * @param $ssoProfile + * @param $ssoProfileName + * @param $filename + * @param $config + * @return Promise\FulfilledPromise|Promise\Promise|Promise\PromiseInterface|Promise\RejectedPromise + */ + private static function getSsoCredentialsLegacy($profiles, $ssoProfileName, $filename, $config) + { + $ssoProfile = $profiles['ssoProfileName']; + if (empty($ssoProfile['sso_start_url']) + || empty($ssoProfile['sso_region']) + || empty($ssoProfile['sso_account_id']) + || empty($ssoProfile['sso_role_name']) + ) { + return self::reject( + "Profile {$ssoProfileName} in {$filename} must contain the following keys: " + . "sso_start_url, sso_region, sso_account_id, and sso_role_name." + ); + } + $tokenLocation = self::getHomeDir() + . '/.aws/sso/cache/' + . sha1($ssoProfile['sso_start_url']) + . ".json"; + + if (!@is_readable($tokenLocation)) { + return self::reject("Unable to read token file at $tokenLocation"); + } + $tokenData = json_decode(file_get_contents($tokenLocation), true); + if (empty($tokenData['accessToken']) || empty($tokenData['expiresAt'])) { + return self::reject( + "Token file at {$tokenLocation} must contain an access token and an expiration" + ); + } + try { + $expiration = (new DateTimeResult($tokenData['expiresAt']))->getTimestamp(); + } catch (\Exception $e) { + return self::reject("Cached SSO credentials returned an invalid expiration"); + } + $now = time(); + if ($expiration < $now) { + return self::reject("Cached SSO credentials returned expired credentials"); + } + $ssoCredentials = CredentialProvider::getCredentialsFromSsoService( + $ssoProfile, + $ssoProfile['sso_region'], + $tokenData['accessToken'], + $config + ); + return Promise\Create::promiseFor( + new Credentials( + $ssoCredentials['accessKeyId'], + $ssoCredentials['secretAccessKey'], + $ssoCredentials['sessionToken'], + $expiration + ) + ); + } } diff --git a/src/Token/SsoTokenProvider.php b/src/Token/SsoTokenProvider.php index 99927daa85..ee37264833 100644 --- a/src/Token/SsoTokenProvider.php +++ b/src/Token/SsoTokenProvider.php @@ -158,7 +158,7 @@ public static function getTokenLocation($sso_session) { return self::getHomeDir() . '/.aws/sso/cache/' - . utf8_encode(sha1($sso_session)) + . mb_convert_encoding(sha1($sso_session), 'UTF-8') . ".json"; } From eb5c2852af415b50f8ac3bae7eac369788d5ed9e Mon Sep 17 00:00:00 2001 From: SamRemis Date: Thu, 20 Apr 2023 13:48:33 -0400 Subject: [PATCH 08/17] Create dependency-review.yml --- .github/workflows/dependency-review.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/dependency-review.yml diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000000..6f567a0ccc --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,16 @@ +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@v3 + - name: Dependency Review + uses: actions/dependency-review-action@v3 + with: + fail-on-severity: low From 139d2759991723fa5be63aab1c47ebedc9f7b8e0 Mon Sep 17 00:00:00 2001 From: SamRemis Date: Thu, 20 Apr 2023 13:52:09 -0400 Subject: [PATCH 09/17] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 0fbde38895..bd63b90770 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "nette/neon": "^2.3", "andrewsville/php-token-reflection": "^1.4", "psr/cache": "^1.0", - "psr/http-message": "^1.0", + "psr/http-message": "<1.0", "psr/simple-cache": "^1.0", "paragonie/random_compat": ">= 2", "sebastian/comparator": "^1.2.3 || ^4.0", From 07154a9880117e781f07d28b56702d1640a13b45 Mon Sep 17 00:00:00 2001 From: SamRemis Date: Thu, 20 Apr 2023 13:58:38 -0400 Subject: [PATCH 10/17] Update dependency-review.yml --- .github/workflows/dependency-review.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 6f567a0ccc..eda22c6c19 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -1,5 +1,9 @@ -name: 'Dependency Review' -on: [pull_request] +name: Dependency Review +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] permissions: contents: read From 21465437db0b4fd64f2ba80e19e9a8cfd2113d45 Mon Sep 17 00:00:00 2001 From: SamRemis Date: Thu, 20 Apr 2023 14:00:33 -0400 Subject: [PATCH 11/17] Update dependency-review.yml --- .github/workflows/dependency-review.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index eda22c6c19..8138d8466e 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -12,9 +12,9 @@ jobs: dependency-review: runs-on: ubuntu-latest steps: - - name: 'Checkout Repository' + - name: Checkout Repository uses: actions/checkout@v3 - name: Dependency Review uses: actions/dependency-review-action@v3 with: - fail-on-severity: low + fail-on-severity: low From 5c13c090957d43f5bb945e7c69753ba3a41ca327 Mon Sep 17 00:00:00 2001 From: SamRemis Date: Thu, 20 Apr 2023 14:05:58 -0400 Subject: [PATCH 12/17] Update dependency-review.yml --- .github/workflows/dependency-review.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 8138d8466e..dd20ed820a 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -10,11 +10,11 @@ permissions: jobs: dependency-review: - runs-on: ubuntu-latest - steps: - - name: Checkout Repository - uses: actions/checkout@v3 - - name: Dependency Review - uses: actions/dependency-review-action@v3 - with: - fail-on-severity: low + runs-on: ubuntu-20.04 + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + - name: Dependency Review + uses: actions/dependency-review-action@v3 + with: + fail-on-severity: low From 11205e12d088be9aaeb2de1caed6b9b38c88318e Mon Sep 17 00:00:00 2001 From: SamRemis Date: Thu, 20 Apr 2023 14:10:30 -0400 Subject: [PATCH 13/17] Update dependency-review.yml --- .github/workflows/dependency-review.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index dd20ed820a..fbf2b3631b 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -10,11 +10,11 @@ permissions: jobs: dependency-review: - runs-on: ubuntu-20.04 - steps: - - name: Checkout Repository - uses: actions/checkout@v3 - - name: Dependency Review - uses: actions/dependency-review-action@v3 - with: - fail-on-severity: low + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + - name: Dependency Review + uses: actions/dependency-review-action@v3 + with: + fail-on-severity: low From c4b93d5499161aac33a42e498b9a18611cd04bda Mon Sep 17 00:00:00 2001 From: SamRemis Date: Thu, 20 Apr 2023 14:14:58 -0400 Subject: [PATCH 14/17] Update dependency-review.yml --- .github/workflows/dependency-review.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index fbf2b3631b..d675f76a86 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -10,11 +10,11 @@ permissions: jobs: dependency-review: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - - name: Checkout Repository - uses: actions/checkout@v3 - - name: Dependency Review - uses: actions/dependency-review-action@v3 - with: + - name: Checkout Repository + uses: actions/checkout@v3 + - name: Dependency Review + uses: actions/dependency-review-action@v3 + with: fail-on-severity: low From 71fd0e132c2eaf1cfeaf28c87f1e76d642f15d1c Mon Sep 17 00:00:00 2001 From: SamRemis Date: Thu, 20 Apr 2023 14:16:58 -0400 Subject: [PATCH 15/17] Update dependency-review.yml --- .github/workflows/dependency-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index d675f76a86..de68edbfca 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -17,4 +17,4 @@ jobs: - name: Dependency Review uses: actions/dependency-review-action@v3 with: - fail-on-severity: low + fail-on-severity: low From 6b772dc91e0ed36ef27ccc2e97cc59edf78614f8 Mon Sep 17 00:00:00 2001 From: SamRemis Date: Thu, 20 Apr 2023 14:20:03 -0400 Subject: [PATCH 16/17] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index bd63b90770..b0b3268463 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ }, "require": { "php": ">=5.5", - "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", + "guzzlehttp/guzzle": "<6.3 || <7.1", "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", "guzzlehttp/promises": "^1.4.0", "mtdowling/jmespath.php": "^2.6", From c089c9eddcd0030f8df6d1f9af5c9fe47c954f01 Mon Sep 17 00:00:00 2001 From: SamRemis Date: Thu, 20 Apr 2023 16:13:49 -0400 Subject: [PATCH 17/17] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b0b3268463..38c37253e0 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "nette/neon": "^2.3", "andrewsville/php-token-reflection": "^1.4", "psr/cache": "^1.0", - "psr/http-message": "<1.0", + "psr/http-message": "<1.1", "psr/simple-cache": "^1.0", "paragonie/random_compat": ">= 2", "sebastian/comparator": "^1.2.3 || ^4.0",