← All changes
|
includes/sdk/s3/Aws/Credentials/CredentialProvider.php
+34
-14
1.2.13
→
1.4.2
View file →
| @@ -52,8 +52,11 @@ | ||
| 52 | 52 | const ENV_ACCOUNT_ID = 'AWS_ACCOUNT_ID'; |
| 53 | 53 | const ENV_SESSION = 'AWS_SESSION_TOKEN'; |
| 54 | 54 | const ENV_TOKEN_FILE = 'AWS_WEB_IDENTITY_TOKEN_FILE'; |
| 55 | 55 | const ENV_SHARED_CREDENTIALS_FILE = 'AWS_SHARED_CREDENTIALS_FILE'; |
| 56 | + public const ENV_REGION = 'AWS_REGION'; | |
| 57 | + public const FALLBACK_REGION = 'us-east-1'; | |
| 58 | + public const REFRESH_WINDOW = 60; | |
| 56 | 59 | /** |
| 57 | 60 | * Create a default credential provider that |
| 58 | 61 | * first checks for environment variables, |
| 59 | 62 | * then checks for assumed role via web identity, |
| @@ -81,9 +84,9 @@ | ||
| 81 | 84 | $defaultChain = ['env' => self::env(), 'web_identity' => self::assumeRoleWithWebIdentityCredentialProvider($config)]; |
| 82 | 85 | if (!isset($config['use_aws_shared_config_files']) || $config['use_aws_shared_config_files'] !== \false) { |
| 83 | 86 | $defaultChain['sso'] = self::sso($profileName, self::getHomeDir() . '/.aws/config', $config); |
| 84 | 87 | $defaultChain['process_credentials'] = self::process(); |
| 85 | - $defaultChain['ini'] = self::ini(); | |
| 88 | + $defaultChain['ini'] = self::ini(null, null, $config); | |
| 86 | 89 | $defaultChain['process_config'] = self::process('profile ' . $profileName, self::getHomeDir() . '/.aws/config'); |
| 87 | 90 | $defaultChain['ini_config'] = self::ini('profile ' . $profileName, self::getHomeDir() . '/.aws/config'); |
| 88 | 91 | } |
| 89 | 92 | if (self::shouldUseEcs()) { |
| @@ -172,10 +175,12 @@ | ||
| 172 | 175 | if (!$creds->getExpiration()) { |
| 173 | 176 | $isConstant = \true; |
| 174 | 177 | return $creds; |
| 175 | 178 | } |
| 176 | - // Refresh expired credentials. | |
| 177 | - if (!$creds->isExpired()) { | |
| 179 | + // Check if credentials are expired or will expire in 1 minute | |
| 180 | + $needsRefresh = $creds->getExpiration() - \time() <= self::REFRESH_WINDOW; | |
| 181 | + // Refresh if expired or expiring soon | |
| 182 | + if (!$needsRefresh && !$creds->isExpired()) { | |
| 178 | 183 | return $creds; |
| 179 | 184 | } |
| 180 | 185 | // Refresh the result and forward the promise. |
| 181 | 186 | return $result = $provider($creds); |
| @@ -225,9 +230,9 @@ | ||
| 225 | 230 | $secret = \getenv(self::ENV_SECRET); |
| 226 | 231 | $accountId = \getenv(self::ENV_ACCOUNT_ID) ?: null; |
| 227 | 232 | $token = \getenv(self::ENV_SESSION) ?: null; |
| 228 | 233 | if ($key && $secret) { |
| 229 | - return Promise\Create::promiseFor(new Credentials($key, $secret, $token, null, $accountId)); | |
| 234 | + return Promise\Create::promiseFor(new Credentials($key, $secret, $token, null, $accountId, CredentialSources::ENVIRONMENT)); | |
| 230 | 235 | } |
| 231 | 236 | return self::reject('Could not find environment variable ' . 'credentials in ' . self::ENV_KEY . '/' . self::ENV_SECRET); |
| 232 | 237 | }; |
| 233 | 238 | } |
| @@ -314,9 +319,9 @@ | ||
| 314 | 319 | $stsClient = isset($config['stsClient']) ? $config['stsClient'] : null; |
| 315 | 320 | $region = isset($config['region']) ? $config['region'] : null; |
| 316 | 321 | if ($tokenFromEnv && $arnFromEnv) { |
| 317 | 322 | $sessionName = \getenv(self::ENV_ROLE_SESSION_NAME) ? \getenv(self::ENV_ROLE_SESSION_NAME) : null; |
| 318 | - $provider = new AssumeRoleWithWebIdentityCredentialProvider(['RoleArn' => $arnFromEnv, 'WebIdentityTokenFile' => $tokenFromEnv, 'SessionName' => $sessionName, 'client' => $stsClient, 'region' => $region]); | |
| 323 | + $provider = new AssumeRoleWithWebIdentityCredentialProvider(['RoleArn' => $arnFromEnv, 'WebIdentityTokenFile' => $tokenFromEnv, 'SessionName' => $sessionName, 'client' => $stsClient, 'region' => $region, 'source' => CredentialSources::ENVIRONMENT_STS_WEB_ID_TOKEN]); | |
| 319 | 324 | return $provider(); |
| 320 | 325 | } |
| 321 | 326 | $profileName = \getenv(self::ENV_PROFILE) ?: 'default'; |
| 322 | 327 | if (isset($config['filename'])) { |
| @@ -330,9 +335,9 @@ | ||
| 330 | 335 | $region = $profile['region']; |
| 331 | 336 | } |
| 332 | 337 | if (isset($profile['web_identity_token_file']) && isset($profile['role_arn'])) { |
| 333 | 338 | $sessionName = isset($profile['role_session_name']) ? $profile['role_session_name'] : null; |
| 334 | - $provider = new AssumeRoleWithWebIdentityCredentialProvider(['RoleArn' => $profile['role_arn'], 'WebIdentityTokenFile' => $profile['web_identity_token_file'], 'SessionName' => $sessionName, 'client' => $stsClient, 'region' => $region]); | |
| 339 | + $provider = new AssumeRoleWithWebIdentityCredentialProvider(['RoleArn' => $profile['role_arn'], 'WebIdentityTokenFile' => $profile['web_identity_token_file'], 'SessionName' => $sessionName, 'client' => $stsClient, 'region' => $region, 'source' => CredentialSources::PROFILE_STS_WEB_ID_TOKEN]); | |
| 335 | 340 | return $provider(); |
| 336 | 341 | } |
| 337 | 342 | } else { |
| 338 | 343 | return self::reject("Unknown profile: {$profileName}"); |
| @@ -399,9 +404,9 @@ | ||
| 399 | 404 | } |
| 400 | 405 | if (empty($data[$profile]['aws_session_token'])) { |
| 401 | 406 | $data[$profile]['aws_session_token'] = isset($data[$profile]['aws_security_token']) ? $data[$profile]['aws_security_token'] : null; |
| 402 | 407 | } |
| 403 | - return Promise\Create::promiseFor(new Credentials($data[$profile]['aws_access_key_id'], $data[$profile]['aws_secret_access_key'], $data[$profile]['aws_session_token'], null, !empty($data[$profile]['aws_account_id']) ? $data[$profile]['aws_account_id'] : null)); | |
| 408 | + return Promise\Create::promiseFor(new Credentials($data[$profile]['aws_access_key_id'], $data[$profile]['aws_secret_access_key'], $data[$profile]['aws_session_token'], null, $data[$profile]['aws_account_id'] ?? null, CredentialSources::PROFILE)); | |
| 404 | 409 | }; |
| 405 | 410 | } |
| 406 | 411 | /** |
| 407 | 412 | * Credentials provider that creates credentials using a process configured in |
| @@ -466,9 +471,9 @@ | ||
| 466 | 471 | $accountId = $processData['AccountId']; |
| 467 | 472 | } elseif (!empty($data[$profile]['aws_account_id'])) { |
| 468 | 473 | $accountId = $data[$profile]['aws_account_id']; |
| 469 | 474 | } |
| 470 | - return Promise\Create::promiseFor(new Credentials($processData['AccessKeyId'], $processData['SecretAccessKey'], $processData['SessionToken'], $expires, $accountId)); | |
| 475 | + return Promise\Create::promiseFor(new Credentials($processData['AccessKeyId'], $processData['SecretAccessKey'], $processData['SessionToken'], $expires, $accountId, CredentialSources::PROFILE_PROCESS)); | |
| 471 | 476 | }; |
| 472 | 477 | } |
| 473 | 478 | /** |
| 474 | 479 | * Assumes role for profile that includes role_arn |
| @@ -498,9 +503,8 @@ | ||
| 498 | 503 | return self::reject("A role_arn must be provided with credential_source in " . "file {$filename} under profile {$profileName} "); |
| 499 | 504 | } |
| 500 | 505 | } |
| 501 | 506 | if (empty($stsClient)) { |
| 502 | - $sourceRegion = isset($profiles[$sourceProfileName]['region']) ? $profiles[$sourceProfileName]['region'] : 'us-east-1'; | |
| 503 | 507 | $config['preferStaticCredentials'] = \true; |
| 504 | 508 | $sourceCredentials = null; |
| 505 | 509 | if (!empty($roleProfile['source_profile'])) { |
| 506 | 510 | $sourceCredentials = \call_user_func(CredentialProvider::ini($sourceProfileName, $filename, $config))->wait(); |
| @@ -506,12 +510,13 @@ | ||
| 506 | 510 | $sourceCredentials = \call_user_func(CredentialProvider::ini($sourceProfileName, $filename, $config))->wait(); |
| 507 | 511 | } else { |
| 508 | 512 | $sourceCredentials = self::getCredentialsFromSource($profileName, $filename); |
| 509 | 513 | } |
| 510 | - $stsClient = new StsClient(['credentials' => $sourceCredentials, 'region' => $sourceRegion, 'version' => '2011-06-15']); | |
| 514 | + $region = $profiles[$sourceProfileName]['region'] ?? $config['region'] ?? \getEnv(self::ENV_REGION) ?: null; | |
| 515 | + $stsClient = self::createDefaultStsClient($sourceCredentials, $region); | |
| 511 | 516 | } |
| 512 | 517 | $result = $stsClient->assumeRole(['RoleArn' => $roleArn, 'RoleSessionName' => $roleSessionName]); |
| 513 | - $credentials = $stsClient->createCredentials($result); | |
| 518 | + $credentials = $stsClient->createCredentials($result, CredentialSources::STS_ASSUME_ROLE); | |
| 514 | 519 | return Promise\Create::promiseFor($credentials); |
| 515 | 520 | } |
| 516 | 521 | /** |
| 517 | 522 | * Gets the environment's HOME directory if available. |
| @@ -646,10 +651,11 @@ | ||
| 646 | 651 | } |
| 647 | 652 | $tokenPromise = new Aws\Token\SsoTokenProvider($ssoProfileName, $filename, $ssoOidcClient); |
| 648 | 653 | $token = $tokenPromise()->wait(); |
| 649 | 654 | $ssoCredentials = CredentialProvider::getCredentialsFromSsoService($ssoProfile, $ssoSession['sso_region'], $token->getToken(), $config); |
| 650 | - $expiration = $ssoCredentials['expiration']; | |
| 651 | - return Promise\Create::promiseFor(new Credentials($ssoCredentials['accessKeyId'], $ssoCredentials['secretAccessKey'], $ssoCredentials['sessionToken'], $expiration, $ssoProfile['sso_account_id'])); | |
| 655 | + //Expiration value is returned in epoch milliseconds. Conversion to seconds | |
| 656 | + $expiration = \intdiv($ssoCredentials['expiration'], 1000); | |
| 657 | + return Promise\Create::promiseFor(new Credentials($ssoCredentials['accessKeyId'], $ssoCredentials['secretAccessKey'], $ssoCredentials['sessionToken'], $expiration, $ssoProfile['sso_account_id'], CredentialSources::PROFILE_SSO)); | |
| 652 | 658 | } |
| 653 | 659 | /** |
| 654 | 660 | * @param $profiles |
| 655 | 661 | * @param $ssoProfileName |
| @@ -680,9 +686,9 @@ | ||
| 680 | 686 | if ($expiration < $now) { |
| 681 | 687 | return self::reject("Cached SSO credentials returned expired credentials"); |
| 682 | 688 | } |
| 683 | 689 | $ssoCredentials = CredentialProvider::getCredentialsFromSsoService($ssoProfile, $ssoProfile['sso_region'], $tokenData['accessToken'], $config); |
| 684 | - return Promise\Create::promiseFor(new Credentials($ssoCredentials['accessKeyId'], $ssoCredentials['secretAccessKey'], $ssoCredentials['sessionToken'], $expiration, $ssoProfile['sso_account_id'])); | |
| 690 | + return Promise\Create::promiseFor(new Credentials($ssoCredentials['accessKeyId'], $ssoCredentials['secretAccessKey'], $ssoCredentials['sessionToken'], $expiration, $ssoProfile['sso_account_id'], CredentialSources::PROFILE_SSO_LEGACY)); | |
| 685 | 691 | } |
| 686 | 692 | /** |
| 687 | 693 | * @param array $ssoProfile |
| 688 | 694 | * @param string $clientRegion |
| @@ -699,6 +705,20 @@ | ||
| 699 | 705 | } |
| 700 | 706 | $ssoResponse = $ssoClient->getRoleCredentials(['accessToken' => $accessToken, 'accountId' => $ssoProfile['sso_account_id'], 'roleName' => $ssoProfile['sso_role_name']]); |
| 701 | 707 | $ssoCredentials = $ssoResponse['roleCredentials']; |
| 702 | 708 | return $ssoCredentials; |
| 709 | + } | |
| 710 | + /** | |
| 711 | + * @param CredentialsInterface $credentials | |
| 712 | + * @param string|null $region | |
| 713 | + * | |
| 714 | + * @return StsClient | |
| 715 | + */ | |
| 716 | + private static function createDefaultStsClient(CredentialsInterface|callable $credentials, ?string $region) : StsClient | |
| 717 | + { | |
| 718 | + if (empty($region)) { | |
| 719 | + $region = self::FALLBACK_REGION; | |
| 720 | + \trigger_error('NOTICE: STS client created without explicit `region` configuration.' . \PHP_EOL . "Defaulting to `{$region}`. This fallback behavior may be removed." . \PHP_EOL . 'To avoid potential disruptions, configure a `region` using one of the following methods:' . \PHP_EOL . '(1) Add `region` to your source profile in ~/.aws/credentials,' . \PHP_EOL . '(2) Pass `region` in the `$config` array when calling the provider,' . \PHP_EOL . '(3) Set the `AWS_REGION` environment variable.' . \PHP_EOL . 'See: https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_credentials_assume_role.html#assume-role-with-profile' . \PHP_EOL, \E_USER_NOTICE); | |
| 721 | + } | |
| 722 | + return new StsClient(['credentials' => $credentials, 'region' => $region]); | |
| 703 | 723 | } |
| 704 | 724 | } |