PluginProbe ʕ •ᴥ•ʔ
Transferito: WP Migration / trunk
Transferito: WP Migration vtrunk
trunk 11.4.0 12.0.0 13.1.0 14.0.0 14.0.11 14.0.7 14.1.0 14.1.1 14.1.2 14.1.3 14.1.4
transferito / vendor / aws / aws-sdk-php / src / Credentials / CredentialProvider.php
transferito / vendor / aws / aws-sdk-php / src / Credentials Last commit date
AssumeRoleCredentialProvider.php 11 months ago AssumeRoleWithWebIdentityCredentialProvider.php 11 months ago CredentialProvider.php 11 months ago Credentials.php 11 months ago CredentialsInterface.php 11 months ago EcsCredentialProvider.php 11 months ago InstanceProfileProvider.php 11 months ago
CredentialProvider.php
992 lines
1 <?php
2 namespace Aws\Credentials;
3
4 use Aws;
5 use Aws\Api\DateTimeResult;
6 use Aws\CacheInterface;
7 use Aws\Exception\CredentialsException;
8 use Aws\Sts\StsClient;
9 use GuzzleHttp\Promise;
10 /**
11 * Credential providers are functions that accept no arguments and return a
12 * promise that is fulfilled with an {@see \Aws\Credentials\CredentialsInterface}
13 * or rejected with an {@see \Aws\Exception\CredentialsException}.
14 *
15 * <code>
16 * use Aws\Credentials\CredentialProvider;
17 * $provider = CredentialProvider::defaultProvider();
18 * // Returns a CredentialsInterface or throws.
19 * $creds = $provider()->wait();
20 * </code>
21 *
22 * Credential providers can be composed to create credentials using conditional
23 * logic that can create different credentials in different environments. You
24 * can compose multiple providers into a single provider using
25 * {@see Aws\Credentials\CredentialProvider::chain}. This function accepts
26 * providers as variadic arguments and returns a new function that will invoke
27 * each provider until a successful set of credentials is returned.
28 *
29 * <code>
30 * // First try an INI file at this location.
31 * $a = CredentialProvider::ini(null, '/path/to/file.ini');
32 * // Then try an INI file at this location.
33 * $b = CredentialProvider::ini(null, '/path/to/other-file.ini');
34 * // Then try loading from environment variables.
35 * $c = CredentialProvider::env();
36 * // Combine the three providers together.
37 * $composed = CredentialProvider::chain($a, $b, $c);
38 * // Returns a promise that is fulfilled with credentials or throws.
39 * $promise = $composed();
40 * // Wait on the credentials to resolve.
41 * $creds = $promise->wait();
42 * </code>
43 */
44 class CredentialProvider
45 {
46 const ENV_ARN = 'AWS_ROLE_ARN';
47 const ENV_KEY = 'AWS_ACCESS_KEY_ID';
48 const ENV_PROFILE = 'AWS_PROFILE';
49 const ENV_ROLE_SESSION_NAME = 'AWS_ROLE_SESSION_NAME';
50 const ENV_SECRET = 'AWS_SECRET_ACCESS_KEY';
51 const ENV_SESSION = 'AWS_SESSION_TOKEN';
52 const ENV_TOKEN_FILE = 'AWS_WEB_IDENTITY_TOKEN_FILE';
53 const ENV_SHARED_CREDENTIALS_FILE = 'AWS_SHARED_CREDENTIALS_FILE';
54
55 /**
56 * Create a default credential provider that
57 * first checks for environment variables,
58 * then checks for assumed role via web identity,
59 * then checks for cached SSO credentials from the CLI,
60 * then check for credential_process in the "default" profile in ~/.aws/credentials,
61 * then checks for the "default" profile in ~/.aws/credentials,
62 * then for credential_process in the "default profile" profile in ~/.aws/config,
63 * then checks for "profile default" profile in ~/.aws/config (which is
64 * the default profile of AWS CLI),
65 * then tries to make a GET Request to fetch credentials if ECS environment variable is presented,
66 * finally checks for EC2 instance profile credentials.
67 *
68 * This provider is automatically wrapped in a memoize function that caches
69 * previously provided credentials.
70 *
71 * @param array $config Optional array of ecs/instance profile credentials
72 * provider options.
73 *
74 * @return callable
75 */
76 public static function defaultProvider(array $config = [])
77 {
78 $cacheable = [
79 'web_identity',
80 'sso',
81 'process_credentials',
82 'process_config',
83 'ecs',
84 'instance'
85 ];
86
87 $profileName = getenv(self::ENV_PROFILE) ?: 'default';
88
89 $defaultChain = [
90 'env' => self::env(),
91 'web_identity' => self::assumeRoleWithWebIdentityCredentialProvider($config),
92 ];
93 if (
94 !isset($config['use_aws_shared_config_files'])
95 || $config['use_aws_shared_config_files'] !== false
96 ) {
97 $defaultChain['sso'] = self::sso(
98 $profileName,
99 self::getHomeDir() . '/.aws/config',
100 $config
101 );
102 $defaultChain['process_credentials'] = self::process();
103 $defaultChain['ini'] = self::ini();
104 $defaultChain['process_config'] = self::process(
105 'profile ' . $profileName,
106 self::getHomeDir() . '/.aws/config'
107 );
108 $defaultChain['ini_config'] = self::ini(
109 'profile '. $profileName,
110 self::getHomeDir() . '/.aws/config'
111 );
112 }
113
114 if (self::shouldUseEcs()) {
115 $defaultChain['ecs'] = self::ecsCredentials($config);
116 } else {
117 $defaultChain['instance'] = self::instanceProfile($config);
118 }
119
120 if (isset($config['credentials'])
121 && $config['credentials'] instanceof CacheInterface
122 ) {
123 foreach ($cacheable as $provider) {
124 if (isset($defaultChain[$provider])) {
125 $defaultChain[$provider] = self::cache(
126 $defaultChain[$provider],
127 $config['credentials'],
128 'aws_cached_' . $provider . '_credentials'
129 );
130 }
131 }
132 }
133
134 return self::memoize(
135 call_user_func_array(
136 [CredentialProvider::class, 'chain'],
137 array_values($defaultChain)
138 )
139 );
140 }
141
142 /**
143 * Create a credential provider function from a set of static credentials.
144 *
145 * @param CredentialsInterface $creds
146 *
147 * @return callable
148 */
149 public static function fromCredentials(CredentialsInterface $creds)
150 {
151 $promise = Promise\Create::promiseFor($creds);
152
153 return function () use ($promise) {
154 return $promise;
155 };
156 }
157
158 /**
159 * Creates an aggregate credentials provider that invokes the provided
160 * variadic providers one after the other until a provider returns
161 * credentials.
162 *
163 * @return callable
164 */
165 public static function chain()
166 {
167 $links = func_get_args();
168 if (empty($links)) {
169 throw new \InvalidArgumentException('No providers in chain');
170 }
171
172 return function ($previousCreds = null) use ($links) {
173 /** @var callable $parent */
174 $parent = array_shift($links);
175 $promise = $parent();
176 while ($next = array_shift($links)) {
177 if ($next instanceof InstanceProfileProvider
178 && $previousCreds instanceof Credentials
179 ) {
180 $promise = $promise->otherwise(
181 function () use ($next, $previousCreds) {return $next($previousCreds);}
182 );
183 } else {
184 $promise = $promise->otherwise($next);
185 }
186 }
187 return $promise;
188 };
189 }
190
191 /**
192 * Wraps a credential provider and caches previously provided credentials.
193 *
194 * Ensures that cached credentials are refreshed when they expire.
195 *
196 * @param callable $provider Credentials provider function to wrap.
197 *
198 * @return callable
199 */
200 public static function memoize(callable $provider)
201 {
202 return function () use ($provider) {
203 static $result;
204 static $isConstant;
205
206 // Constant credentials will be returned constantly.
207 if ($isConstant) {
208 return $result;
209 }
210
211 // Create the initial promise that will be used as the cached value
212 // until it expires.
213 if (null === $result) {
214 $result = $provider();
215 }
216
217 // Return credentials that could expire and refresh when needed.
218 return $result
219 ->then(function (CredentialsInterface $creds) use ($provider, &$isConstant, &$result) {
220 // Determine if these are constant credentials.
221 if (!$creds->getExpiration()) {
222 $isConstant = true;
223 return $creds;
224 }
225
226 // Refresh expired credentials.
227 if (!$creds->isExpired()) {
228 return $creds;
229 }
230 // Refresh the result and forward the promise.
231 return $result = $provider($creds);
232 })
233 ->otherwise(function($reason) use (&$result) {
234 // Cleanup rejected promise.
235 $result = null;
236 return new Promise\RejectedPromise($reason);
237 });
238 };
239 }
240
241 /**
242 * Wraps a credential provider and saves provided credentials in an
243 * instance of Aws\CacheInterface. Forwards calls when no credentials found
244 * in cache and updates cache with the results.
245 *
246 * @param callable $provider Credentials provider function to wrap
247 * @param CacheInterface $cache Cache to store credentials
248 * @param string|null $cacheKey (optional) Cache key to use
249 *
250 * @return callable
251 */
252 public static function cache(
253 callable $provider,
254 CacheInterface $cache,
255 $cacheKey = null
256 ) {
257 $cacheKey = $cacheKey ?: 'aws_cached_credentials';
258
259 return function () use ($provider, $cache, $cacheKey) {
260 $found = $cache->get($cacheKey);
261 if ($found instanceof CredentialsInterface && !$found->isExpired()) {
262 return Promise\Create::promiseFor($found);
263 }
264
265 return $provider()
266 ->then(function (CredentialsInterface $creds) use (
267 $cache,
268 $cacheKey
269 ) {
270 $cache->set(
271 $cacheKey,
272 $creds,
273 null === $creds->getExpiration() ?
274 0 : $creds->getExpiration() - time()
275 );
276
277 return $creds;
278 });
279 };
280 }
281
282 /**
283 * Provider that creates credentials from environment variables
284 * AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN.
285 *
286 * @return callable
287 */
288 public static function env()
289 {
290 return function () {
291 // Use credentials from environment variables, if available
292 $key = getenv(self::ENV_KEY);
293 $secret = getenv(self::ENV_SECRET);
294 if ($key && $secret) {
295 return Promise\Create::promiseFor(
296 new Credentials($key, $secret, getenv(self::ENV_SESSION) ?: NULL)
297 );
298 }
299
300 return self::reject('Could not find environment variable '
301 . 'credentials in ' . self::ENV_KEY . '/' . self::ENV_SECRET);
302 };
303 }
304
305 /**
306 * Credential provider that creates credentials using instance profile
307 * credentials.
308 *
309 * @param array $config Array of configuration data.
310 *
311 * @return InstanceProfileProvider
312 * @see Aws\Credentials\InstanceProfileProvider for $config details.
313 */
314 public static function instanceProfile(array $config = [])
315 {
316 return new InstanceProfileProvider($config);
317 }
318
319 /**
320 * Credential provider that retrieves cached SSO credentials from the CLI
321 *
322 * @return callable
323 */
324 public static function sso($ssoProfileName = 'default',
325 $filename = null,
326 $config = []
327 ) {
328 $filename = $filename ?: (self::getHomeDir() . '/.aws/config');
329
330 return function () use ($ssoProfileName, $filename, $config) {
331 if (!@is_readable($filename)) {
332 return self::reject("Cannot read credentials from $filename");
333 }
334 $profiles = self::loadProfiles($filename);
335
336 if (isset($profiles[$ssoProfileName])) {
337 $ssoProfile = $profiles[$ssoProfileName];
338 } elseif (isset($profiles['profile ' . $ssoProfileName])) {
339 $ssoProfileName = 'profile ' . $ssoProfileName;
340 $ssoProfile = $profiles[$ssoProfileName];
341 } else {
342 return self::reject("Profile {$ssoProfileName} does not exist in {$filename}.");
343 }
344
345 if (!empty($ssoProfile['sso_session'])) {
346 return CredentialProvider::getSsoCredentials($profiles, $ssoProfileName, $filename, $config);
347 } else {
348 return CredentialProvider::getSsoCredentialsLegacy($profiles, $ssoProfileName, $filename, $config);
349 }
350 };
351 }
352
353 /**
354 * Credential provider that creates credentials using
355 * ecs credentials by a GET request, whose uri is specified
356 * by environment variable
357 *
358 * @param array $config Array of configuration data.
359 *
360 * @return EcsCredentialProvider
361 * @see Aws\Credentials\EcsCredentialProvider for $config details.
362 */
363 public static function ecsCredentials(array $config = [])
364 {
365 return new EcsCredentialProvider($config);
366 }
367
368 /**
369 * Credential provider that creates credentials using assume role
370 *
371 * @param array $config Array of configuration data
372 * @return callable
373 * @see Aws\Credentials\AssumeRoleCredentialProvider for $config details.
374 */
375 public static function assumeRole(array $config=[])
376 {
377 return new AssumeRoleCredentialProvider($config);
378 }
379
380 /**
381 * Credential provider that creates credentials by assuming role from a
382 * Web Identity Token
383 *
384 * @param array $config Array of configuration data
385 * @return callable
386 * @see Aws\Credentials\AssumeRoleWithWebIdentityCredentialProvider for
387 * $config details.
388 */
389 public static function assumeRoleWithWebIdentityCredentialProvider(array $config = [])
390 {
391 return function () use ($config) {
392 $arnFromEnv = getenv(self::ENV_ARN);
393 $tokenFromEnv = getenv(self::ENV_TOKEN_FILE);
394 $stsClient = isset($config['stsClient'])
395 ? $config['stsClient']
396 : null;
397 $region = isset($config['region'])
398 ? $config['region']
399 : null;
400
401 if ($tokenFromEnv && $arnFromEnv) {
402 $sessionName = getenv(self::ENV_ROLE_SESSION_NAME)
403 ? getenv(self::ENV_ROLE_SESSION_NAME)
404 : null;
405 $provider = new AssumeRoleWithWebIdentityCredentialProvider([
406 'RoleArn' => $arnFromEnv,
407 'WebIdentityTokenFile' => $tokenFromEnv,
408 'SessionName' => $sessionName,
409 'client' => $stsClient,
410 'region' => $region
411 ]);
412
413 return $provider();
414 }
415
416 $profileName = getenv(self::ENV_PROFILE) ?: 'default';
417 if (isset($config['filename'])) {
418 $profiles = self::loadProfiles($config['filename']);
419 } else {
420 $profiles = self::loadDefaultProfiles();
421 }
422
423 if (isset($profiles[$profileName])) {
424 $profile = $profiles[$profileName];
425 if (isset($profile['region'])) {
426 $region = $profile['region'];
427 }
428 if (isset($profile['web_identity_token_file'])
429 && isset($profile['role_arn'])
430 ) {
431 $sessionName = isset($profile['role_session_name'])
432 ? $profile['role_session_name']
433 : null;
434 $provider = new AssumeRoleWithWebIdentityCredentialProvider([
435 'RoleArn' => $profile['role_arn'],
436 'WebIdentityTokenFile' => $profile['web_identity_token_file'],
437 'SessionName' => $sessionName,
438 'client' => $stsClient,
439 'region' => $region
440 ]);
441
442 return $provider();
443 }
444 } else {
445 return self::reject("Unknown profile: $profileName");
446 }
447 return self::reject("No RoleArn or WebIdentityTokenFile specified");
448 };
449 }
450
451 /**
452 * Credentials provider that creates credentials using an ini file stored
453 * in the current user's home directory. A source can be provided
454 * in this file for assuming a role using the credential_source config option.
455 *
456 * @param string|null $profile Profile to use. If not specified will use
457 * the "default" profile in "~/.aws/credentials".
458 * @param string|null $filename If provided, uses a custom filename rather
459 * than looking in the home directory.
460 * @param array|null $config If provided, may contain the following:
461 * preferStaticCredentials: If true, prefer static
462 * credentials to role_arn if both are present
463 * disableAssumeRole: If true, disable support for
464 * roles that assume an IAM role. If true and role profile
465 * is selected, an error is raised.
466 * stsClient: StsClient used to assume role specified in profile
467 *
468 * @return callable
469 */
470 public static function ini($profile = null, $filename = null, array $config = [])
471 {
472 $filename = self::getFileName($filename);
473 $profile = $profile ?: (getenv(self::ENV_PROFILE) ?: 'default');
474
475 return function () use ($profile, $filename, $config) {
476 $preferStaticCredentials = isset($config['preferStaticCredentials'])
477 ? $config['preferStaticCredentials']
478 : false;
479 $disableAssumeRole = isset($config['disableAssumeRole'])
480 ? $config['disableAssumeRole']
481 : false;
482 $stsClient = isset($config['stsClient']) ? $config['stsClient'] : null;
483
484 if (!@is_readable($filename)) {
485 return self::reject("Cannot read credentials from $filename");
486 }
487 $data = self::loadProfiles($filename);
488 if ($data === false) {
489 return self::reject("Invalid credentials file: $filename");
490 }
491 if (!isset($data[$profile])) {
492 return self::reject("'$profile' not found in credentials file");
493 }
494
495 /*
496 In the CLI, the presence of both a role_arn and static credentials have
497 different meanings depending on how many profiles have been visited. For
498 the first profile processed, role_arn takes precedence over any static
499 credentials, but for all subsequent profiles, static credentials are
500 used if present, and only in their absence will the profile's
501 source_profile and role_arn keys be used to load another set of
502 credentials. This bool is intended to yield compatible behaviour in this
503 sdk.
504 */
505 $preferStaticCredentialsToRoleArn = ($preferStaticCredentials
506 && isset($data[$profile]['aws_access_key_id'])
507 && isset($data[$profile]['aws_secret_access_key']));
508
509 if (isset($data[$profile]['role_arn'])
510 && !$preferStaticCredentialsToRoleArn
511 ) {
512 if ($disableAssumeRole) {
513 return self::reject(
514 "Role assumption profiles are disabled. "
515 . "Failed to load profile " . $profile);
516 }
517 return self::loadRoleProfile(
518 $data,
519 $profile,
520 $filename,
521 $stsClient,
522 $config
523 );
524 }
525
526 if (!isset($data[$profile]['aws_access_key_id'])
527 || !isset($data[$profile]['aws_secret_access_key'])
528 ) {
529 return self::reject("No credentials present in INI profile "
530 . "'$profile' ($filename)");
531 }
532
533 if (empty($data[$profile]['aws_session_token'])) {
534 $data[$profile]['aws_session_token']
535 = isset($data[$profile]['aws_security_token'])
536 ? $data[$profile]['aws_security_token']
537 : null;
538 }
539
540 return Promise\Create::promiseFor(
541 new Credentials(
542 $data[$profile]['aws_access_key_id'],
543 $data[$profile]['aws_secret_access_key'],
544 $data[$profile]['aws_session_token']
545 )
546 );
547 };
548 }
549
550 /**
551 * Credentials provider that creates credentials using a process configured in
552 * ini file stored in the current user's home directory.
553 *
554 * @param string|null $profile Profile to use. If not specified will use
555 * the "default" profile in "~/.aws/credentials".
556 * @param string|null $filename If provided, uses a custom filename rather
557 * than looking in the home directory.
558 *
559 * @return callable
560 */
561 public static function process($profile = null, $filename = null)
562 {
563 $filename = self::getFileName($filename);
564 $profile = $profile ?: (getenv(self::ENV_PROFILE) ?: 'default');
565
566 return function () use ($profile, $filename) {
567 if (!@is_readable($filename)) {
568 return self::reject("Cannot read process credentials from $filename");
569 }
570 $data = \Aws\parse_ini_file($filename, true, INI_SCANNER_RAW);
571 if ($data === false) {
572 return self::reject("Invalid credentials file: $filename");
573 }
574 if (!isset($data[$profile])) {
575 return self::reject("'$profile' not found in credentials file");
576 }
577 if (!isset($data[$profile]['credential_process'])) {
578 return self::reject("No credential_process present in INI profile "
579 . "'$profile' ($filename)");
580 }
581
582 $credentialProcess = $data[$profile]['credential_process'];
583 $json = shell_exec($credentialProcess);
584
585 $processData = json_decode($json, true);
586
587 // Only support version 1
588 if (isset($processData['Version'])) {
589 if ($processData['Version'] !== 1) {
590 return self::reject("credential_process does not return Version == 1");
591 }
592 }
593
594 if (!isset($processData['AccessKeyId'])
595 || !isset($processData['SecretAccessKey']))
596 {
597 return self::reject("credential_process does not return valid credentials");
598 }
599
600 if (isset($processData['Expiration'])) {
601 try {
602 $expiration = new DateTimeResult($processData['Expiration']);
603 } catch (\Exception $e) {
604 return self::reject("credential_process returned invalid expiration");
605 }
606 $now = new DateTimeResult();
607 if ($expiration < $now) {
608 return self::reject("credential_process returned expired credentials");
609 }
610 $expires = $expiration->getTimestamp();
611 } else {
612 $expires = null;
613 }
614
615 if (empty($processData['SessionToken'])) {
616 $processData['SessionToken'] = null;
617 }
618
619 return Promise\Create::promiseFor(
620 new Credentials(
621 $processData['AccessKeyId'],
622 $processData['SecretAccessKey'],
623 $processData['SessionToken'],
624 $expires
625 )
626 );
627 };
628 }
629
630 /**
631 * Assumes role for profile that includes role_arn
632 *
633 * @return callable
634 */
635 private static function loadRoleProfile(
636 $profiles,
637 $profileName,
638 $filename,
639 $stsClient,
640 $config = []
641 ) {
642 $roleProfile = $profiles[$profileName];
643 $roleArn = isset($roleProfile['role_arn']) ? $roleProfile['role_arn'] : '';
644 $roleSessionName = isset($roleProfile['role_session_name'])
645 ? $roleProfile['role_session_name']
646 : 'aws-sdk-php-' . round(microtime(true) * 1000);
647
648 if (
649 empty($roleProfile['source_profile'])
650 == empty($roleProfile['credential_source'])
651 ) {
652 return self::reject("Either source_profile or credential_source must be set " .
653 "using profile " . $profileName . ", but not both."
654 );
655 }
656
657 $sourceProfileName = "";
658 if (!empty($roleProfile['source_profile'])) {
659 $sourceProfileName = $roleProfile['source_profile'];
660 if (!isset($profiles[$sourceProfileName])) {
661 return self::reject("source_profile " . $sourceProfileName
662 . " using profile " . $profileName . " does not exist"
663 );
664 }
665 if (isset($config['visited_profiles']) &&
666 in_array($roleProfile['source_profile'], $config['visited_profiles'])
667 ) {
668 return self::reject("Circular source_profile reference found.");
669 }
670 $config['visited_profiles'] [] = $roleProfile['source_profile'];
671 } else {
672 if (empty($roleArn)) {
673 return self::reject(
674 "A role_arn must be provided with credential_source in " .
675 "file {$filename} under profile {$profileName} "
676 );
677 }
678 }
679
680 if (empty($stsClient)) {
681 $sourceRegion = isset($profiles[$sourceProfileName]['region'])
682 ? $profiles[$sourceProfileName]['region']
683 : 'us-east-1';
684 $config['preferStaticCredentials'] = true;
685 $sourceCredentials = null;
686 if (!empty($roleProfile['source_profile'])){
687 $sourceCredentials = call_user_func(
688 CredentialProvider::ini($sourceProfileName, $filename, $config)
689 )->wait();
690 } else {
691 $sourceCredentials = self::getCredentialsFromSource(
692 $profileName,
693 $filename
694 );
695 }
696 $stsClient = new StsClient([
697 'credentials' => $sourceCredentials,
698 'region' => $sourceRegion,
699 'version' => '2011-06-15',
700 ]);
701 }
702
703 $result = $stsClient->assumeRole([
704 'RoleArn' => $roleArn,
705 'RoleSessionName' => $roleSessionName
706 ]);
707
708 $credentials = $stsClient->createCredentials($result);
709 return Promise\Create::promiseFor($credentials);
710 }
711
712 /**
713 * Gets the environment's HOME directory if available.
714 *
715 * @return null|string
716 */
717 private static function getHomeDir()
718 {
719 // On Linux/Unix-like systems, use the HOME environment variable
720 if ($homeDir = getenv('HOME')) {
721 return $homeDir;
722 }
723
724 // Get the HOMEDRIVE and HOMEPATH values for Windows hosts
725 $homeDrive = getenv('HOMEDRIVE');
726 $homePath = getenv('HOMEPATH');
727
728 return ($homeDrive && $homePath) ? $homeDrive . $homePath : null;
729 }
730
731 /**
732 * Gets profiles from specified $filename, or default ini files.
733 */
734 private static function loadProfiles($filename)
735 {
736 $profileData = \Aws\parse_ini_file($filename, true, INI_SCANNER_RAW);
737
738 // If loading .aws/credentials, also load .aws/config when AWS_SDK_LOAD_NONDEFAULT_CONFIG is set
739 if ($filename === self::getHomeDir() . '/.aws/credentials'
740 && getenv('AWS_SDK_LOAD_NONDEFAULT_CONFIG')
741 ) {
742 $configFilename = self::getHomeDir() . '/.aws/config';
743 $configProfileData = \Aws\parse_ini_file($configFilename, true, INI_SCANNER_RAW);
744 foreach ($configProfileData as $name => $profile) {
745 // standardize config profile names
746 $name = str_replace('profile ', '', $name);
747 if (!isset($profileData[$name])) {
748 $profileData[$name] = $profile;
749 }
750 }
751 }
752
753 return $profileData;
754 }
755
756 /**
757 * Gets profiles from ~/.aws/credentials and ~/.aws/config ini files
758 */
759 private static function loadDefaultProfiles() {
760 $profiles = [];
761 $credFile = self::getHomeDir() . '/.aws/credentials';
762 $configFile = self::getHomeDir() . '/.aws/config';
763 if (file_exists($credFile)) {
764 $profiles = \Aws\parse_ini_file($credFile, true, INI_SCANNER_RAW);
765 }
766
767 if (file_exists($configFile)) {
768 $configProfileData = \Aws\parse_ini_file($configFile, true, INI_SCANNER_RAW);
769 foreach ($configProfileData as $name => $profile) {
770 // standardize config profile names
771 $name = str_replace('profile ', '', $name);
772 if (!isset($profiles[$name])) {
773 $profiles[$name] = $profile;
774 }
775 }
776 }
777
778 return $profiles;
779 }
780
781 public static function getCredentialsFromSource(
782 $profileName = '',
783 $filename = '',
784 $config = []
785 ) {
786 $data = self::loadProfiles($filename);
787 $credentialSource = !empty($data[$profileName]['credential_source'])
788 ? $data[$profileName]['credential_source']
789 : null;
790 $credentialsPromise = null;
791
792 switch ($credentialSource) {
793 case 'Environment':
794 $credentialsPromise = self::env();
795 break;
796 case 'Ec2InstanceMetadata':
797 $credentialsPromise = self::instanceProfile($config);
798 break;
799 case 'EcsContainer':
800 $credentialsPromise = self::ecsCredentials($config);
801 break;
802 default:
803 throw new CredentialsException(
804 "Invalid credential_source found in config file: {$credentialSource}. Valid inputs "
805 . "include Environment, Ec2InstanceMetadata, and EcsContainer."
806 );
807 }
808
809 $credentialsResult = null;
810 try {
811 $credentialsResult = $credentialsPromise()->wait();
812 } catch (\Exception $reason) {
813 return self::reject(
814 "Unable to successfully retrieve credentials from the source specified in the"
815 . " credentials file: {$credentialSource}; failure message was: "
816 . $reason->getMessage()
817 );
818 }
819 return function () use ($credentialsResult) {
820 return Promise\Create::promiseFor($credentialsResult);
821 };
822 }
823
824 private static function reject($msg)
825 {
826 return new Promise\RejectedPromise(new CredentialsException($msg));
827 }
828
829 /**
830 * @param $filename
831 * @return string
832 */
833 private static function getFileName($filename)
834 {
835 if (!isset($filename)) {
836 $filename = getenv(self::ENV_SHARED_CREDENTIALS_FILE) ?:
837 (self::getHomeDir() . '/.aws/credentials');
838 }
839 return $filename;
840 }
841
842 /**
843 * @return boolean
844 */
845 public static function shouldUseEcs()
846 {
847 //Check for relative uri. if not, then full uri.
848 //fall back to server for each as getenv is not thread-safe.
849 return !empty(getenv(EcsCredentialProvider::ENV_URI))
850 || !empty($_SERVER[EcsCredentialProvider::ENV_URI])
851 || !empty(getenv(EcsCredentialProvider::ENV_FULL_URI))
852 || !empty($_SERVER[EcsCredentialProvider::ENV_FULL_URI]);
853 }
854
855 /**
856 * @param $profiles
857 * @param $ssoProfileName
858 * @param $filename
859 * @param $config
860 * @return Promise\PromiseInterface
861 */
862 private static function getSsoCredentials($profiles, $ssoProfileName, $filename, $config)
863 {
864 if (empty($config['ssoOidcClient'])) {
865 $ssoProfile = $profiles[$ssoProfileName];
866 $sessionName = $ssoProfile['sso_session'];
867 if (empty($profiles['sso-session ' . $sessionName])) {
868 return self::reject(
869 "Could not find sso-session {$sessionName} in {$filename}"
870 );
871 }
872 $ssoSession = $profiles['sso-session ' . $ssoProfile['sso_session']];
873 $ssoOidcClient = new Aws\SSOOIDC\SSOOIDCClient([
874 'region' => $ssoSession['sso_region'],
875 'version' => '2019-06-10',
876 'credentials' => false
877 ]);
878 } else {
879 $ssoOidcClient = $config['ssoClient'];
880 }
881
882 $tokenPromise = new Aws\Token\SsoTokenProvider(
883 $ssoProfileName,
884 $filename,
885 $ssoOidcClient
886 );
887 $token = $tokenPromise()->wait();
888 $ssoCredentials = CredentialProvider::getCredentialsFromSsoService(
889 $ssoProfile,
890 $ssoSession['sso_region'],
891 $token->getToken(),
892 $config
893 );
894 $expiration = $ssoCredentials['expiration'];
895 return Promise\Create::promiseFor(
896 new Credentials(
897 $ssoCredentials['accessKeyId'],
898 $ssoCredentials['secretAccessKey'],
899 $ssoCredentials['sessionToken'],
900 $expiration
901 )
902 );
903 }
904
905 /**
906 * @param $profiles
907 * @param $ssoProfileName
908 * @param $filename
909 * @param $config
910 * @return Promise\PromiseInterface
911 */
912 private static function getSsoCredentialsLegacy($profiles, $ssoProfileName, $filename, $config)
913 {
914 $ssoProfile = $profiles[$ssoProfileName];
915 if (empty($ssoProfile['sso_start_url'])
916 || empty($ssoProfile['sso_region'])
917 || empty($ssoProfile['sso_account_id'])
918 || empty($ssoProfile['sso_role_name'])
919 ) {
920 return self::reject(
921 "Profile {$ssoProfileName} in {$filename} must contain the following keys: "
922 . "sso_start_url, sso_region, sso_account_id, and sso_role_name."
923 );
924 }
925 $tokenLocation = self::getHomeDir()
926 . '/.aws/sso/cache/'
927 . sha1($ssoProfile['sso_start_url'])
928 . ".json";
929
930 if (!@is_readable($tokenLocation)) {
931 return self::reject("Unable to read token file at $tokenLocation");
932 }
933 $tokenData = json_decode(file_get_contents($tokenLocation), true);
934 if (empty($tokenData['accessToken']) || empty($tokenData['expiresAt'])) {
935 return self::reject(
936 "Token file at {$tokenLocation} must contain an access token and an expiration"
937 );
938 }
939 try {
940 $expiration = (new DateTimeResult($tokenData['expiresAt']))->getTimestamp();
941 } catch (\Exception $e) {
942 return self::reject("Cached SSO credentials returned an invalid expiration");
943 }
944 $now = time();
945 if ($expiration < $now) {
946 return self::reject("Cached SSO credentials returned expired credentials");
947 }
948 $ssoCredentials = CredentialProvider::getCredentialsFromSsoService(
949 $ssoProfile,
950 $ssoProfile['sso_region'],
951 $tokenData['accessToken'],
952 $config
953 );
954 return Promise\Create::promiseFor(
955 new Credentials(
956 $ssoCredentials['accessKeyId'],
957 $ssoCredentials['secretAccessKey'],
958 $ssoCredentials['sessionToken'],
959 $expiration
960 )
961 );
962 }
963 /**
964 * @param array $ssoProfile
965 * @param string $clientRegion
966 * @param string $accessToken
967 * @param array $config
968 * @return array|null
969 */
970 private static function getCredentialsFromSsoService($ssoProfile, $clientRegion, $accessToken, $config)
971 {
972 if (empty($config['ssoClient'])) {
973 $ssoClient = new Aws\SSO\SSOClient([
974 'region' => $clientRegion,
975 'version' => '2019-06-10',
976 'credentials' => false
977 ]);
978 } else {
979 $ssoClient = $config['ssoClient'];
980 }
981 $ssoResponse = $ssoClient->getRoleCredentials([
982 'accessToken' => $accessToken,
983 'accountId' => $ssoProfile['sso_account_id'],
984 'roleName' => $ssoProfile['sso_role_name']
985 ]);
986
987 $ssoCredentials = $ssoResponse['roleCredentials'];
988 return $ssoCredentials;
989 }
990 }
991
992