import { GaxiosOptions } from 'gaxios' ; import { AwsRequestSigner } from './awsrequestsigner' ; import { BaseExternalAccountClient , BaseExternalAccountClientOptions , } from './baseexternalclient' ; import { RefreshOptions } from './oauth2client' ; /**
 * AWS credentials JSON interface. This is used for AWS workloads.
 */ export interface AwsClientOptions extends BaseExternalAccountClientOptions { credential_source : { environment_id : string ; region_url ? : string ; url ? : string ; regional_cred_verification_url : string ; } ; } /**
 * Interface defining the AWS security-credentials endpoint response.
 */ interface AwsSecurityCredentials { Code : string ; LastUpdated : string ; Type : string ; AccessKeyId : string ; SecretAccessKey : string ; Token : string ; Expiration : string ; } /**
 * AWS external account client. This is used for AWS workloads, where
 * AWS STS GetCallerIdentity serialized signed requests are exchanged for
 * GCP access token.
 */ export class AwsClient extends BaseExternalAccountClient { private readonly environmentId : string ; private readonly regionUrl ? : string ; private readonly securityCredentialsUrl ? : string ; private readonly regionalCredVerificationUrl : string ; private awsRequestSigner : AwsRequestSigner | null ; private region : string ; param options param additionalOptions constructor ( options , additionalOptions ? ) { super ( options , additionalOptions ) ; this . environmentId = options . credential_source . environment_id ; this . regionUrl = options . credential_source . region_url ; this . securityCredentialsUrl = options . credential_source . url ; this . regionalCredVerificationUrl = options . credential_source . regional_cred_verification_url ; const match = this . environmentId ?. match ( /^(aws)(\d+)$/ ) ; if ( ! match || ! this . regionalCredVerificationUrl ) { throw new Error ( 'No valid AWS "credential_source" provided' ) ; } else if ( parseInt ( match [ 2 ] , 10 ) !== 1 ) { throw new Error ( `aws version "${ match [ 2 ] }" is not supported in the current build.` ) ; } this . awsRequestSigner = null ; this . region = '' ; } return async retrieveSubjectToken ( ) { if ( ! this . awsRequestSigner ) { this . region = await this . getAwsRegion ( ) ; this . awsRequestSigner = new AwsRequestSigner ( async ( ) => { if ( process . env [ 'AWS_ACCESS_KEY_ID' ] && process . env [ 'AWS_SECRET_ACCESS_KEY' ] ) { return { accessKeyId : process . env [ 'AWS_ACCESS_KEY_ID' ] ! , secretAccessKey : process . env [ 'AWS_SECRET_ACCESS_KEY' ] ! , token : process . env [ 'AWS_SESSION_TOKEN' ] , } ; } const roleName = await this . getAwsRoleName ( ) ; const awsCreds = await this . getAwsSecurityCredentials ( roleName ) ; return { accessKeyId : awsCreds . AccessKeyId , secretAccessKey : awsCreds . SecretAccessKey , token : awsCreds . Token , } ; } , this . region ) ; } const options = await this . awsRequestSigner . getRequestOptions ( { url : this . regionalCredVerificationUrl . replace ( '{region}' , this . region ) , method : 'POST' , } ) ; const reformattedHeader = [ ] ; const extendedHeaders = Object . assign ( { 'x-goog-cloud-target-resource' : this . audience , } , options . headers ) ; for ( const key in extendedHeaders ) { reformattedHeader . push ( { key , value : extendedHeaders [ key ] , } ) ; } return encodeURIComponent ( JSON . stringify ( { url : options . url , method : options . method , headers : reformattedHeader , } ) ) ; } return private async getAwsRegion ( ) { if ( process . env [ 'AWS_REGION' ] || process . env [ 'AWS_DEFAULT_REGION' ] ) { return ( process . env [ 'AWS_REGION' ] || process . env [ 'AWS_DEFAULT_REGION' ] ) ! ; } if ( ! this . regionUrl ) { throw new Error ( 'Unable to determine AWS region due to missing ' + '"options.credential_source.region_url"' ) ; } const opts = { url : this . regionUrl , method : 'GET' , responseType : 'text' , } ; const response = await this . transporter . request < string > ( opts ) ; return response . data . substr ( 0 , response . data . length - 1 ) ; } return private async getAwsRoleName ( ) { if ( ! this . securityCredentialsUrl ) { throw new Error ( 'Unable to determine AWS role name due to missing ' + '"options.credential_source.url"' ) ; } const opts = { url : this . securityCredentialsUrl , method : 'GET' , responseType : 'text' , } ; const response = await this . transporter . request < string > ( opts ) ; return response . data ; } param roleName return private async getAwsSecurityCredentials ( roleName ) { const response = await this . transporter . request < AwsSecurityCredentials > ( { url : `${ this . securityCredentialsUrl }/${ roleName }` , responseType : 'json' , } ) ; return response . data ; } }