4848
4949# Populate values from the output of scripts/setup_external_accounts.sh.
5050_AUDIENCE_OIDC = "//iam.googleapis.com/projects/79992041559/locations/global/workloadIdentityPools/pool-73wslmxn/providers/oidc-73wslmxn"
51+ _AUDIENCE_AWS = "//iam.googleapis.com/projects/79992041559/locations/global/workloadIdentityPools/pool-73wslmxn/providers/aws-73wslmxn"
52+ _ROLE_AWS = "arn:aws:iam::077071391996:role/ci-python-test"
5153
5254
5355def dns_access_direct (request , project_id ):
@@ -100,6 +102,27 @@ def service_account_info(service_account_file):
100102 yield json .load (f )
101103
102104
105+ @pytest .fixture
106+ def aws_oidc_credentials (
107+ service_account_file , service_account_info , authenticated_request
108+ ):
109+ credentials = service_account .Credentials .from_service_account_file (
110+ service_account_file , scopes = ["https://www.googleapis.com/auth/cloud-platform" ]
111+ )
112+ result = authenticated_request (credentials )(
113+ url = "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/{}:generateIdToken" .format (
114+ service_account_info ["client_email" ]
115+ ),
116+ method = "POST" ,
117+ body = json .dumps (
118+ {"audience" : service_account_info ["client_id" ], "includeEmail" : True }
119+ ),
120+ )
121+ assert result .status == 200
122+
123+ yield json .loads (result .data )["token" ]
124+
125+
103126# Our external accounts tests involve setting up some preconditions, setting a
104127# credential file, and then making sure that our client libraries can work with
105128# the set credentials.
@@ -115,6 +138,14 @@ def get_project_dns(dns_access, credential_data):
115138 return dns_access ()
116139
117140
141+ def get_xml_value_by_tagname (data , tagname ):
142+ startIndex = data .index ("<{}>" .format (tagname ))
143+ if startIndex >= 0 :
144+ endIndex = data .index ("</{}>" .format (tagname ), startIndex )
145+ if endIndex > startIndex :
146+ return data [startIndex + len (tagname ) + 2 : endIndex ]
147+
148+
118149# This test makes sure that setting an accesible credential file
119150# works to allow access to Google resources.
120151def test_file_based_external_account (
@@ -211,3 +242,64 @@ def __enter__(self):
211242 },
212243 },
213244 )
245+
246+
247+ # AWS provider tests for AWS credentials
248+ # The test suite will also run tests for AWS credentials. This works as
249+ # follows. (Note prequisite setup is needed. This is documented in
250+ # setup_external_accounts.sh).
251+ # - iamcredentials:generateIdToken is used to generate a Google ID token using
252+ # the service account access token. The service account client_id is used as
253+ # audience.
254+ # - AWS STS AssumeRoleWithWebIdentity API is used to exchange this token for
255+ # temporary AWS security credentials for a specified AWS ARN role.
256+ # - AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN
257+ # environment variables are set using these credentials before the test is
258+ # run simulating an AWS VM.
259+ # - The test can now be run.
260+ def test_aws_based_external_account (
261+ aws_oidc_credentials , service_account_info , dns_access , http_request
262+ ):
263+
264+ response = http_request (
265+ url = (
266+ "https://sts.amazonaws.com/"
267+ "?Action=AssumeRoleWithWebIdentity"
268+ "&Version=2011-06-15"
269+ "&DurationSeconds=3600"
270+ "&RoleSessionName=python-test"
271+ "&RoleArn={}"
272+ "&WebIdentityToken={}"
273+ ).format (_ROLE_AWS , aws_oidc_credentials )
274+ )
275+ assert response .status == 200
276+
277+ # The returned data is in XML, but loading an XML parser would be overkill.
278+ # Searching the return text manually for the start and finish tag.
279+ data = response .data .decode ("utf-8" )
280+
281+ with patch .dict (
282+ os .environ ,
283+ {
284+ "AWS_REGION" : "us-east-2" ,
285+ "AWS_ACCESS_KEY_ID" : get_xml_value_by_tagname (data , "AccessKeyId" ),
286+ "AWS_SECRET_ACCESS_KEY" : get_xml_value_by_tagname (data , "SecretAccessKey" ),
287+ "AWS_SESSION_TOKEN" : get_xml_value_by_tagname (data , "SessionToken" ),
288+ },
289+ ):
290+ assert get_project_dns (
291+ dns_access ,
292+ {
293+ "type" : "external_account" ,
294+ "audience" : _AUDIENCE_AWS ,
295+ "subject_token_type" : "urn:ietf:params:aws:token-type:aws4_request" ,
296+ "token_url" : "https://sts.googleapis.com/v1/token" ,
297+ "service_account_impersonation_url" : "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/{}:generateAccessToken" .format (
298+ service_account_info ["client_email" ]
299+ ),
300+ "credential_source" : {
301+ "environment_id" : "aws1" ,
302+ "regional_cred_verification_url" : "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15" ,
303+ },
304+ },
305+ )
0 commit comments