• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
19 #include "src/core/lib/security/credentials/credentials.h"
20 
21 #include <gmock/gmock.h>
22 #include <grpc/credentials.h>
23 #include <grpc/grpc_security.h>
24 #include <grpc/slice.h>
25 #include <grpc/support/alloc.h>
26 #include <grpc/support/port_platform.h>
27 #include <grpc/support/string_util.h>
28 #include <grpc/support/time.h>
29 #include <openssl/rsa.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include <string>
34 
35 #include "absl/log/check.h"
36 #include "absl/log/log.h"
37 #include "absl/strings/match.h"
38 #include "absl/strings/str_cat.h"
39 #include "absl/strings/str_format.h"
40 #include "absl/strings/str_replace.h"
41 #include "src/core/lib/channel/channel_args.h"
42 #include "src/core/lib/iomgr/error.h"
43 #include "src/core/lib/iomgr/timer_manager.h"
44 #include "src/core/lib/promise/exec_ctx_wakeup_scheduler.h"
45 #include "src/core/lib/promise/map.h"
46 #include "src/core/lib/promise/promise.h"
47 #include "src/core/lib/promise/seq.h"
48 #include "src/core/lib/security/context/security_context.h"
49 #include "src/core/lib/security/credentials/composite/composite_credentials.h"
50 #include "src/core/lib/security/credentials/external/aws_external_account_credentials.h"
51 #include "src/core/lib/security/credentials/external/external_account_credentials.h"
52 #include "src/core/lib/security/credentials/external/file_external_account_credentials.h"
53 #include "src/core/lib/security/credentials/external/url_external_account_credentials.h"
54 #include "src/core/lib/security/credentials/fake/fake_credentials.h"
55 #include "src/core/lib/security/credentials/gcp_service_account_identity/gcp_service_account_identity_credentials.h"
56 #include "src/core/lib/security/credentials/google_default/google_default_credentials.h"
57 #include "src/core/lib/security/credentials/iam/iam_credentials.h"
58 #include "src/core/lib/security/credentials/jwt/jwt_credentials.h"
59 #include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
60 #include "src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h"
61 #include "src/core/lib/security/credentials/xds/xds_credentials.h"
62 #include "src/core/lib/security/transport/auth_filters.h"
63 #include "src/core/lib/transport/error_utils.h"
64 #include "src/core/util/crash.h"
65 #include "src/core/util/env.h"
66 #include "src/core/util/host_port.h"
67 #include "src/core/util/http_client/httpcli.h"
68 #include "src/core/util/http_client/httpcli_ssl_credentials.h"
69 #include "src/core/util/json/json_reader.h"
70 #include "src/core/util/string.h"
71 #include "src/core/util/time.h"
72 #include "src/core/util/tmpfile.h"
73 #include "src/core/util/unique_type_name.h"
74 #include "src/core/util/uri.h"
75 #include "test/core/event_engine/event_engine_test_utils.h"
76 #include "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.h"
77 #include "test/core/test_util/test_config.h"
78 
79 namespace grpc_core {
80 
81 using grpc_event_engine::experimental::FuzzingEventEngine;
82 using internal::grpc_flush_cached_google_default_credentials;
83 using internal::set_gce_tenancy_checker_for_testing;
84 
85 namespace {
86 
87 // -- Constants. --
88 
89 const char test_google_iam_authorization_token[] = "blahblahblhahb";
90 const char test_google_iam_authority_selector[] = "respectmyauthoritah";
91 const char test_oauth2_bearer_token[] = "Bearer blaaslkdjfaslkdfasdsfasf";
92 
93 // This JSON key was generated with the GCE console and revoked immediately.
94 // The identifiers have been changed as well.
95 // Maximum size for a string literal is 509 chars in C89, yay!
96 const char test_json_key_str_part1[] =
97     "{ \"private_key\": \"-----BEGIN PRIVATE KEY-----"
98     "\\nMIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOEvJsnoHnyHkXcp\\n7mJE"
99     "qg"
100     "WGjiw71NfXByguekSKho65FxaGbsnSM9SMQAqVk7Q2rG+I0OpsT0LrWQtZ\\nyjSeg/"
101     "rWBQvS4hle4LfijkP3J5BG+"
102     "IXDMP8RfziNRQsenAXDNPkY4kJCvKux2xdD\\nOnVF6N7dL3nTYZg+"
103     "uQrNsMTz9UxVAgMBAAECgYEAzbLewe1xe9vy+2GoSsfib+28\\nDZgSE6Bu/"
104     "zuFoPrRc6qL9p2SsnV7txrunTyJkkOnPLND9ABAXybRTlcVKP/sGgza\\n/"
105     "8HpCqFYM9V8f34SBWfD4fRFT+n/"
106     "73cfRUtGXdXpseva2lh8RilIQfPhNZAncenU\\ngqXjDvpkypEusgXAykECQQD+";
107 const char test_json_key_str_part2[] =
108     "53XxNVnxBHsYb+AYEfklR96yVi8HywjVHP34+OQZ\\nCslxoHQM8s+"
109     "dBnjfScLu22JqkPv04xyxmt0QAKm9+vTdAkEA4ib7YvEAn2jXzcCI\\nEkoy2L/"
110     "XydR1GCHoacdfdAwiL2npOdnbvi4ZmdYRPY1LSTO058tQHKVXV7NLeCa3\\nAARh2QJBAMKeDA"
111     "G"
112     "W303SQv2cZTdbeaLKJbB5drz3eo3j7dDKjrTD9JupixFbzcGw\\n8FZi5c8idxiwC36kbAL6Hz"
113     "A"
114     "ZoX+ofI0CQE6KCzPJTtYNqyShgKAZdJ8hwOcvCZtf\\n6z8RJm0+"
115     "6YBd38lfh5j8mZd7aHFf6I17j5AQY7oPEc47TjJj/"
116     "5nZ68ECQQDvYuI3\\nLyK5fS8g0SYbmPOL9TlcHDOqwG0mrX9qpg5DC2fniXNSrrZ64GTDKdzZ"
117     "Y"
118     "Ap6LI9W\\nIqv4vr6y38N79TTC\\n-----END PRIVATE KEY-----\\n\", ";
119 const char test_json_key_str_part3[] =
120     "\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
121     "\"client_email\": "
122     "\"777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount."
123     "com\", \"client_id\": "
124     "\"777-abaslkan11hlb6nmim3bpspl31ud.apps.googleusercontent."
125     "com\", \"type\": \"service_account\" }";
126 
127 // Test refresh token.
128 const char test_refresh_token_str[] =
129     "{ \"client_id\": \"32555999999.apps.googleusercontent.com\","
130     "  \"client_secret\": \"EmssLNjJy1332hD4KFsecret\","
131     "  \"refresh_token\": \"1/Blahblasj424jladJDSGNf-u4Sua3HDA2ngjd42\","
132     "  \"type\": \"authorized_user\"}";
133 
134 const char test_external_account_credentials_psc_sts_str[] =
135     "{\"type\":\"external_account\",\"audience\":\"audience\",\"subject_"
136     "token_type\":\"subject_token_type\",\"service_account_impersonation_"
137     "url\":\"https://sts-xyz.p.googleapis.com:5555/"
138     "service_account_impersonation_url\",\"token_url\":\"https://"
139     "sts-xyz-123.p.googleapis.com:5555/token\",\"token_info_url\":\"https://"
140     "sts-xyz.p.googleapis.com:5555/introspect"
141     "token_info\",\"credential_source\":{\"file\":\"credentials_file_path\"},"
142     "\"quota_project_id\":\"quota_"
143     "project_id\",\"client_id\":\"client_id\",\"client_secret\":\"client_"
144     "secret\"}";
145 
146 const char test_external_account_credentials_psc_iam_str[] =
147     "{\"type\":\"external_account\",\"audience\":\"audience\",\"subject_"
148     "token_type\":\"subject_token_type\",\"service_account_impersonation_"
149     "url\":\"https://iamcredentials-xyz.p.googleapis.com:5555/"
150     "service_account_impersonation_url\",\"token_url\":\"https://"
151     "iamcredentials-xyz-123.p.googleapis.com:5555/"
152     "token\",\"token_info_url\":\"https://"
153     "iamcredentials-xyz-123.p.googleapis.com:5555/introspect"
154     "token_info\",\"credential_source\":{\"file\":\"credentials_file_path\"},"
155     "\"quota_project_id\":\"quota_"
156     "project_id\",\"client_id\":\"client_id\",\"client_secret\":\"client_"
157     "secret\"}";
158 
159 const char valid_oauth2_json_response[] =
160     "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\","
161     " \"expires_in\":3599, "
162     " \"token_type\":\"Bearer\"}";
163 
164 const char valid_sts_json_response[] =
165     "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\","
166     " \"expires_in\":3599, "
167     " \"issued_token_type\":\"urn:ietf:params:oauth:token-type:access_token\", "
168     " \"token_type\":\"Bearer\"}";
169 
170 const char test_scope[] = "perm1 perm2";
171 
172 const char test_signed_jwt[] =
173     "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImY0OTRkN2M1YWU2MGRmOTcyNmM4YW"
174     "U0MDcyZTViYTdmZDkwODg2YzcifQ";
175 const char test_signed_jwt_token_type[] =
176     "urn:ietf:params:oauth:token-type:id_token";
177 const char test_signed_jwt2[] =
178     "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImY0OTRkN2M1YWU2MGRmOTcyNmM5YW"
179     "U2MDcyZTViYTdnZDkwODg5YzcifQ";
180 const char test_signed_jwt_token_type2[] =
181     "urn:ietf:params:oauth:token-type:jwt";
182 const char test_signed_jwt_path_prefix[] = "test_sign_jwt";
183 
184 const char test_service_url[] = "https://foo.com/foo.v1";
185 const char test_service_url_no_service_name[] = "https://foo.com/";
186 const char other_test_service_url_no_service_name[] = "https://bar.com/";
187 const char test_method[] = "ThisIsNotAMethod";
188 
189 const char kTestUrlScheme[] = "https";
190 const char kTestAuthority[] = "foo.com";
191 const char kTestPath[] = "/foo.v1/ThisIsNotAMethod";
192 const char kTestOtherAuthority[] = "bar.com";
193 const char kTestOtherPath[] = "/bar.v1/ThisIsNotAMethod";
194 
195 const char test_sts_endpoint_url[] = "https://foo.com:5555/v1/token-exchange";
196 
197 const char valid_external_account_creds_token_exchange_response[] =
198     "{\"access_token\":\"token_exchange_access_token\","
199     " \"expires_in\":3599,"
200     " \"token_type\":\"Bearer\"}";
201 
202 const char
203     valid_external_account_creds_service_account_impersonation_response[] =
204         "{\"accessToken\":\"service_account_impersonation_access_token\","
205         " \"expireTime\":\"2050-01-01T00:00:00Z\"}";
206 
207 const char
208     valid_url_external_account_creds_options_credential_source_format_text[] =
209         "{\"url\":\"https://foo.com:5555/generate_subject_token_format_text\","
210         "\"headers\":{\"Metadata-Flavor\":\"Google\"}}";
211 
212 const char
213     valid_url_external_account_creds_options_credential_source_with_query_params_format_text
214         [] = "{\"url\":\"https://foo.com:5555/"
215              "path/to/url/creds?p1=v1&p2=v2\","
216              "\"headers\":{\"Metadata-Flavor\":\"Google\"}}";
217 
218 const char
219     valid_url_external_account_creds_retrieve_subject_token_response_format_text
220         [] = "test_subject_token";
221 
222 const char
223     valid_url_external_account_creds_options_credential_source_format_json[] =
224         "{\"url\":\"https://foo.com:5555/generate_subject_token_format_json\","
225         "\"headers\":{\"Metadata-Flavor\":\"Google\"},"
226         "\"format\":{\"type\":\"json\",\"subject_token_field_name\":\"access_"
227         "token\"}}";
228 
229 const char
230     valid_url_external_account_creds_retrieve_subject_token_response_format_json
231         [] = "{\"access_token\":\"test_subject_token\"}";
232 
233 const char invalid_url_external_account_creds_options_credential_source[] =
234     "{\"url\":\"invalid_credential_source_url\","
235     "\"headers\":{\"Metadata-Flavor\":\"Google\"}}";
236 
237 const char valid_aws_external_account_creds_retrieve_signing_keys_response[] =
238     "{\"AccessKeyId\":\"test_access_key_id\",\"SecretAccessKey\":"
239     "\"test_secret_access_key\",\"Token\":\"test_token\"}";
240 
241 const char aws_imdsv2_session_token[] = "imdsv2_session_token";
242 
243 const char valid_aws_external_account_creds_options_credential_source[] =
244     "{\"environment_id\":\"aws1\","
245     "\"region_url\":\"https://169.254.169.254:5555/region_url\","
246     "\"url\":\"https://169.254.169.254:5555/url\","
247     "\"regional_cred_verification_url\":\"https://foo.com:5555/"
248     "regional_cred_verification_url_{region}\"}";
249 
250 const char valid_aws_imdsv2_external_account_creds_options_credential_source[] =
251     "{\"environment_id\":\"aws1\","
252     "\"region_url\":\"http://169.254.169.254:5555/region_url\","
253     "\"url\":\"https://169.254.169.254:5555/url\","
254     "\"imdsv2_session_token_url\":\"https://169.254.169.254/"
255     "imdsv2_session_token_url\","
256     "\"regional_cred_verification_url\":\"https://foo.com:5555/"
257     "regional_cred_verification_url_{region}\"}";
258 
259 const char valid_aws_external_account_creds_options_credential_source_ipv6[] =
260     "{\"environment_id\":\"aws1\","
261     "\"region_url\":\"https://[fd00:ec2::254]:5555/region_url\","
262     "\"url\":\"http://[fd00:ec2::254]:5555/url\","
263     "\"imdsv2_session_token_url\":\"https://[fd00:ec2::254]/"
264     "imdsv2_session_token_url\","
265     "\"regional_cred_verification_url\":\"https://foo.com:5555/"
266     "regional_cred_verification_url_{region}\"}";
267 
268 const char
269     invalid_aws_external_account_creds_options_credential_source_unmatched_environment_id
270         [] = "{\"environment_id\":\"unsupported_aws_version\","
271              "\"region_url\":\"https://169.254.169.254:5555/region_url\","
272              "\"url\":\"https://169.254.169.254:5555/url\","
273              "\"regional_cred_verification_url\":\"https://foo.com:5555/"
274              "regional_cred_verification_url_{region}\"}";
275 
276 const char
277     invalid_aws_external_account_creds_options_credential_source_invalid_regional_cred_verification_url
278         [] = "{\"environment_id\":\"aws1\","
279              "\"region_url\":\"https://169.254.169.254:5555/region_url\","
280              "\"url\":\"https://169.254.169.254:5555/url\","
281              "\"regional_cred_verification_url\":\"invalid_regional_cred_"
282              "verification_url\"}";
283 
284 const char
285     invalid_aws_external_account_creds_options_credential_source_missing_role_name
286         [] = "{\"environment_id\":\"aws1\","
287              "\"region_url\":\"https://169.254.169.254:5555/region_url\","
288              "\"url\":\"https://169.254.169.254:5555/url_no_role_name\","
289              "\"regional_cred_verification_url\":\"https://foo.com:5555/"
290              "regional_cred_verification_url_{region}\"}";
291 
292 //  -- Global state flags. --
293 
294 bool g_test_is_on_gce = false;
295 
296 bool g_test_gce_tenancy_checker_called = false;
297 
298 // -- Utils. --
299 
test_json_key_str(void)300 char* test_json_key_str(void) {
301   size_t result_len = strlen(test_json_key_str_part1) +
302                       strlen(test_json_key_str_part2) +
303                       strlen(test_json_key_str_part3);
304   char* result = static_cast<char*>(gpr_malloc(result_len + 1));
305   char* current = result;
306   strcpy(result, test_json_key_str_part1);
307   current += strlen(test_json_key_str_part1);
308   strcpy(current, test_json_key_str_part2);
309   current += strlen(test_json_key_str_part2);
310   strcpy(current, test_json_key_str_part3);
311   return result;
312 }
313 
http_response(int status,const char * body)314 grpc_http_response http_response(int status, const char* body) {
315   grpc_http_response response;
316   response = {};
317   response.status = status;
318   response.body = gpr_strdup(const_cast<char*>(body));
319   response.body_length = strlen(body);
320   return response;
321 }
322 
323 // -- Tests. --
324 
325 class CredentialsTest : public ::testing::Test {
326  protected:
SetUp()327   void SetUp() override { grpc_init(); }
328 
TearDown()329   void TearDown() override { grpc_shutdown_blocking(); }
330 };
331 
TEST_F(CredentialsTest,TestOauth2TokenFetcherCredsParsingOk)332 TEST_F(CredentialsTest, TestOauth2TokenFetcherCredsParsingOk) {
333   ExecCtx exec_ctx;
334   absl::optional<Slice> token_value;
335   Duration token_lifetime;
336   grpc_http_response response = http_response(200, valid_oauth2_json_response);
337   CHECK(grpc_oauth2_token_fetcher_credentials_parse_server_response(
338             &response, &token_value, &token_lifetime) == GRPC_CREDENTIALS_OK);
339   CHECK(token_lifetime == Duration::Seconds(3599));
340   CHECK(token_value->as_string_view() ==
341         "Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_");
342   grpc_http_response_destroy(&response);
343 }
344 
TEST_F(CredentialsTest,TestOauth2TokenFetcherCredsParsingBadHttpStatus)345 TEST_F(CredentialsTest, TestOauth2TokenFetcherCredsParsingBadHttpStatus) {
346   ExecCtx exec_ctx;
347   absl::optional<Slice> token_value;
348   Duration token_lifetime;
349   grpc_http_response response = http_response(401, valid_oauth2_json_response);
350   CHECK(grpc_oauth2_token_fetcher_credentials_parse_server_response(
351             &response, &token_value, &token_lifetime) ==
352         GRPC_CREDENTIALS_ERROR);
353   grpc_http_response_destroy(&response);
354 }
355 
TEST_F(CredentialsTest,TestOauth2TokenFetcherCredsParsingEmptyHttpBody)356 TEST_F(CredentialsTest, TestOauth2TokenFetcherCredsParsingEmptyHttpBody) {
357   ExecCtx exec_ctx;
358   absl::optional<Slice> token_value;
359   Duration token_lifetime;
360   grpc_http_response response = http_response(200, "");
361   CHECK(grpc_oauth2_token_fetcher_credentials_parse_server_response(
362             &response, &token_value, &token_lifetime) ==
363         GRPC_CREDENTIALS_ERROR);
364   grpc_http_response_destroy(&response);
365 }
366 
TEST_F(CredentialsTest,TestOauth2TokenFetcherCredsParsingInvalidJson)367 TEST_F(CredentialsTest, TestOauth2TokenFetcherCredsParsingInvalidJson) {
368   ExecCtx exec_ctx;
369   absl::optional<Slice> token_value;
370   Duration token_lifetime;
371   grpc_http_response response =
372       http_response(200,
373                     "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\","
374                     " \"expires_in\":3599, "
375                     " \"token_type\":\"Bearer\"");
376   CHECK(grpc_oauth2_token_fetcher_credentials_parse_server_response(
377             &response, &token_value, &token_lifetime) ==
378         GRPC_CREDENTIALS_ERROR);
379   grpc_http_response_destroy(&response);
380 }
381 
TEST_F(CredentialsTest,TestOauth2TokenFetcherCredsParsingMissingToken)382 TEST_F(CredentialsTest, TestOauth2TokenFetcherCredsParsingMissingToken) {
383   ExecCtx exec_ctx;
384   absl::optional<Slice> token_value;
385   Duration token_lifetime;
386   grpc_http_response response = http_response(200,
387                                               "{"
388                                               " \"expires_in\":3599, "
389                                               " \"token_type\":\"Bearer\"}");
390   CHECK(grpc_oauth2_token_fetcher_credentials_parse_server_response(
391             &response, &token_value, &token_lifetime) ==
392         GRPC_CREDENTIALS_ERROR);
393   grpc_http_response_destroy(&response);
394 }
395 
TEST_F(CredentialsTest,TestOauth2TokenFetcherCredsParsingMissingTokenType)396 TEST_F(CredentialsTest, TestOauth2TokenFetcherCredsParsingMissingTokenType) {
397   ExecCtx exec_ctx;
398   absl::optional<Slice> token_value;
399   Duration token_lifetime;
400   grpc_http_response response =
401       http_response(200,
402                     "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\","
403                     " \"expires_in\":3599, "
404                     "}");
405   CHECK(grpc_oauth2_token_fetcher_credentials_parse_server_response(
406             &response, &token_value, &token_lifetime) ==
407         GRPC_CREDENTIALS_ERROR);
408   grpc_http_response_destroy(&response);
409 }
410 
TEST_F(CredentialsTest,TestOauth2TokenFetcherCredsParsingMissingTokenLifetime)411 TEST_F(CredentialsTest,
412        TestOauth2TokenFetcherCredsParsingMissingTokenLifetime) {
413   ExecCtx exec_ctx;
414   absl::optional<Slice> token_value;
415   Duration token_lifetime;
416   grpc_http_response response =
417       http_response(200,
418                     "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\","
419                     " \"token_type\":\"Bearer\"}");
420   CHECK(grpc_oauth2_token_fetcher_credentials_parse_server_response(
421             &response, &token_value, &token_lifetime) ==
422         GRPC_CREDENTIALS_ERROR);
423   grpc_http_response_destroy(&response);
424 }
425 
426 class RequestMetadataState : public RefCounted<RequestMetadataState> {
427  public:
NewInstance(grpc_error_handle expected_error,std::string expected,absl::optional<bool> expect_delay=absl::nullopt)428   static RefCountedPtr<RequestMetadataState> NewInstance(
429       grpc_error_handle expected_error, std::string expected,
430       absl::optional<bool> expect_delay = absl::nullopt) {
431     return MakeRefCounted<RequestMetadataState>(
432         expected_error, std::move(expected), expect_delay,
433         grpc_polling_entity_create_from_pollset_set(grpc_pollset_set_create()));
434   }
435 
RequestMetadataState(grpc_error_handle expected_error,std::string expected,absl::optional<bool> expect_delay,grpc_polling_entity pollent)436   RequestMetadataState(grpc_error_handle expected_error, std::string expected,
437                        absl::optional<bool> expect_delay,
438                        grpc_polling_entity pollent)
439       : expected_error_(expected_error),
440         expected_(std::move(expected)),
441         expect_delay_(expect_delay),
442         pollent_(pollent) {}
443 
~RequestMetadataState()444   ~RequestMetadataState() override {
445     grpc_pollset_set_destroy(grpc_polling_entity_pollset_set(&pollent_));
446   }
447 
RunRequestMetadataTest(grpc_call_credentials * creds,const char * url_scheme,const char * authority,const char * path)448   void RunRequestMetadataTest(grpc_call_credentials* creds,
449                               const char* url_scheme, const char* authority,
450                               const char* path) {
451     auto self = Ref();
452     get_request_metadata_args_.security_connector =
453         MakeRefCounted<BogusSecurityConnector>(url_scheme);
454     md_.Set(HttpAuthorityMetadata(), Slice::FromStaticString(authority));
455     md_.Set(HttpPathMetadata(), Slice::FromStaticString(path));
456     activity_ = MakeActivity(
457         [this, creds] {
458           return Seq(
459               CheckDelayed(creds->GetRequestMetadata(
460                   ClientMetadataHandle(&md_, Arena::PooledDeleter(nullptr)),
461                   &get_request_metadata_args_)),
462               [this](std::tuple<absl::StatusOr<ClientMetadataHandle>, bool>
463                          metadata_and_delayed) {
464                 auto& metadata = std::get<0>(metadata_and_delayed);
465                 const bool delayed = std::get<1>(metadata_and_delayed);
466                 if (expect_delay_.has_value()) {
467                   EXPECT_EQ(delayed, *expect_delay_);
468                 }
469                 if (metadata.ok()) {
470                   EXPECT_EQ(metadata->get(), &md_);
471                 }
472                 return metadata.status();
473               });
474         },
475         ExecCtxWakeupScheduler(),
476         [self](absl::Status status) mutable {
477           self->CheckRequestMetadata(
478               absl_status_to_grpc_error(std::move(status)));
479           self.reset();
480         },
481         arena_.get(), &pollent_);
482   }
483 
484  private:
485   // No-op security connector, exists only to inject url_scheme.
486   class BogusSecurityConnector : public grpc_channel_security_connector {
487    public:
BogusSecurityConnector(absl::string_view url_scheme)488     explicit BogusSecurityConnector(absl::string_view url_scheme)
489         : grpc_channel_security_connector(url_scheme, nullptr, nullptr) {}
490 
check_peer(tsi_peer,grpc_endpoint *,const ChannelArgs &,RefCountedPtr<grpc_auth_context> *,grpc_closure *)491     void check_peer(tsi_peer, grpc_endpoint*, const ChannelArgs&,
492                     RefCountedPtr<grpc_auth_context>*, grpc_closure*) override {
493       Crash("unreachable");
494     }
495 
cancel_check_peer(grpc_closure *,grpc_error_handle)496     void cancel_check_peer(grpc_closure*, grpc_error_handle) override {
497       Crash("unreachable");
498     }
499 
cmp(const grpc_security_connector *) const500     int cmp(const grpc_security_connector*) const override {
501       GPR_UNREACHABLE_CODE(return 0);
502     }
503 
CheckCallHost(absl::string_view,grpc_auth_context *)504     ArenaPromise<absl::Status> CheckCallHost(absl::string_view,
505                                              grpc_auth_context*) override {
506       GPR_UNREACHABLE_CODE(
507           return Immediate(absl::PermissionDeniedError("should never happen")));
508     }
509 
add_handshakers(const ChannelArgs &,grpc_pollset_set *,HandshakeManager *)510     void add_handshakers(const ChannelArgs&, grpc_pollset_set*,
511                          HandshakeManager*) override {
512       Crash("unreachable");
513     }
514   };
515 
CheckRequestMetadata(grpc_error_handle error)516   void CheckRequestMetadata(grpc_error_handle error) {
517     if (expected_error_.ok()) {
518       ASSERT_TRUE(error.ok()) << error;
519     } else {
520       grpc_status_code actual_code;
521       std::string actual_message;
522       grpc_error_get_status(error, Timestamp::InfFuture(), &actual_code,
523                             &actual_message, nullptr, nullptr);
524       EXPECT_EQ(absl::Status(static_cast<absl::StatusCode>(actual_code),
525                              actual_message),
526                 expected_error_);
527     }
528     md_.Remove(HttpAuthorityMetadata());
529     md_.Remove(HttpPathMetadata());
530     LOG(INFO) << "expected metadata: " << expected_;
531     LOG(INFO) << "actual metadata: " << md_.DebugString();
532   }
533 
534   grpc_error_handle expected_error_;
535   std::string expected_;
536   absl::optional<bool> expect_delay_;
537   RefCountedPtr<Arena> arena_ = SimpleArenaAllocator()->MakeArena();
538   grpc_metadata_batch md_;
539   grpc_call_credentials::GetRequestMetadataArgs get_request_metadata_args_;
540   grpc_polling_entity pollent_;
541   ActivityPtr activity_;
542 };
543 
TEST_F(CredentialsTest,TestGoogleIamCreds)544 TEST_F(CredentialsTest, TestGoogleIamCreds) {
545   ExecCtx exec_ctx;
546   auto state = RequestMetadataState::NewInstance(
547       absl::OkStatus(),
548       absl::StrCat(GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, ": ",
549                    test_google_iam_authorization_token, ", ",
550                    GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, ": ",
551                    test_google_iam_authority_selector));
552   grpc_call_credentials* creds = grpc_google_iam_credentials_create(
553       test_google_iam_authorization_token, test_google_iam_authority_selector,
554       nullptr);
555   // Check security level.
556   CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
557   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
558                                 kTestPath);
559   creds->Unref();
560 }
561 
TEST_F(CredentialsTest,TestAccessTokenCreds)562 TEST_F(CredentialsTest, TestAccessTokenCreds) {
563   ExecCtx exec_ctx;
564   auto state = RequestMetadataState::NewInstance(absl::OkStatus(),
565                                                  "authorization: Bearer blah");
566   grpc_call_credentials* creds =
567       grpc_access_token_credentials_create("blah", nullptr);
568   CHECK(creds->type() == grpc_access_token_credentials::Type());
569   // Check security level.
570   CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
571   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
572                                 kTestPath);
573   creds->Unref();
574 }
575 
576 class check_channel_oauth2 final : public grpc_channel_credentials {
577  public:
create_security_connector(RefCountedPtr<grpc_call_credentials> call_creds,const char *,ChannelArgs *)578   RefCountedPtr<grpc_channel_security_connector> create_security_connector(
579       RefCountedPtr<grpc_call_credentials> call_creds, const char* /*target*/,
580       ChannelArgs* /*new_args*/) override {
581     CHECK(type() == Type());
582     CHECK(call_creds != nullptr);
583     CHECK(call_creds->type() == grpc_access_token_credentials::Type());
584     return nullptr;
585   }
586 
Type()587   static UniqueTypeName Type() {
588     static UniqueTypeName::Factory kFactory("check_channel_oauth2");
589     return kFactory.Create();
590   }
591 
type() const592   UniqueTypeName type() const override { return Type(); }
593 
594  private:
cmp_impl(const grpc_channel_credentials * other) const595   int cmp_impl(const grpc_channel_credentials* other) const override {
596     // TODO(yashykt): Check if we can do something better here
597     return QsortCompare(static_cast<const grpc_channel_credentials*>(this),
598                         other);
599   }
600 };
601 
TEST_F(CredentialsTest,TestChannelOauth2CompositeCreds)602 TEST_F(CredentialsTest, TestChannelOauth2CompositeCreds) {
603   ExecCtx exec_ctx;
604   ChannelArgs new_args;
605   grpc_channel_credentials* channel_creds = new check_channel_oauth2();
606   grpc_call_credentials* oauth2_creds =
607       grpc_access_token_credentials_create("blah", nullptr);
608   grpc_channel_credentials* channel_oauth2_creds =
609       grpc_composite_channel_credentials_create(channel_creds, oauth2_creds,
610                                                 nullptr);
611   grpc_channel_credentials_release(channel_creds);
612   grpc_call_credentials_release(oauth2_creds);
613   channel_oauth2_creds->create_security_connector(nullptr, nullptr, &new_args);
614   grpc_channel_credentials_release(channel_oauth2_creds);
615 }
616 
TEST_F(CredentialsTest,TestOauth2GoogleIamCompositeCreds)617 TEST_F(CredentialsTest, TestOauth2GoogleIamCompositeCreds) {
618   ExecCtx exec_ctx;
619   auto state = RequestMetadataState::NewInstance(
620       absl::OkStatus(),
621       absl::StrCat(GRPC_AUTHORIZATION_METADATA_KEY, ": ",
622                    test_oauth2_bearer_token, ", ",
623                    GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, ": ",
624                    test_google_iam_authorization_token, ", ",
625                    GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, ": ",
626                    test_google_iam_authority_selector));
627   grpc_call_credentials* oauth2_creds = grpc_md_only_test_credentials_create(
628       "authorization", test_oauth2_bearer_token);
629 
630   // Check security level of fake credentials.
631   CHECK_EQ(oauth2_creds->min_security_level(), GRPC_SECURITY_NONE);
632 
633   grpc_call_credentials* google_iam_creds = grpc_google_iam_credentials_create(
634       test_google_iam_authorization_token, test_google_iam_authority_selector,
635       nullptr);
636   grpc_call_credentials* composite_creds =
637       grpc_composite_call_credentials_create(oauth2_creds, google_iam_creds,
638                                              nullptr);
639   // Check security level of composite credentials.
640   CHECK_EQ(composite_creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
641 
642   oauth2_creds->Unref();
643   google_iam_creds->Unref();
644   CHECK(composite_creds->type() == grpc_composite_call_credentials::Type());
645   const grpc_composite_call_credentials::CallCredentialsList& creds_list =
646       static_cast<const grpc_composite_call_credentials*>(composite_creds)
647           ->inner();
648   CHECK_EQ(creds_list.size(), 2);
649   CHECK(creds_list[0]->type() == grpc_md_only_test_credentials::Type());
650   CHECK(creds_list[1]->type() == grpc_google_iam_credentials::Type());
651   state->RunRequestMetadataTest(composite_creds, kTestUrlScheme, kTestAuthority,
652                                 kTestPath);
653   composite_creds->Unref();
654 }
655 
656 class check_channel_oauth2_google_iam final : public grpc_channel_credentials {
657  public:
create_security_connector(RefCountedPtr<grpc_call_credentials> call_creds,const char *,ChannelArgs *)658   RefCountedPtr<grpc_channel_security_connector> create_security_connector(
659       RefCountedPtr<grpc_call_credentials> call_creds, const char* /*target*/,
660       ChannelArgs* /*new_args*/) override {
661     CHECK(type() == Type());
662     CHECK(call_creds != nullptr);
663     CHECK(call_creds->type() == grpc_composite_call_credentials::Type());
664     const grpc_composite_call_credentials::CallCredentialsList& creds_list =
665         static_cast<const grpc_composite_call_credentials*>(call_creds.get())
666             ->inner();
667     CHECK(creds_list[0]->type() == grpc_access_token_credentials::Type());
668     CHECK(creds_list[1]->type() == grpc_google_iam_credentials::Type());
669     return nullptr;
670   }
671 
Type()672   static UniqueTypeName Type() {
673     static UniqueTypeName::Factory kFactory("check_channel_oauth2_google_iam");
674     return kFactory.Create();
675   }
676 
type() const677   UniqueTypeName type() const override { return Type(); }
678 
679  private:
cmp_impl(const grpc_channel_credentials * other) const680   int cmp_impl(const grpc_channel_credentials* other) const override {
681     // TODO(yashykt): Check if we can do something better here
682     return QsortCompare(static_cast<const grpc_channel_credentials*>(this),
683                         other);
684   }
685 };
686 
TEST_F(CredentialsTest,TestChannelOauth2GoogleIamCompositeCreds)687 TEST_F(CredentialsTest, TestChannelOauth2GoogleIamCompositeCreds) {
688   ExecCtx exec_ctx;
689   ChannelArgs new_args;
690   grpc_channel_credentials* channel_creds =
691       new check_channel_oauth2_google_iam();
692   grpc_call_credentials* oauth2_creds =
693       grpc_access_token_credentials_create("blah", nullptr);
694   grpc_channel_credentials* channel_oauth2_creds =
695       grpc_composite_channel_credentials_create(channel_creds, oauth2_creds,
696                                                 nullptr);
697   grpc_call_credentials* google_iam_creds = grpc_google_iam_credentials_create(
698       test_google_iam_authorization_token, test_google_iam_authority_selector,
699       nullptr);
700 
701   grpc_channel_credentials* channel_oauth2_iam_creds =
702       grpc_composite_channel_credentials_create(channel_oauth2_creds,
703                                                 google_iam_creds, nullptr);
704   grpc_channel_credentials_release(channel_creds);
705   grpc_call_credentials_release(oauth2_creds);
706   grpc_channel_credentials_release(channel_oauth2_creds);
707   grpc_call_credentials_release(google_iam_creds);
708 
709   channel_oauth2_iam_creds->create_security_connector(nullptr, nullptr,
710                                                       &new_args);
711 
712   grpc_channel_credentials_release(channel_oauth2_iam_creds);
713 }
714 
validate_compute_engine_http_request(const grpc_http_request * request,const URI & uri)715 void validate_compute_engine_http_request(const grpc_http_request* request,
716                                           const URI& uri) {
717   EXPECT_EQ(uri.authority(), "metadata.google.internal.");
718   EXPECT_EQ(uri.path(),
719             "/computeMetadata/v1/instance/service-accounts/default/token");
720   ASSERT_EQ(request->hdr_count, 1);
721   EXPECT_EQ(absl::string_view(request->hdrs[0].key), "Metadata-Flavor");
722   EXPECT_EQ(absl::string_view(request->hdrs[0].value), "Google");
723 }
724 
compute_engine_httpcli_get_success_override(const grpc_http_request * request,const URI & uri,Timestamp,grpc_closure * on_done,grpc_http_response * response)725 int compute_engine_httpcli_get_success_override(
726     const grpc_http_request* request, const URI& uri, Timestamp /*deadline*/,
727     grpc_closure* on_done, grpc_http_response* response) {
728   validate_compute_engine_http_request(request, uri);
729   *response = http_response(200, valid_oauth2_json_response);
730   ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus());
731   return 1;
732 }
733 
compute_engine_httpcli_get_failure_override(const grpc_http_request * request,const URI & uri,Timestamp,grpc_closure * on_done,grpc_http_response * response)734 int compute_engine_httpcli_get_failure_override(
735     const grpc_http_request* request, const URI& uri, Timestamp /*deadline*/,
736     grpc_closure* on_done, grpc_http_response* response) {
737   validate_compute_engine_http_request(request, uri);
738   *response = http_response(403, "Not Authorized.");
739   ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus());
740   return 1;
741 }
742 
httpcli_post_should_not_be_called(const grpc_http_request *,const URI &,absl::string_view,Timestamp,grpc_closure *,grpc_http_response *)743 int httpcli_post_should_not_be_called(const grpc_http_request* /*request*/,
744                                       const URI& /*uri*/,
745                                       absl::string_view /*body*/,
746                                       Timestamp /*deadline*/,
747                                       grpc_closure* /*on_done*/,
748                                       grpc_http_response* /*response*/) {
749   CHECK(false) << "HTTP POST should not be called";
750   return 1;
751 }
752 
httpcli_get_should_not_be_called(const grpc_http_request *,const URI &,Timestamp,grpc_closure *,grpc_http_response *)753 int httpcli_get_should_not_be_called(const grpc_http_request* /*request*/,
754                                      const URI& /*uri*/, Timestamp /*deadline*/,
755                                      grpc_closure* /*on_done*/,
756                                      grpc_http_response* /*response*/) {
757   CHECK(false) << "HTTP GET should not be called";
758   return 1;
759 }
760 
httpcli_put_should_not_be_called(const grpc_http_request *,const URI &,absl::string_view,Timestamp,grpc_closure *,grpc_http_response *)761 int httpcli_put_should_not_be_called(const grpc_http_request* /*request*/,
762                                      const URI& /*uri*/,
763                                      absl::string_view /*body*/,
764                                      Timestamp /*deadline*/,
765                                      grpc_closure* /*on_done*/,
766                                      grpc_http_response* /*response*/) {
767   CHECK(false) << "HTTP PUT should not be called";
768   return 1;
769 }
770 
TEST_F(CredentialsTest,TestComputeEngineCredsSuccess)771 TEST_F(CredentialsTest, TestComputeEngineCredsSuccess) {
772   ExecCtx exec_ctx;
773   std::string emd = "authorization: Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_";
774   const char expected_creds_debug_string[] =
775       "GoogleComputeEngineTokenFetcherCredentials{"
776       "OAuth2TokenFetcherCredentials}";
777   grpc_call_credentials* creds =
778       grpc_google_compute_engine_credentials_create(nullptr);
779   // Check security level.
780   CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
781 
782   // First request: http get should be called.
783   auto state = RequestMetadataState::NewInstance(absl::OkStatus(), emd);
784   HttpRequest::SetOverride(compute_engine_httpcli_get_success_override,
785                            httpcli_post_should_not_be_called,
786                            httpcli_put_should_not_be_called);
787   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
788                                 kTestPath);
789   ExecCtx::Get()->Flush();
790 
791   // Second request: the cached token should be served directly.
792   state = RequestMetadataState::NewInstance(absl::OkStatus(), emd);
793   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
794                            httpcli_post_should_not_be_called,
795                            httpcli_put_should_not_be_called);
796   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
797                                 kTestPath);
798   ExecCtx::Get()->Flush();
799 
800   CHECK_EQ(strcmp(creds->debug_string().c_str(), expected_creds_debug_string),
801            0);
802   creds->Unref();
803   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
804 }
805 
TEST_F(CredentialsTest,TestComputeEngineCredsFailure)806 TEST_F(CredentialsTest, TestComputeEngineCredsFailure) {
807   ExecCtx exec_ctx;
808   const char expected_creds_debug_string[] =
809       "GoogleComputeEngineTokenFetcherCredentials{"
810       "OAuth2TokenFetcherCredentials}";
811   auto state = RequestMetadataState::NewInstance(
812       // TODO(roth): This should return UNAUTHENTICATED.
813       absl::UnavailableError("error parsing oauth2 token"), {});
814   grpc_call_credentials* creds =
815       grpc_google_compute_engine_credentials_create(nullptr);
816   HttpRequest::SetOverride(compute_engine_httpcli_get_failure_override,
817                            httpcli_post_should_not_be_called,
818                            httpcli_put_should_not_be_called);
819   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
820                                 kTestPath);
821   CHECK_EQ(strcmp(creds->debug_string().c_str(), expected_creds_debug_string),
822            0);
823   creds->Unref();
824   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
825 }
826 
validate_refresh_token_http_request(const grpc_http_request * request,const URI & uri,absl::string_view body)827 void validate_refresh_token_http_request(const grpc_http_request* request,
828                                          const URI& uri,
829                                          absl::string_view body) {
830   // The content of the assertion is tested extensively in json_token_test.
831   EXPECT_EQ(body, absl::StrFormat(GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING,
832                                   "32555999999.apps.googleusercontent.com",
833                                   "EmssLNjJy1332hD4KFsecret",
834                                   "1/Blahblasj424jladJDSGNf-u4Sua3HDA2ngjd42"));
835   EXPECT_EQ(uri.authority(), GRPC_GOOGLE_OAUTH2_SERVICE_HOST);
836   EXPECT_EQ(uri.path(), GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH);
837   ASSERT_EQ(request->hdr_count, 1);
838   EXPECT_EQ(absl::string_view(request->hdrs[0].key), "Content-Type");
839   EXPECT_EQ(absl::string_view(request->hdrs[0].value),
840             "application/x-www-form-urlencoded");
841 }
842 
refresh_token_httpcli_post_success(const grpc_http_request * request,const URI & uri,absl::string_view body,Timestamp,grpc_closure * on_done,grpc_http_response * response)843 int refresh_token_httpcli_post_success(const grpc_http_request* request,
844                                        const URI& uri, absl::string_view body,
845                                        Timestamp /*deadline*/,
846                                        grpc_closure* on_done,
847                                        grpc_http_response* response) {
848   validate_refresh_token_http_request(request, uri, body);
849   *response = http_response(200, valid_oauth2_json_response);
850   ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus());
851   return 1;
852 }
853 
token_httpcli_post_failure(const grpc_http_request *,const URI &,absl::string_view,Timestamp,grpc_closure * on_done,grpc_http_response * response)854 int token_httpcli_post_failure(const grpc_http_request* /*request*/,
855                                const URI& /*uri*/, absl::string_view /*body*/,
856                                Timestamp /*deadline*/, grpc_closure* on_done,
857                                grpc_http_response* response) {
858   *response = http_response(403, "Not Authorized.");
859   ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus());
860   return 1;
861 }
862 
TEST_F(CredentialsTest,TestRefreshTokenCredsSuccess)863 TEST_F(CredentialsTest, TestRefreshTokenCredsSuccess) {
864   ExecCtx exec_ctx;
865   std::string emd = "authorization: Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_";
866   const char expected_creds_debug_string[] =
867       "GoogleRefreshToken{ClientID:32555999999.apps.googleusercontent.com,"
868       "OAuth2TokenFetcherCredentials}";
869   grpc_call_credentials* creds = grpc_google_refresh_token_credentials_create(
870       test_refresh_token_str, nullptr);
871 
872   // Check security level.
873   CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
874 
875   // First request: http put should be called.
876   auto state = RequestMetadataState::NewInstance(absl::OkStatus(), emd);
877   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
878                            refresh_token_httpcli_post_success,
879                            httpcli_put_should_not_be_called);
880   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
881                                 kTestPath);
882   ExecCtx::Get()->Flush();
883 
884   // Second request: the cached token should be served directly.
885   state = RequestMetadataState::NewInstance(absl::OkStatus(), emd);
886   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
887                            httpcli_post_should_not_be_called,
888                            httpcli_put_should_not_be_called);
889   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
890                                 kTestPath);
891   ExecCtx::Get()->Flush();
892   CHECK_EQ(strcmp(creds->debug_string().c_str(), expected_creds_debug_string),
893            0);
894 
895   creds->Unref();
896   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
897 }
898 
TEST_F(CredentialsTest,TestRefreshTokenCredsFailure)899 TEST_F(CredentialsTest, TestRefreshTokenCredsFailure) {
900   ExecCtx exec_ctx;
901   const char expected_creds_debug_string[] =
902       "GoogleRefreshToken{ClientID:32555999999.apps.googleusercontent.com,"
903       "OAuth2TokenFetcherCredentials}";
904   auto state = RequestMetadataState::NewInstance(
905       // TODO(roth): This should return UNAUTHENTICATED.
906       absl::UnavailableError("error parsing oauth2 token"), {});
907   grpc_call_credentials* creds = grpc_google_refresh_token_credentials_create(
908       test_refresh_token_str, nullptr);
909   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
910                            token_httpcli_post_failure,
911                            httpcli_put_should_not_be_called);
912   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
913                                 kTestPath);
914   CHECK_EQ(strcmp(creds->debug_string().c_str(), expected_creds_debug_string),
915            0);
916 
917   creds->Unref();
918   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
919 }
920 
TEST_F(CredentialsTest,TestValidStsCredsOptions)921 TEST_F(CredentialsTest, TestValidStsCredsOptions) {
922   grpc_sts_credentials_options valid_options = {
923       test_sts_endpoint_url,        // sts_endpoint_url
924       nullptr,                      // resource
925       nullptr,                      // audience
926       nullptr,                      // scope
927       nullptr,                      // requested_token_type
928       test_signed_jwt_path_prefix,  // subject_token_path
929       test_signed_jwt_token_type,   // subject_token_type
930       nullptr,                      // actor_token_path
931       nullptr                       // actor_token_type
932   };
933   absl::StatusOr<URI> sts_url = ValidateStsCredentialsOptions(&valid_options);
934   CHECK_OK(sts_url);
935   absl::string_view host;
936   absl::string_view port;
937   CHECK(SplitHostPort(sts_url->authority(), &host, &port));
938   CHECK(host == "foo.com");
939   CHECK(port == "5555");
940 }
941 
TEST_F(CredentialsTest,TestInvalidStsCredsOptions)942 TEST_F(CredentialsTest, TestInvalidStsCredsOptions) {
943   grpc_sts_credentials_options invalid_options = {
944       test_sts_endpoint_url,       // sts_endpoint_url
945       nullptr,                     // resource
946       nullptr,                     // audience
947       nullptr,                     // scope
948       nullptr,                     // requested_token_type
949       nullptr,                     // subject_token_path (Required)
950       test_signed_jwt_token_type,  // subject_token_type
951       nullptr,                     // actor_token_path
952       nullptr                      // actor_token_type
953   };
954   absl::StatusOr<URI> url_should_be_invalid =
955       ValidateStsCredentialsOptions(&invalid_options);
956   CHECK(!url_should_be_invalid.ok());
957 
958   invalid_options = {
959       test_sts_endpoint_url,        // sts_endpoint_url
960       nullptr,                      // resource
961       nullptr,                      // audience
962       nullptr,                      // scope
963       nullptr,                      // requested_token_type
964       test_signed_jwt_path_prefix,  // subject_token_path
965       nullptr,                      // subject_token_type (Required)
966       nullptr,                      // actor_token_path
967       nullptr                       // actor_token_type
968   };
969   url_should_be_invalid = ValidateStsCredentialsOptions(&invalid_options);
970   CHECK(!url_should_be_invalid.ok());
971 
972   invalid_options = {
973       nullptr,                      // sts_endpoint_url (Required)
974       nullptr,                      // resource
975       nullptr,                      // audience
976       nullptr,                      // scope
977       nullptr,                      // requested_token_type
978       test_signed_jwt_path_prefix,  // subject_token_path
979       test_signed_jwt_token_type,   // subject_token_type (Required)
980       nullptr,                      // actor_token_path
981       nullptr                       // actor_token_type
982   };
983   url_should_be_invalid = ValidateStsCredentialsOptions(&invalid_options);
984   CHECK(!url_should_be_invalid.ok());
985 
986   invalid_options = {
987       "not_a_valid_uri",            // sts_endpoint_url
988       nullptr,                      // resource
989       nullptr,                      // audience
990       nullptr,                      // scope
991       nullptr,                      // requested_token_type
992       test_signed_jwt_path_prefix,  // subject_token_path
993       test_signed_jwt_token_type,   // subject_token_type (Required)
994       nullptr,                      // actor_token_path
995       nullptr                       // actor_token_type
996   };
997   url_should_be_invalid = ValidateStsCredentialsOptions(&invalid_options);
998   CHECK(!url_should_be_invalid.ok());
999 
1000   invalid_options = {
1001       "ftp://ftp.is.not.a.valid.scheme/bar",  // sts_endpoint_url
1002       nullptr,                                // resource
1003       nullptr,                                // audience
1004       nullptr,                                // scope
1005       nullptr,                                // requested_token_type
1006       test_signed_jwt_path_prefix,            // subject_token_path
1007       test_signed_jwt_token_type,             // subject_token_type (Required)
1008       nullptr,                                // actor_token_path
1009       nullptr                                 // actor_token_type
1010   };
1011   url_should_be_invalid = ValidateStsCredentialsOptions(&invalid_options);
1012   CHECK(!url_should_be_invalid.ok());
1013 }
1014 
assert_query_parameters(const URI & uri,absl::string_view expected_key,absl::string_view expected_val)1015 void assert_query_parameters(const URI& uri, absl::string_view expected_key,
1016                              absl::string_view expected_val) {
1017   const auto it = uri.query_parameter_map().find(expected_key);
1018   CHECK(it != uri.query_parameter_map().end());
1019   if (it->second != expected_val) {
1020     LOG(ERROR) << it->second << "!=" << expected_val;
1021   }
1022   CHECK(it->second == expected_val);
1023 }
1024 
validate_sts_token_http_request(const grpc_http_request * request,const URI & uri,absl::string_view body,bool expect_actor_token)1025 void validate_sts_token_http_request(const grpc_http_request* request,
1026                                      const URI& uri, absl::string_view body,
1027                                      bool expect_actor_token) {
1028   // Check that the body is constructed properly.
1029   std::string get_url_equivalent =
1030       absl::StrFormat("%s?%s", test_sts_endpoint_url, body);
1031   absl::StatusOr<URI> url = URI::Parse(get_url_equivalent);
1032   if (!url.ok()) {
1033     LOG(ERROR) << url.status();
1034     CHECK_OK(url);
1035   }
1036   assert_query_parameters(*url, "resource", "resource");
1037   assert_query_parameters(*url, "audience", "audience");
1038   assert_query_parameters(*url, "scope", "scope");
1039   assert_query_parameters(*url, "requested_token_type", "requested_token_type");
1040   assert_query_parameters(*url, "subject_token", test_signed_jwt);
1041   assert_query_parameters(*url, "subject_token_type",
1042                           test_signed_jwt_token_type);
1043   if (expect_actor_token) {
1044     assert_query_parameters(*url, "actor_token", test_signed_jwt2);
1045     assert_query_parameters(*url, "actor_token_type",
1046                             test_signed_jwt_token_type2);
1047   } else {
1048     CHECK(url->query_parameter_map().find("actor_token") ==
1049           url->query_parameter_map().end());
1050     CHECK(url->query_parameter_map().find("actor_token_type") ==
1051           url->query_parameter_map().end());
1052   }
1053 
1054   // Check the rest of the request.
1055   EXPECT_EQ(uri.authority(), "foo.com:5555");
1056   EXPECT_EQ(uri.path(), "/v1/token-exchange");
1057   ASSERT_EQ(request->hdr_count, 1);
1058   EXPECT_EQ(absl::string_view(request->hdrs[0].key), "Content-Type");
1059   EXPECT_EQ(absl::string_view(request->hdrs[0].value),
1060             "application/x-www-form-urlencoded");
1061 }
1062 
sts_token_httpcli_post_success(const grpc_http_request * request,const URI & uri,absl::string_view body,Timestamp,grpc_closure * on_done,grpc_http_response * response)1063 int sts_token_httpcli_post_success(const grpc_http_request* request,
1064                                    const URI& uri, absl::string_view body,
1065                                    Timestamp /*deadline*/,
1066                                    grpc_closure* on_done,
1067                                    grpc_http_response* response) {
1068   validate_sts_token_http_request(request, uri, body, true);
1069   *response = http_response(200, valid_sts_json_response);
1070   ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus());
1071   return 1;
1072 }
1073 
sts_token_httpcli_post_success_no_actor_token(const grpc_http_request * request,const URI & uri,absl::string_view body,Timestamp,grpc_closure * on_done,grpc_http_response * response)1074 int sts_token_httpcli_post_success_no_actor_token(
1075     const grpc_http_request* request, const URI& uri, absl::string_view body,
1076     Timestamp /*deadline*/, grpc_closure* on_done,
1077     grpc_http_response* response) {
1078   validate_sts_token_http_request(request, uri, body, false);
1079   *response = http_response(200, valid_sts_json_response);
1080   ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus());
1081   return 1;
1082 }
1083 
write_tmp_jwt_file(const char * jwt_contents)1084 char* write_tmp_jwt_file(const char* jwt_contents) {
1085   char* path;
1086   FILE* tmp = gpr_tmpfile(test_signed_jwt_path_prefix, &path);
1087   CHECK_NE(path, nullptr);
1088   CHECK_NE(tmp, nullptr);
1089   size_t jwt_length = strlen(jwt_contents);
1090   CHECK_EQ(fwrite(jwt_contents, 1, jwt_length, tmp), jwt_length);
1091   fclose(tmp);
1092   return path;
1093 }
1094 
TEST_F(CredentialsTest,TestStsCredsSuccess)1095 TEST_F(CredentialsTest, TestStsCredsSuccess) {
1096   ExecCtx exec_ctx;
1097   std::string emd = "authorization: Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_";
1098   const char expected_creds_debug_string[] =
1099       "StsTokenFetcherCredentials{Path:/v1/"
1100       "token-exchange,Authority:foo.com:5555,OAuth2TokenFetcherCredentials}";
1101   char* subject_token_path = write_tmp_jwt_file(test_signed_jwt);
1102   char* actor_token_path = write_tmp_jwt_file(test_signed_jwt2);
1103   grpc_sts_credentials_options valid_options = {
1104       test_sts_endpoint_url,       // sts_endpoint_url
1105       "resource",                  // resource
1106       "audience",                  // audience
1107       "scope",                     // scope
1108       "requested_token_type",      // requested_token_type
1109       subject_token_path,          // subject_token_path
1110       test_signed_jwt_token_type,  // subject_token_type
1111       actor_token_path,            // actor_token_path
1112       test_signed_jwt_token_type2  // actor_token_type
1113   };
1114   grpc_call_credentials* creds =
1115       grpc_sts_credentials_create(&valid_options, nullptr);
1116 
1117   // Check security level.
1118   CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
1119 
1120   // First request: http put should be called.
1121   auto state = RequestMetadataState::NewInstance(absl::OkStatus(), emd);
1122   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
1123                            sts_token_httpcli_post_success,
1124                            httpcli_put_should_not_be_called);
1125   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
1126                                 kTestPath);
1127   ExecCtx::Get()->Flush();
1128 
1129   // Second request: the cached token should be served directly.
1130   state = RequestMetadataState::NewInstance(absl::OkStatus(), emd);
1131   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
1132                            httpcli_post_should_not_be_called,
1133                            httpcli_put_should_not_be_called);
1134   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
1135                                 kTestPath);
1136   ExecCtx::Get()->Flush();
1137   CHECK_EQ(strcmp(creds->debug_string().c_str(), expected_creds_debug_string),
1138            0);
1139 
1140   creds->Unref();
1141   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
1142   gpr_free(subject_token_path);
1143   gpr_free(actor_token_path);
1144 }
1145 
TEST_F(CredentialsTest,TestStsCredsTokenFileNotFound)1146 TEST_F(CredentialsTest, TestStsCredsTokenFileNotFound) {
1147   ExecCtx exec_ctx;
1148   grpc_sts_credentials_options valid_options = {
1149       test_sts_endpoint_url,           // sts_endpoint_url
1150       "resource",                      // resource
1151       "audience",                      // audience
1152       "scope",                         // scope
1153       "requested_token_type",          // requested_token_type
1154       "/some/completely/random/path",  // subject_token_path
1155       test_signed_jwt_token_type,      // subject_token_type
1156       "",                              // actor_token_path
1157       ""                               // actor_token_type
1158   };
1159   grpc_call_credentials* creds =
1160       grpc_sts_credentials_create(&valid_options, nullptr);
1161 
1162   // Check security level.
1163   CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
1164 
1165   auto state = RequestMetadataState::NewInstance(
1166       // TODO(roth): This should return UNAVAILABLE.
1167       absl::InternalError(
1168           "Failed to load file: /some/completely/random/path due to "
1169           "error(fdopen): No such file or directory"),
1170       {});
1171   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
1172                            httpcli_post_should_not_be_called,
1173                            httpcli_put_should_not_be_called);
1174   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
1175                                 kTestPath);
1176   ExecCtx::Get()->Flush();
1177 
1178   // Cleanup.
1179   creds->Unref();
1180   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
1181 }
1182 
TEST_F(CredentialsTest,TestStsCredsNoActorTokenSuccess)1183 TEST_F(CredentialsTest, TestStsCredsNoActorTokenSuccess) {
1184   ExecCtx exec_ctx;
1185   std::string emd = "authorization: Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_";
1186   const char expected_creds_debug_string[] =
1187       "StsTokenFetcherCredentials{Path:/v1/"
1188       "token-exchange,Authority:foo.com:5555,OAuth2TokenFetcherCredentials}";
1189   char* subject_token_path = write_tmp_jwt_file(test_signed_jwt);
1190   grpc_sts_credentials_options valid_options = {
1191       test_sts_endpoint_url,       // sts_endpoint_url
1192       "resource",                  // resource
1193       "audience",                  // audience
1194       "scope",                     // scope
1195       "requested_token_type",      // requested_token_type
1196       subject_token_path,          // subject_token_path
1197       test_signed_jwt_token_type,  // subject_token_type
1198       "",                          // actor_token_path
1199       ""                           // actor_token_type
1200   };
1201   grpc_call_credentials* creds =
1202       grpc_sts_credentials_create(&valid_options, nullptr);
1203 
1204   // Check security level.
1205   CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
1206 
1207   // First request: http put should be called.
1208   auto state = RequestMetadataState::NewInstance(absl::OkStatus(), emd);
1209   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
1210                            sts_token_httpcli_post_success_no_actor_token,
1211                            httpcli_put_should_not_be_called);
1212   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
1213                                 kTestPath);
1214   ExecCtx::Get()->Flush();
1215 
1216   // Second request: the cached token should be served directly.
1217   state = RequestMetadataState::NewInstance(absl::OkStatus(), emd);
1218   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
1219                            httpcli_post_should_not_be_called,
1220                            httpcli_put_should_not_be_called);
1221   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
1222                                 kTestPath);
1223   ExecCtx::Get()->Flush();
1224   CHECK_EQ(strcmp(creds->debug_string().c_str(), expected_creds_debug_string),
1225            0);
1226 
1227   creds->Unref();
1228   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
1229   gpr_free(subject_token_path);
1230 }
1231 
TEST_F(CredentialsTest,TestStsCredsLoadTokenFailure)1232 TEST_F(CredentialsTest, TestStsCredsLoadTokenFailure) {
1233   const char expected_creds_debug_string[] =
1234       "StsTokenFetcherCredentials{Path:/v1/"
1235       "token-exchange,Authority:foo.com:5555,OAuth2TokenFetcherCredentials}";
1236   ExecCtx exec_ctx;
1237   auto state = RequestMetadataState::NewInstance(
1238       // TODO(roth): This should return UNAVAILABLE.
1239       absl::InternalError("Failed to load file: invalid_path due to "
1240                           "error(fdopen): No such file or directory"),
1241       {});
1242   char* test_signed_jwt_path = write_tmp_jwt_file(test_signed_jwt);
1243   grpc_sts_credentials_options options = {
1244       test_sts_endpoint_url,       // sts_endpoint_url
1245       "resource",                  // resource
1246       "audience",                  // audience
1247       "scope",                     // scope
1248       "requested_token_type",      // requested_token_type
1249       "invalid_path",              // subject_token_path
1250       test_signed_jwt_token_type,  // subject_token_type
1251       nullptr,                     // actor_token_path
1252       nullptr                      // actor_token_type
1253   };
1254   grpc_call_credentials* creds = grpc_sts_credentials_create(&options, nullptr);
1255   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
1256                            httpcli_post_should_not_be_called,
1257                            httpcli_put_should_not_be_called);
1258   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
1259                                 kTestPath);
1260   CHECK_EQ(strcmp(creds->debug_string().c_str(), expected_creds_debug_string),
1261            0);
1262 
1263   creds->Unref();
1264   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
1265   gpr_free(test_signed_jwt_path);
1266 }
1267 
TEST_F(CredentialsTest,TestStsCredsHttpFailure)1268 TEST_F(CredentialsTest, TestStsCredsHttpFailure) {
1269   const char expected_creds_debug_string[] =
1270       "StsTokenFetcherCredentials{Path:/v1/"
1271       "token-exchange,Authority:foo.com:5555,OAuth2TokenFetcherCredentials}";
1272   ExecCtx exec_ctx;
1273   auto state = RequestMetadataState::NewInstance(
1274       // TODO(roth): This should return UNAUTHENTICATED.
1275       absl::UnavailableError("error parsing oauth2 token"), {});
1276   char* test_signed_jwt_path = write_tmp_jwt_file(test_signed_jwt);
1277   grpc_sts_credentials_options valid_options = {
1278       test_sts_endpoint_url,       // sts_endpoint_url
1279       "resource",                  // resource
1280       "audience",                  // audience
1281       "scope",                     // scope
1282       "requested_token_type",      // requested_token_type
1283       test_signed_jwt_path,        // subject_token_path
1284       test_signed_jwt_token_type,  // subject_token_type
1285       nullptr,                     // actor_token_path
1286       nullptr                      // actor_token_type
1287   };
1288   grpc_call_credentials* creds =
1289       grpc_sts_credentials_create(&valid_options, nullptr);
1290   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
1291                            token_httpcli_post_failure,
1292                            httpcli_put_should_not_be_called);
1293   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
1294                                 kTestPath);
1295   CHECK_EQ(strcmp(creds->debug_string().c_str(), expected_creds_debug_string),
1296            0);
1297   creds->Unref();
1298   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
1299   gpr_free(test_signed_jwt_path);
1300 }
1301 
validate_jwt_encode_and_sign_params(const grpc_auth_json_key * json_key,const char * scope,gpr_timespec token_lifetime)1302 void validate_jwt_encode_and_sign_params(const grpc_auth_json_key* json_key,
1303                                          const char* scope,
1304                                          gpr_timespec token_lifetime) {
1305   CHECK(grpc_auth_json_key_is_valid(json_key));
1306   CHECK_NE(json_key->private_key, nullptr);
1307 #if OPENSSL_VERSION_NUMBER < 0x30000000L
1308   CHECK(RSA_check_key(json_key->private_key));
1309 #else
1310   EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(json_key->private_key, NULL);
1311   CHECK(EVP_PKEY_private_check(ctx));
1312   EVP_PKEY_CTX_free(ctx);
1313 #endif
1314   CHECK(json_key->type != nullptr &&
1315         strcmp(json_key->type, "service_account") == 0);
1316   CHECK(json_key->private_key_id != nullptr &&
1317         strcmp(json_key->private_key_id,
1318                "e6b5137873db8d2ef81e06a47289e6434ec8a165") == 0);
1319   CHECK(json_key->client_id != nullptr &&
1320         strcmp(json_key->client_id,
1321                "777-abaslkan11hlb6nmim3bpspl31ud.apps."
1322                "googleusercontent.com") == 0);
1323   CHECK(json_key->client_email != nullptr &&
1324         strcmp(json_key->client_email,
1325                "777-abaslkan11hlb6nmim3bpspl31ud@developer."
1326                "gserviceaccount.com") == 0);
1327   if (scope != nullptr) CHECK_EQ(strcmp(scope, test_scope), 0);
1328   CHECK_EQ(gpr_time_cmp(token_lifetime, grpc_max_auth_token_lifetime()), 0);
1329 }
1330 
encode_and_sign_jwt_success(const grpc_auth_json_key * json_key,const char * audience,gpr_timespec token_lifetime,const char * scope)1331 char* encode_and_sign_jwt_success(const grpc_auth_json_key* json_key,
1332                                   const char* audience,
1333                                   gpr_timespec token_lifetime,
1334                                   const char* scope) {
1335   if (strcmp(audience, test_service_url_no_service_name) != 0 &&
1336       strcmp(audience, other_test_service_url_no_service_name) != 0) {
1337     return nullptr;
1338   }
1339   validate_jwt_encode_and_sign_params(json_key, scope, token_lifetime);
1340   return gpr_strdup(test_signed_jwt);
1341 }
1342 
encode_and_sign_jwt_failure(const grpc_auth_json_key * json_key,const char *,gpr_timespec token_lifetime,const char * scope)1343 char* encode_and_sign_jwt_failure(const grpc_auth_json_key* json_key,
1344                                   const char* /*audience*/,
1345                                   gpr_timespec token_lifetime,
1346                                   const char* scope) {
1347   validate_jwt_encode_and_sign_params(json_key, scope, token_lifetime);
1348   return nullptr;
1349 }
1350 
encode_and_sign_jwt_should_not_be_called(const grpc_auth_json_key *,const char *,gpr_timespec,const char *)1351 char* encode_and_sign_jwt_should_not_be_called(
1352     const grpc_auth_json_key* /*json_key*/, const char* /*audience*/,
1353     gpr_timespec /*token_lifetime*/, const char* /*scope*/) {
1354   CHECK_EQ("grpc_jwt_encode_and_sign should not be called", nullptr);
1355   return nullptr;
1356 }
1357 
creds_as_jwt(grpc_call_credentials * creds)1358 grpc_service_account_jwt_access_credentials* creds_as_jwt(
1359     grpc_call_credentials* creds) {
1360   CHECK(creds != nullptr);
1361   CHECK(creds->type() == grpc_service_account_jwt_access_credentials::Type());
1362   return reinterpret_cast<grpc_service_account_jwt_access_credentials*>(creds);
1363 }
1364 
TEST_F(CredentialsTest,TestJwtCredsLifetime)1365 TEST_F(CredentialsTest, TestJwtCredsLifetime) {
1366   char* json_key_string = test_json_key_str();
1367   const char expected_creds_debug_string_prefix[] =
1368       "JWTAccessCredentials{ExpirationTime:";
1369   // Max lifetime.
1370   grpc_call_credentials* jwt_creds =
1371       grpc_service_account_jwt_access_credentials_create(
1372           json_key_string, grpc_max_auth_token_lifetime(), nullptr);
1373   CHECK_EQ(gpr_time_cmp(creds_as_jwt(jwt_creds)->jwt_lifetime(),
1374                         grpc_max_auth_token_lifetime()),
1375            0);
1376   // Check security level.
1377   CHECK_EQ(jwt_creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
1378   CHECK_EQ(strncmp(expected_creds_debug_string_prefix,
1379                    jwt_creds->debug_string().c_str(),
1380                    strlen(expected_creds_debug_string_prefix)),
1381            0);
1382   grpc_call_credentials_release(jwt_creds);
1383 
1384   // Shorter lifetime.
1385   gpr_timespec token_lifetime = {10, 0, GPR_TIMESPAN};
1386   CHECK_GT(gpr_time_cmp(grpc_max_auth_token_lifetime(), token_lifetime), 0);
1387   jwt_creds = grpc_service_account_jwt_access_credentials_create(
1388       json_key_string, token_lifetime, nullptr);
1389   CHECK_EQ(
1390       gpr_time_cmp(creds_as_jwt(jwt_creds)->jwt_lifetime(), token_lifetime), 0);
1391   CHECK_EQ(strncmp(expected_creds_debug_string_prefix,
1392                    jwt_creds->debug_string().c_str(),
1393                    strlen(expected_creds_debug_string_prefix)),
1394            0);
1395   grpc_call_credentials_release(jwt_creds);
1396 
1397   // Cropped lifetime.
1398   gpr_timespec add_to_max = {10, 0, GPR_TIMESPAN};
1399   token_lifetime = gpr_time_add(grpc_max_auth_token_lifetime(), add_to_max);
1400   jwt_creds = grpc_service_account_jwt_access_credentials_create(
1401       json_key_string, token_lifetime, nullptr);
1402   CHECK(gpr_time_cmp(creds_as_jwt(jwt_creds)->jwt_lifetime(),
1403                      grpc_max_auth_token_lifetime()) == 0);
1404   CHECK(strncmp(expected_creds_debug_string_prefix,
1405                 jwt_creds->debug_string().c_str(),
1406                 strlen(expected_creds_debug_string_prefix)) == 0);
1407   grpc_call_credentials_release(jwt_creds);
1408 
1409   gpr_free(json_key_string);
1410 }
1411 
TEST_F(CredentialsTest,TestRemoveServiceFromJwtUri)1412 TEST_F(CredentialsTest, TestRemoveServiceFromJwtUri) {
1413   const char wrong_uri[] = "hello world";
1414   CHECK(!RemoveServiceNameFromJwtUri(wrong_uri).ok());
1415   const char valid_uri[] = "https://foo.com/get/";
1416   const char expected_uri[] = "https://foo.com/";
1417   auto output = RemoveServiceNameFromJwtUri(valid_uri);
1418   CHECK_OK(output);
1419   CHECK_EQ(strcmp(output->c_str(), expected_uri), 0);
1420 }
1421 
TEST_F(CredentialsTest,TestJwtCredsSuccess)1422 TEST_F(CredentialsTest, TestJwtCredsSuccess) {
1423   const char expected_creds_debug_string_prefix[] =
1424       "JWTAccessCredentials{ExpirationTime:";
1425 
1426   char* json_key_string = test_json_key_str();
1427   ExecCtx exec_ctx;
1428   std::string expected_md_value = absl::StrCat("Bearer ", test_signed_jwt);
1429   std::string emd = absl::StrCat("authorization: ", expected_md_value);
1430   grpc_call_credentials* creds =
1431       grpc_service_account_jwt_access_credentials_create(
1432           json_key_string, grpc_max_auth_token_lifetime(), nullptr);
1433 
1434   // First request: jwt_encode_and_sign should be called.
1435   auto state = RequestMetadataState::NewInstance(absl::OkStatus(), emd);
1436   grpc_jwt_encode_and_sign_set_override(encode_and_sign_jwt_success);
1437   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
1438                                 kTestPath);
1439   ExecCtx::Get()->Flush();
1440 
1441   // Second request: the cached token should be served directly.
1442   state = RequestMetadataState::NewInstance(absl::OkStatus(), emd);
1443   grpc_jwt_encode_and_sign_set_override(
1444       encode_and_sign_jwt_should_not_be_called);
1445   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
1446                                 kTestPath);
1447   ExecCtx::Get()->Flush();
1448 
1449   // Third request: Different service url so jwt_encode_and_sign should be
1450   // called again (no caching).
1451   state = RequestMetadataState::NewInstance(absl::OkStatus(), emd);
1452   grpc_jwt_encode_and_sign_set_override(encode_and_sign_jwt_success);
1453   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestOtherAuthority,
1454                                 kTestOtherPath);
1455   ExecCtx::Get()->Flush();
1456   CHECK_EQ(
1457       strncmp(expected_creds_debug_string_prefix, creds->debug_string().c_str(),
1458               strlen(expected_creds_debug_string_prefix)),
1459       0);
1460 
1461   creds->Unref();
1462   gpr_free(json_key_string);
1463   grpc_jwt_encode_and_sign_set_override(nullptr);
1464 }
1465 
TEST_F(CredentialsTest,TestJwtCredsSigningFailure)1466 TEST_F(CredentialsTest, TestJwtCredsSigningFailure) {
1467   const char expected_creds_debug_string_prefix[] =
1468       "JWTAccessCredentials{ExpirationTime:";
1469   char* json_key_string = test_json_key_str();
1470   ExecCtx exec_ctx;
1471   auto state = RequestMetadataState::NewInstance(
1472       absl::UnauthenticatedError("Could not generate JWT."), {});
1473   grpc_call_credentials* creds =
1474       grpc_service_account_jwt_access_credentials_create(
1475           json_key_string, grpc_max_auth_token_lifetime(), nullptr);
1476 
1477   grpc_jwt_encode_and_sign_set_override(encode_and_sign_jwt_failure);
1478   state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
1479                                 kTestPath);
1480 
1481   gpr_free(json_key_string);
1482   CHECK_EQ(
1483       strncmp(expected_creds_debug_string_prefix, creds->debug_string().c_str(),
1484               strlen(expected_creds_debug_string_prefix)),
1485       0);
1486 
1487   creds->Unref();
1488   grpc_jwt_encode_and_sign_set_override(nullptr);
1489 }
1490 
set_google_default_creds_env_var_with_file_contents(const char * file_prefix,const char * contents)1491 void set_google_default_creds_env_var_with_file_contents(
1492     const char* file_prefix, const char* contents) {
1493   size_t contents_len = strlen(contents);
1494   char* creds_file_name;
1495   FILE* creds_file = gpr_tmpfile(file_prefix, &creds_file_name);
1496   CHECK_NE(creds_file_name, nullptr);
1497   CHECK_NE(creds_file, nullptr);
1498   CHECK_EQ(fwrite(contents, 1, contents_len, creds_file), contents_len);
1499   fclose(creds_file);
1500   SetEnv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, creds_file_name);
1501   gpr_free(creds_file_name);
1502 }
1503 
test_gce_tenancy_checker(void)1504 bool test_gce_tenancy_checker(void) {
1505   g_test_gce_tenancy_checker_called = true;
1506   return g_test_is_on_gce;
1507 }
1508 
null_well_known_creds_path_getter(void)1509 std::string null_well_known_creds_path_getter(void) { return ""; }
1510 
TEST_F(CredentialsTest,TestGoogleDefaultCredsAuthKey)1511 TEST_F(CredentialsTest, TestGoogleDefaultCredsAuthKey) {
1512   ExecCtx exec_ctx;
1513   grpc_composite_channel_credentials* creds;
1514   char* json_key = test_json_key_str();
1515   grpc_flush_cached_google_default_credentials();
1516   set_gce_tenancy_checker_for_testing(test_gce_tenancy_checker);
1517   g_test_gce_tenancy_checker_called = false;
1518   g_test_is_on_gce = true;
1519   set_google_default_creds_env_var_with_file_contents(
1520       "json_key_google_default_creds", json_key);
1521   grpc_override_well_known_credentials_path_getter(
1522       null_well_known_creds_path_getter);
1523   gpr_free(json_key);
1524   creds = reinterpret_cast<grpc_composite_channel_credentials*>(
1525       grpc_google_default_credentials_create(nullptr));
1526   auto* default_creds =
1527       reinterpret_cast<const grpc_google_default_channel_credentials*>(
1528           creds->inner_creds());
1529   CHECK_NE(default_creds->ssl_creds(), nullptr);
1530   auto* jwt =
1531       reinterpret_cast<const grpc_service_account_jwt_access_credentials*>(
1532           creds->call_creds());
1533   CHECK_EQ(
1534       strcmp(jwt->key().client_id,
1535              "777-abaslkan11hlb6nmim3bpspl31ud.apps.googleusercontent.com"),
1536       0);
1537   CHECK_EQ(g_test_gce_tenancy_checker_called, false);
1538   creds->Unref();
1539   SetEnv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, "");  // Reset.
1540   grpc_override_well_known_credentials_path_getter(nullptr);
1541 }
1542 
TEST_F(CredentialsTest,TestGoogleDefaultCredsRefreshToken)1543 TEST_F(CredentialsTest, TestGoogleDefaultCredsRefreshToken) {
1544   ExecCtx exec_ctx;
1545   grpc_composite_channel_credentials* creds;
1546   grpc_flush_cached_google_default_credentials();
1547   set_google_default_creds_env_var_with_file_contents(
1548       "refresh_token_google_default_creds", test_refresh_token_str);
1549   grpc_override_well_known_credentials_path_getter(
1550       null_well_known_creds_path_getter);
1551   creds = reinterpret_cast<grpc_composite_channel_credentials*>(
1552       grpc_google_default_credentials_create(nullptr));
1553   auto* default_creds =
1554       reinterpret_cast<const grpc_google_default_channel_credentials*>(
1555           creds->inner_creds());
1556   CHECK_NE(default_creds->ssl_creds(), nullptr);
1557   auto* refresh =
1558       reinterpret_cast<const grpc_google_refresh_token_credentials*>(
1559           creds->call_creds());
1560   CHECK_EQ(strcmp(refresh->refresh_token().client_id,
1561                   "32555999999.apps.googleusercontent.com"),
1562            0);
1563   creds->Unref();
1564   SetEnv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, "");  // Reset.
1565   grpc_override_well_known_credentials_path_getter(nullptr);
1566 }
1567 
TEST_F(CredentialsTest,TestGoogleDefaultCredsExternalAccountCredentialsPscSts)1568 TEST_F(CredentialsTest,
1569        TestGoogleDefaultCredsExternalAccountCredentialsPscSts) {
1570   ExecCtx exec_ctx;
1571   grpc_composite_channel_credentials* creds;
1572   grpc_flush_cached_google_default_credentials();
1573   set_google_default_creds_env_var_with_file_contents(
1574       "google_default_creds_external_account_credentials_psc_sts",
1575       test_external_account_credentials_psc_sts_str);
1576   grpc_override_well_known_credentials_path_getter(
1577       null_well_known_creds_path_getter);
1578   creds = reinterpret_cast<grpc_composite_channel_credentials*>(
1579       grpc_google_default_credentials_create(nullptr));
1580   auto* default_creds =
1581       reinterpret_cast<const grpc_google_default_channel_credentials*>(
1582           creds->inner_creds());
1583   CHECK_NE(default_creds->ssl_creds(), nullptr);
1584   auto* external =
1585       reinterpret_cast<const ExternalAccountCredentials*>(creds->call_creds());
1586   CHECK_NE(external, nullptr);
1587   creds->Unref();
1588   SetEnv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, "");  // Reset.
1589   grpc_override_well_known_credentials_path_getter(nullptr);
1590 }
1591 
TEST_F(CredentialsTest,TestGoogleDefaultCredsExternalAccountCredentialsPscIam)1592 TEST_F(CredentialsTest,
1593        TestGoogleDefaultCredsExternalAccountCredentialsPscIam) {
1594   ExecCtx exec_ctx;
1595   grpc_composite_channel_credentials* creds;
1596   grpc_flush_cached_google_default_credentials();
1597   set_google_default_creds_env_var_with_file_contents(
1598       "google_default_creds_external_account_credentials_psc_iam",
1599       test_external_account_credentials_psc_iam_str);
1600   grpc_override_well_known_credentials_path_getter(
1601       null_well_known_creds_path_getter);
1602   creds = reinterpret_cast<grpc_composite_channel_credentials*>(
1603       grpc_google_default_credentials_create(nullptr));
1604   auto* default_creds =
1605       reinterpret_cast<const grpc_google_default_channel_credentials*>(
1606           creds->inner_creds());
1607   CHECK_NE(default_creds->ssl_creds(), nullptr);
1608   auto* external =
1609       reinterpret_cast<const ExternalAccountCredentials*>(creds->call_creds());
1610   CHECK_NE(external, nullptr);
1611   creds->Unref();
1612   SetEnv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, "");  // Reset.
1613   grpc_override_well_known_credentials_path_getter(nullptr);
1614 }
1615 
default_creds_metadata_server_detection_httpcli_get_success_override(const grpc_http_request *,const URI & uri,Timestamp,grpc_closure * on_done,grpc_http_response * response)1616 int default_creds_metadata_server_detection_httpcli_get_success_override(
1617     const grpc_http_request* /*request*/, const URI& uri,
1618     Timestamp /*deadline*/, grpc_closure* on_done,
1619     grpc_http_response* response) {
1620   *response = http_response(200, "");
1621   grpc_http_header* headers =
1622       static_cast<grpc_http_header*>(gpr_malloc(sizeof(*headers) * 1));
1623   headers[0].key = gpr_strdup("Metadata-Flavor");
1624   headers[0].value = gpr_strdup("Google");
1625   response->hdr_count = 1;
1626   response->hdrs = headers;
1627   EXPECT_EQ(uri.path(), "/");
1628   EXPECT_EQ(uri.authority(), "metadata.google.internal.");
1629   ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus());
1630   return 1;
1631 }
1632 
TEST_F(CredentialsTest,TestGoogleDefaultCredsGce)1633 TEST_F(CredentialsTest, TestGoogleDefaultCredsGce) {
1634   ExecCtx exec_ctx;
1635   auto state = RequestMetadataState::NewInstance(
1636       absl::OkStatus(),
1637       "authorization: Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_");
1638   grpc_flush_cached_google_default_credentials();
1639   SetEnv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, "");  // Reset.
1640   grpc_override_well_known_credentials_path_getter(
1641       null_well_known_creds_path_getter);
1642   set_gce_tenancy_checker_for_testing(test_gce_tenancy_checker);
1643   g_test_gce_tenancy_checker_called = false;
1644   g_test_is_on_gce = true;
1645 
1646   // Simulate a successful detection of GCE.
1647   grpc_composite_channel_credentials* creds =
1648       reinterpret_cast<grpc_composite_channel_credentials*>(
1649           grpc_google_default_credentials_create(nullptr));
1650 
1651   // Verify that the default creds actually embeds a GCE creds.
1652   CHECK(creds != nullptr);
1653   CHECK_NE(creds->call_creds(), nullptr);
1654   HttpRequest::SetOverride(compute_engine_httpcli_get_success_override,
1655                            httpcli_post_should_not_be_called,
1656                            httpcli_put_should_not_be_called);
1657   state->RunRequestMetadataTest(creds->mutable_call_creds(), kTestUrlScheme,
1658                                 kTestAuthority, kTestPath);
1659   ExecCtx::Get()->Flush();
1660 
1661   CHECK_EQ(g_test_gce_tenancy_checker_called, true);
1662 
1663   // Cleanup.
1664   creds->Unref();
1665   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
1666   grpc_override_well_known_credentials_path_getter(nullptr);
1667 }
1668 
TEST_F(CredentialsTest,TestGoogleDefaultCredsNonGce)1669 TEST_F(CredentialsTest, TestGoogleDefaultCredsNonGce) {
1670   ExecCtx exec_ctx;
1671   auto state = RequestMetadataState::NewInstance(
1672       absl::OkStatus(),
1673       "authorization: Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_");
1674   grpc_flush_cached_google_default_credentials();
1675   SetEnv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, "");  // Reset.
1676   grpc_override_well_known_credentials_path_getter(
1677       null_well_known_creds_path_getter);
1678   set_gce_tenancy_checker_for_testing(test_gce_tenancy_checker);
1679   g_test_gce_tenancy_checker_called = false;
1680   g_test_is_on_gce = false;
1681   // Simulate a successful detection of metadata server.
1682   HttpRequest::SetOverride(
1683       default_creds_metadata_server_detection_httpcli_get_success_override,
1684       httpcli_post_should_not_be_called, httpcli_put_should_not_be_called);
1685   grpc_composite_channel_credentials* creds =
1686       reinterpret_cast<grpc_composite_channel_credentials*>(
1687           grpc_google_default_credentials_create(nullptr));
1688   // Verify that the default creds actually embeds a GCE creds.
1689   CHECK(creds != nullptr);
1690   CHECK_NE(creds->call_creds(), nullptr);
1691   HttpRequest::SetOverride(compute_engine_httpcli_get_success_override,
1692                            httpcli_post_should_not_be_called,
1693                            httpcli_put_should_not_be_called);
1694   state->RunRequestMetadataTest(creds->mutable_call_creds(), kTestUrlScheme,
1695                                 kTestAuthority, kTestPath);
1696   ExecCtx::Get()->Flush();
1697   CHECK_EQ(g_test_gce_tenancy_checker_called, true);
1698   // Cleanup.
1699   creds->Unref();
1700   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
1701   grpc_override_well_known_credentials_path_getter(nullptr);
1702 }
1703 
default_creds_gce_detection_httpcli_get_failure_override(const grpc_http_request *,const URI & uri,Timestamp,grpc_closure * on_done,grpc_http_response * response)1704 int default_creds_gce_detection_httpcli_get_failure_override(
1705     const grpc_http_request* /*request*/, const URI& uri,
1706     Timestamp /*deadline*/, grpc_closure* on_done,
1707     grpc_http_response* response) {
1708   // No magic header.
1709   EXPECT_EQ(uri.path(), "/");
1710   EXPECT_EQ(uri.authority(), "metadata.google.internal.");
1711   *response = http_response(200, "");
1712   ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus());
1713   return 1;
1714 }
1715 
TEST_F(CredentialsTest,TestNoGoogleDefaultCreds)1716 TEST_F(CredentialsTest, TestNoGoogleDefaultCreds) {
1717   grpc_flush_cached_google_default_credentials();
1718   SetEnv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, "");  // Reset.
1719   grpc_override_well_known_credentials_path_getter(
1720       null_well_known_creds_path_getter);
1721   set_gce_tenancy_checker_for_testing(test_gce_tenancy_checker);
1722   g_test_gce_tenancy_checker_called = false;
1723   g_test_is_on_gce = false;
1724   HttpRequest::SetOverride(
1725       default_creds_gce_detection_httpcli_get_failure_override,
1726       httpcli_post_should_not_be_called, httpcli_put_should_not_be_called);
1727   // Simulate a successful detection of GCE.
1728   CHECK_EQ(grpc_google_default_credentials_create(nullptr), nullptr);
1729   // Try a second one. GCE detection should occur again.
1730   g_test_gce_tenancy_checker_called = false;
1731   CHECK_EQ(grpc_google_default_credentials_create(nullptr), nullptr);
1732   CHECK_EQ(g_test_gce_tenancy_checker_called, true);
1733   // Cleanup.
1734   grpc_override_well_known_credentials_path_getter(nullptr);
1735   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
1736 }
1737 
TEST_F(CredentialsTest,TestGoogleDefaultCredsCallCredsSpecified)1738 TEST_F(CredentialsTest, TestGoogleDefaultCredsCallCredsSpecified) {
1739   auto state = RequestMetadataState::NewInstance(
1740       absl::OkStatus(),
1741       "authorization: Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_");
1742   ExecCtx exec_ctx;
1743   grpc_flush_cached_google_default_credentials();
1744   grpc_call_credentials* call_creds =
1745       grpc_google_compute_engine_credentials_create(nullptr);
1746   set_gce_tenancy_checker_for_testing(test_gce_tenancy_checker);
1747   g_test_gce_tenancy_checker_called = false;
1748   g_test_is_on_gce = true;
1749   HttpRequest::SetOverride(
1750       default_creds_metadata_server_detection_httpcli_get_success_override,
1751       httpcli_post_should_not_be_called, httpcli_put_should_not_be_called);
1752   grpc_composite_channel_credentials* channel_creds =
1753       reinterpret_cast<grpc_composite_channel_credentials*>(
1754           grpc_google_default_credentials_create(call_creds));
1755   CHECK_EQ(g_test_gce_tenancy_checker_called, false);
1756   CHECK_NE(channel_creds, nullptr);
1757   CHECK_NE(channel_creds->call_creds(), nullptr);
1758   HttpRequest::SetOverride(compute_engine_httpcli_get_success_override,
1759                            httpcli_post_should_not_be_called,
1760                            httpcli_put_should_not_be_called);
1761   state->RunRequestMetadataTest(channel_creds->mutable_call_creds(),
1762                                 kTestUrlScheme, kTestAuthority, kTestPath);
1763   ExecCtx::Get()->Flush();
1764   channel_creds->Unref();
1765   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
1766 }
1767 
1768 struct fake_call_creds : public grpc_call_credentials {
1769  public:
Orphanedgrpc_core::__anon62eef70e0111::fake_call_creds1770   void Orphaned() override {}
1771 
GetRequestMetadatagrpc_core::__anon62eef70e0111::fake_call_creds1772   ArenaPromise<absl::StatusOr<ClientMetadataHandle>> GetRequestMetadata(
1773       ClientMetadataHandle initial_metadata,
1774       const grpc_call_credentials::GetRequestMetadataArgs*) override {
1775     initial_metadata->Append("foo", Slice::FromStaticString("oof"),
1776                              [](absl::string_view, const Slice&) { abort(); });
1777     return Immediate(std::move(initial_metadata));
1778   }
1779 
typegrpc_core::__anon62eef70e0111::fake_call_creds1780   UniqueTypeName type() const override {
1781     static UniqueTypeName::Factory kFactory("fake");
1782     return kFactory.Create();
1783   }
1784 
1785  private:
cmp_implgrpc_core::__anon62eef70e0111::fake_call_creds1786   int cmp_impl(const grpc_call_credentials* other) const override {
1787     // TODO(yashykt): Check if we can do something better here
1788     return QsortCompare(static_cast<const grpc_call_credentials*>(this), other);
1789   }
1790 };
1791 
TEST_F(CredentialsTest,TestGoogleDefaultCredsNotDefault)1792 TEST_F(CredentialsTest, TestGoogleDefaultCredsNotDefault) {
1793   auto state = RequestMetadataState::NewInstance(absl::OkStatus(), "foo: oof");
1794   ExecCtx exec_ctx;
1795   grpc_flush_cached_google_default_credentials();
1796   RefCountedPtr<grpc_call_credentials> call_creds =
1797       MakeRefCounted<fake_call_creds>();
1798   set_gce_tenancy_checker_for_testing(test_gce_tenancy_checker);
1799   g_test_gce_tenancy_checker_called = false;
1800   g_test_is_on_gce = true;
1801   HttpRequest::SetOverride(
1802       default_creds_metadata_server_detection_httpcli_get_success_override,
1803       httpcli_post_should_not_be_called, httpcli_put_should_not_be_called);
1804   grpc_composite_channel_credentials* channel_creds =
1805       reinterpret_cast<grpc_composite_channel_credentials*>(
1806           grpc_google_default_credentials_create(call_creds.release()));
1807   CHECK_EQ(g_test_gce_tenancy_checker_called, false);
1808   CHECK_NE(channel_creds, nullptr);
1809   CHECK_NE(channel_creds->call_creds(), nullptr);
1810   state->RunRequestMetadataTest(channel_creds->mutable_call_creds(),
1811                                 kTestUrlScheme, kTestAuthority, kTestPath);
1812   ExecCtx::Get()->Flush();
1813   channel_creds->Unref();
1814   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
1815 }
1816 
1817 typedef enum {
1818   PLUGIN_INITIAL_STATE,
1819   PLUGIN_GET_METADATA_CALLED_STATE,
1820   PLUGIN_DESTROY_CALLED_STATE
1821 } plugin_state;
1822 
1823 const std::map<std::string, std::string> plugin_md = {{"foo", "bar"},
1824                                                       {"hi", "there"}};
1825 
plugin_get_metadata_success(void * state,grpc_auth_metadata_context context,grpc_credentials_plugin_metadata_cb,void *,grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],size_t * num_creds_md,grpc_status_code *,const char **)1826 int plugin_get_metadata_success(
1827     void* state, grpc_auth_metadata_context context,
1828     grpc_credentials_plugin_metadata_cb /*cb*/, void* /*user_data*/,
1829     grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
1830     size_t* num_creds_md, grpc_status_code* /*status*/,
1831     const char** /*error_details*/) {
1832   CHECK_EQ(strcmp(context.service_url, test_service_url), 0);
1833   CHECK_EQ(strcmp(context.method_name, test_method), 0);
1834   CHECK_EQ(context.channel_auth_context, nullptr);
1835   CHECK_EQ(context.reserved, nullptr);
1836   CHECK_LT(plugin_md.size(), GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX);
1837   plugin_state* s = static_cast<plugin_state*>(state);
1838   *s = PLUGIN_GET_METADATA_CALLED_STATE;
1839   size_t i = 0;
1840   for (auto const& md : plugin_md) {
1841     memset(&creds_md[i], 0, sizeof(grpc_metadata));
1842     creds_md[i].key = grpc_slice_from_copied_string(md.first.c_str());
1843     creds_md[i].value = grpc_slice_from_copied_string(md.second.c_str());
1844     i += 1;
1845   }
1846   *num_creds_md = plugin_md.size();
1847   return true;  // Synchronous return.
1848 }
1849 
1850 const char* plugin_error_details = "Could not get metadata for plugin.";
1851 
plugin_get_metadata_failure(void * state,grpc_auth_metadata_context context,grpc_credentials_plugin_metadata_cb,void *,grpc_metadata[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],size_t *,grpc_status_code * status,const char ** error_details)1852 int plugin_get_metadata_failure(
1853     void* state, grpc_auth_metadata_context context,
1854     grpc_credentials_plugin_metadata_cb /*cb*/, void* /*user_data*/,
1855     grpc_metadata /*creds_md*/[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
1856     size_t* /*num_creds_md*/, grpc_status_code* status,
1857     const char** error_details) {
1858   CHECK_EQ(strcmp(context.service_url, test_service_url), 0);
1859   CHECK_EQ(strcmp(context.method_name, test_method), 0);
1860   CHECK_EQ(context.channel_auth_context, nullptr);
1861   CHECK_EQ(context.reserved, nullptr);
1862   plugin_state* s = static_cast<plugin_state*>(state);
1863   *s = PLUGIN_GET_METADATA_CALLED_STATE;
1864   *status = GRPC_STATUS_UNAUTHENTICATED;
1865   *error_details = gpr_strdup(plugin_error_details);
1866   return true;  // Synchronous return.
1867 }
1868 
plugin_destroy(void * state)1869 void plugin_destroy(void* state) {
1870   plugin_state* s = static_cast<plugin_state*>(state);
1871   *s = PLUGIN_DESTROY_CALLED_STATE;
1872 }
1873 
plugin_debug_string(void * state)1874 char* plugin_debug_string(void* state) {
1875   plugin_state* s = static_cast<plugin_state*>(state);
1876   char* ret = nullptr;
1877   switch (*s) {
1878     case PLUGIN_INITIAL_STATE:
1879       gpr_asprintf(&ret, "TestPluginCredentials{state:INITIAL}");
1880       break;
1881     case PLUGIN_GET_METADATA_CALLED_STATE:
1882       gpr_asprintf(&ret, "TestPluginCredentials{state:GET_METADATA_CALLED}");
1883       break;
1884     case PLUGIN_DESTROY_CALLED_STATE:
1885       gpr_asprintf(&ret, "TestPluginCredentials{state:DESTROY}");
1886       break;
1887     default:
1888       gpr_asprintf(&ret, "TestPluginCredentials{state:UNKNOWN}");
1889       break;
1890   }
1891   return ret;
1892 }
1893 
TEST_F(CredentialsTest,TestMetadataPluginSuccess)1894 TEST_F(CredentialsTest, TestMetadataPluginSuccess) {
1895   const char expected_creds_debug_string[] =
1896       "TestPluginCredentials{state:GET_METADATA_CALLED}";
1897   plugin_state state = PLUGIN_INITIAL_STATE;
1898   grpc_metadata_credentials_plugin plugin;
1899   ExecCtx exec_ctx;
1900   auto md_state = RequestMetadataState::NewInstance(absl::OkStatus(),
1901                                                     "foo: bar, hi: there");
1902 
1903   plugin.state = &state;
1904   plugin.get_metadata = plugin_get_metadata_success;
1905   plugin.destroy = plugin_destroy;
1906   plugin.debug_string = plugin_debug_string;
1907 
1908   grpc_call_credentials* creds = grpc_metadata_credentials_create_from_plugin(
1909       plugin, GRPC_PRIVACY_AND_INTEGRITY, nullptr);
1910   // Check security level.
1911   CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
1912   CHECK_EQ(state, PLUGIN_INITIAL_STATE);
1913   md_state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
1914                                    kTestPath);
1915   CHECK_EQ(state, PLUGIN_GET_METADATA_CALLED_STATE);
1916   CHECK_EQ(strcmp(creds->debug_string().c_str(), expected_creds_debug_string),
1917            0);
1918   creds->Unref();
1919 
1920   CHECK_EQ(state, PLUGIN_DESTROY_CALLED_STATE);
1921 }
1922 
TEST_F(CredentialsTest,TestMetadataPluginFailure)1923 TEST_F(CredentialsTest, TestMetadataPluginFailure) {
1924   const char expected_creds_debug_string[] =
1925       "TestPluginCredentials{state:GET_METADATA_CALLED}";
1926 
1927   plugin_state state = PLUGIN_INITIAL_STATE;
1928   grpc_metadata_credentials_plugin plugin;
1929   ExecCtx exec_ctx;
1930   auto md_state = RequestMetadataState::NewInstance(
1931       // TODO(roth): Is this the right status to use here?
1932       absl::UnavailableError(
1933           absl::StrCat("Getting metadata from plugin failed with error: ",
1934                        plugin_error_details)),
1935       {});
1936 
1937   plugin.state = &state;
1938   plugin.get_metadata = plugin_get_metadata_failure;
1939   plugin.destroy = plugin_destroy;
1940   plugin.debug_string = plugin_debug_string;
1941 
1942   grpc_call_credentials* creds = grpc_metadata_credentials_create_from_plugin(
1943       plugin, GRPC_PRIVACY_AND_INTEGRITY, nullptr);
1944   CHECK_EQ(state, PLUGIN_INITIAL_STATE);
1945   md_state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority,
1946                                    kTestPath);
1947   CHECK_EQ(state, PLUGIN_GET_METADATA_CALLED_STATE);
1948   CHECK_EQ(strcmp(creds->debug_string().c_str(), expected_creds_debug_string),
1949            0);
1950   creds->Unref();
1951 
1952   CHECK_EQ(state, PLUGIN_DESTROY_CALLED_STATE);
1953 }
1954 
TEST_F(CredentialsTest,TestGetWellKnownGoogleCredentialsFilePath)1955 TEST_F(CredentialsTest, TestGetWellKnownGoogleCredentialsFilePath) {
1956   auto home = GetEnv("HOME");
1957   bool restore_home_env = false;
1958 #if defined(GRPC_BAZEL_BUILD) && \
1959     (defined(GPR_POSIX_ENV) || defined(GPR_LINUX_ENV))
1960   // when running under bazel locally, the HOME variable is not set
1961   // so we set it to some fake value
1962   restore_home_env = true;
1963   SetEnv("HOME", "/fake/home/for/bazel");
1964 #endif  // defined(GRPC_BAZEL_BUILD) && (defined(GPR_POSIX_ENV) ||
1965         // defined(GPR_LINUX_ENV))
1966   std::string path = grpc_get_well_known_google_credentials_file_path();
1967   CHECK(!path.empty());
1968 #if defined(GPR_POSIX_ENV) || defined(GPR_LINUX_ENV)
1969   restore_home_env = true;
1970   UnsetEnv("HOME");
1971   path = grpc_get_well_known_google_credentials_file_path();
1972   CHECK(path.empty());
1973 #endif  // GPR_POSIX_ENV || GPR_LINUX_ENV
1974   if (restore_home_env) {
1975     SetOrUnsetEnv("HOME", home);
1976   }
1977 }
1978 
TEST_F(CredentialsTest,TestChannelCredsDuplicateWithoutCallCreds)1979 TEST_F(CredentialsTest, TestChannelCredsDuplicateWithoutCallCreds) {
1980   const char expected_creds_debug_string[] =
1981       "AccessTokenCredentials{Token:present}";
1982   ExecCtx exec_ctx;
1983 
1984   grpc_channel_credentials* channel_creds =
1985       grpc_fake_transport_security_credentials_create();
1986 
1987   RefCountedPtr<grpc_channel_credentials> dup =
1988       channel_creds->duplicate_without_call_credentials();
1989   CHECK(dup == channel_creds);
1990   dup.reset();
1991 
1992   grpc_call_credentials* call_creds =
1993       grpc_access_token_credentials_create("blah", nullptr);
1994   grpc_channel_credentials* composite_creds =
1995       grpc_composite_channel_credentials_create(channel_creds, call_creds,
1996                                                 nullptr);
1997   CHECK_EQ(
1998       strcmp(call_creds->debug_string().c_str(), expected_creds_debug_string),
1999       0);
2000 
2001   call_creds->Unref();
2002   dup = composite_creds->duplicate_without_call_credentials();
2003   CHECK(dup == channel_creds);
2004   dup.reset();
2005 
2006   channel_creds->Unref();
2007   composite_creds->Unref();
2008 }
2009 
2010 typedef struct {
2011   const char* url_scheme;
2012   const char* call_host;
2013   const char* call_method;
2014   const char* desired_service_url;
2015   const char* desired_method_name;
2016 } auth_metadata_context_test_case;
2017 
auth_metadata_context_build(const char * url_scheme,const grpc_slice & call_host,const grpc_slice & call_method,grpc_auth_context * auth_context,grpc_auth_metadata_context * auth_md_context)2018 void auth_metadata_context_build(const char* url_scheme,
2019                                  const grpc_slice& call_host,
2020                                  const grpc_slice& call_method,
2021                                  grpc_auth_context* auth_context,
2022                                  grpc_auth_metadata_context* auth_md_context) {
2023   char* service = grpc_slice_to_c_string(call_method);
2024   char* last_slash = strrchr(service, '/');
2025   char* method_name = nullptr;
2026   char* service_url = nullptr;
2027   grpc_auth_metadata_context_reset(auth_md_context);
2028   if (last_slash == nullptr) {
2029     LOG(ERROR) << "No '/' found in fully qualified method name";
2030     service[0] = '\0';
2031     method_name = gpr_strdup("");
2032   } else if (last_slash == service) {
2033     method_name = gpr_strdup("");
2034   } else {
2035     *last_slash = '\0';
2036     method_name = gpr_strdup(last_slash + 1);
2037   }
2038   char* host_and_port = grpc_slice_to_c_string(call_host);
2039   if (url_scheme != nullptr && strcmp(url_scheme, GRPC_SSL_URL_SCHEME) == 0) {
2040     // Remove the port if it is 443.
2041     char* port_delimiter = strrchr(host_and_port, ':');
2042     if (port_delimiter != nullptr && strcmp(port_delimiter + 1, "443") == 0) {
2043       *port_delimiter = '\0';
2044     }
2045   }
2046   gpr_asprintf(&service_url, "%s://%s%s",
2047                url_scheme == nullptr ? "" : url_scheme, host_and_port, service);
2048   auth_md_context->service_url = service_url;
2049   auth_md_context->method_name = method_name;
2050   auth_md_context->channel_auth_context =
2051       auth_context == nullptr
2052           ? nullptr
2053           : auth_context->Ref(DEBUG_LOCATION, "grpc_auth_metadata_context")
2054                 .release();
2055   gpr_free(service);
2056   gpr_free(host_and_port);
2057 }
2058 
TEST_F(CredentialsTest,TestAuthMetadataContext)2059 TEST_F(CredentialsTest, TestAuthMetadataContext) {
2060   auth_metadata_context_test_case test_cases[] = {
2061       // No service nor method.
2062       {"https", "www.foo.com", "", "https://www.foo.com", ""},
2063       // No method.
2064       {"https", "www.foo.com", "/Service", "https://www.foo.com/Service", ""},
2065       // Empty service and method.
2066       {"https", "www.foo.com", "//", "https://www.foo.com/", ""},
2067       // Empty method.
2068       {"https", "www.foo.com", "/Service/", "https://www.foo.com/Service", ""},
2069       // Malformed url.
2070       {"https", "www.foo.com:", "/Service/", "https://www.foo.com:/Service",
2071        ""},
2072       // https, default explicit port.
2073       {"https", "www.foo.com:443", "/Service/FooMethod",
2074        "https://www.foo.com/Service", "FooMethod"},
2075       // https, default implicit port.
2076       {"https", "www.foo.com", "/Service/FooMethod",
2077        "https://www.foo.com/Service", "FooMethod"},
2078       // https with ipv6 literal, default explicit port.
2079       {"https", "[1080:0:0:0:8:800:200C:417A]:443", "/Service/FooMethod",
2080        "https://[1080:0:0:0:8:800:200C:417A]/Service", "FooMethod"},
2081       // https with ipv6 literal, default implicit port.
2082       {"https", "[1080:0:0:0:8:800:200C:443]", "/Service/FooMethod",
2083        "https://[1080:0:0:0:8:800:200C:443]/Service", "FooMethod"},
2084       // https, custom port.
2085       {"https", "www.foo.com:8888", "/Service/FooMethod",
2086        "https://www.foo.com:8888/Service", "FooMethod"},
2087       // https with ipv6 literal, custom port.
2088       {"https", "[1080:0:0:0:8:800:200C:417A]:8888", "/Service/FooMethod",
2089        "https://[1080:0:0:0:8:800:200C:417A]:8888/Service", "FooMethod"},
2090       // custom url scheme, https default port.
2091       {"blah", "www.foo.com:443", "/Service/FooMethod",
2092        "blah://www.foo.com:443/Service", "FooMethod"}};
2093   for (uint32_t i = 0; i < GPR_ARRAY_SIZE(test_cases); i++) {
2094     const char* url_scheme = test_cases[i].url_scheme;
2095     grpc_slice call_host =
2096         grpc_slice_from_copied_string(test_cases[i].call_host);
2097     grpc_slice call_method =
2098         grpc_slice_from_copied_string(test_cases[i].call_method);
2099     grpc_auth_metadata_context auth_md_context;
2100     memset(&auth_md_context, 0, sizeof(auth_md_context));
2101     auth_metadata_context_build(url_scheme, call_host, call_method, nullptr,
2102                                 &auth_md_context);
2103     if (strcmp(auth_md_context.service_url,
2104                test_cases[i].desired_service_url) != 0) {
2105       Crash(absl::StrFormat("Invalid service url, want: %s, got %s.",
2106                             test_cases[i].desired_service_url,
2107                             auth_md_context.service_url));
2108     }
2109     if (strcmp(auth_md_context.method_name,
2110                test_cases[i].desired_method_name) != 0) {
2111       Crash(absl::StrFormat("Invalid method name, want: %s, got %s.",
2112                             test_cases[i].desired_method_name,
2113                             auth_md_context.method_name));
2114     }
2115     CHECK_EQ(auth_md_context.channel_auth_context, nullptr);
2116     grpc_slice_unref(call_host);
2117     grpc_slice_unref(call_method);
2118     grpc_auth_metadata_context_reset(&auth_md_context);
2119   }
2120 }
2121 
validate_external_account_creds_token_exchange_request(const grpc_http_request * request,const URI & request_uri,absl::string_view body)2122 void validate_external_account_creds_token_exchange_request(
2123     const grpc_http_request* request, const URI& request_uri,
2124     absl::string_view body) {
2125   // Check that the body is constructed properly.
2126   std::string get_url_equivalent =
2127       absl::StrFormat("%s?%s", "https://foo.com:5555/token", body);
2128   absl::StatusOr<URI> uri = URI::Parse(get_url_equivalent);
2129   if (!uri.ok()) {
2130     LOG(ERROR) << uri.status().ToString();
2131     CHECK_OK(uri);
2132   }
2133   assert_query_parameters(*uri, "audience", "audience");
2134   assert_query_parameters(*uri, "grant_type",
2135                           "urn:ietf:params:oauth:grant-type:token-exchange");
2136   assert_query_parameters(*uri, "requested_token_type",
2137                           "urn:ietf:params:oauth:token-type:access_token");
2138   assert_query_parameters(*uri, "subject_token", "test_subject_token");
2139   assert_query_parameters(*uri, "subject_token_type", "subject_token_type");
2140   assert_query_parameters(*uri, "scope",
2141                           "https://www.googleapis.com/auth/cloud-platform");
2142   // Check the rest of the request.
2143   EXPECT_EQ(request_uri.authority(), "foo.com:5555");
2144   EXPECT_EQ(request_uri.path(), "/token");
2145   ASSERT_EQ(request->hdr_count, 3);
2146   EXPECT_EQ(absl::string_view(request->hdrs[0].key), "Content-Type");
2147   EXPECT_EQ(absl::string_view(request->hdrs[0].value),
2148             "application/x-www-form-urlencoded");
2149   EXPECT_EQ(absl::string_view(request->hdrs[2].key), "Authorization");
2150   EXPECT_EQ(absl::string_view(request->hdrs[2].value),
2151             "Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=");
2152 }
2153 
validate_external_account_creds_token_exchange_request_with_url_encode(const grpc_http_request * request,const URI & uri,absl::string_view body)2154 void validate_external_account_creds_token_exchange_request_with_url_encode(
2155     const grpc_http_request* request, const URI& uri, absl::string_view body) {
2156   // Check that the body is constructed properly.
2157   EXPECT_EQ(
2158       body,
2159       "audience=audience_!%40%23%24&grant_type=urn%3Aietf%3Aparams%3Aoauth%"
2160       "3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%"
2161       "3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&subject_token_type="
2162       "subject_token_type_!%40%23%24&subject_token=test_subject_token&"
2163       "scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform&"
2164       "options=%7B%7D");
2165   // Check the rest of the request.
2166   EXPECT_EQ(uri.authority(), "foo.com:5555");
2167   EXPECT_EQ(uri.path(), "/token_url_encode");
2168   ASSERT_EQ(request->hdr_count, 3);
2169   EXPECT_EQ(absl::string_view(request->hdrs[0].key), "Content-Type");
2170   EXPECT_EQ(absl::string_view(request->hdrs[0].value),
2171             "application/x-www-form-urlencoded");
2172   EXPECT_EQ(absl::string_view(request->hdrs[2].key), "Authorization");
2173   EXPECT_EQ(absl::string_view(request->hdrs[2].value),
2174             "Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=");
2175 }
2176 
validate_external_account_creds_service_account_impersonation_request(const grpc_http_request * request,const URI & uri,absl::string_view body)2177 void validate_external_account_creds_service_account_impersonation_request(
2178     const grpc_http_request* request, const URI& uri, absl::string_view body) {
2179   // Check that the body is constructed properly.
2180   EXPECT_EQ(body, "scope=scope_1%20scope_2&lifetime=3600s");
2181   // Check the rest of the request.
2182   EXPECT_EQ(uri.authority(), "foo.com:5555");
2183   EXPECT_EQ(uri.path(), "/service_account_impersonation");
2184   ASSERT_EQ(request->hdr_count, 2);
2185   EXPECT_EQ(absl::string_view(request->hdrs[0].key), "Content-Type");
2186   EXPECT_EQ(absl::string_view(request->hdrs[0].value),
2187             "application/x-www-form-urlencoded");
2188   EXPECT_EQ(absl::string_view(request->hdrs[1].key), "Authorization");
2189   EXPECT_EQ(absl::string_view(request->hdrs[1].value),
2190             "Bearer token_exchange_access_token");
2191 }
2192 
validate_external_account_creds_serv_acc_imp_custom_lifetime_request(const grpc_http_request * request,const URI & uri,absl::string_view body)2193 void validate_external_account_creds_serv_acc_imp_custom_lifetime_request(
2194     const grpc_http_request* request, const URI& uri, absl::string_view body) {
2195   // Check that the body is constructed properly.
2196   EXPECT_EQ(body, "scope=scope_1%20scope_2&lifetime=1800s");
2197   // Check the rest of the request.
2198   EXPECT_EQ(uri.authority(), "foo.com:5555");
2199   EXPECT_EQ(uri.path(), "/service_account_impersonation");
2200   ASSERT_EQ(request->hdr_count, 2);
2201   EXPECT_EQ(absl::string_view(request->hdrs[0].key), "Content-Type");
2202   EXPECT_EQ(absl::string_view(request->hdrs[0].value),
2203             "application/x-www-form-urlencoded");
2204   EXPECT_EQ(absl::string_view(request->hdrs[1].key), "Authorization");
2205   EXPECT_EQ(absl::string_view(request->hdrs[1].value),
2206             "Bearer token_exchange_access_token");
2207 }
2208 
external_acc_creds_serv_acc_imp_custom_lifetime_httpcli_post_success(const grpc_http_request * request,const URI & uri,absl::string_view body,Timestamp,grpc_closure * on_done,grpc_http_response * response)2209 int external_acc_creds_serv_acc_imp_custom_lifetime_httpcli_post_success(
2210     const grpc_http_request* request, const URI& uri, absl::string_view body,
2211     Timestamp /*deadline*/, grpc_closure* on_done,
2212     grpc_http_response* response) {
2213   if (uri.path() == "/token") {
2214     validate_external_account_creds_token_exchange_request(request, uri, body);
2215     *response = http_response(
2216         200, valid_external_account_creds_token_exchange_response);
2217   } else if (uri.path() == "/service_account_impersonation") {
2218     validate_external_account_creds_serv_acc_imp_custom_lifetime_request(
2219         request, uri, body);
2220     *response = http_response(
2221         200,
2222         valid_external_account_creds_service_account_impersonation_response);
2223   }
2224   ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus());
2225   return 1;
2226 }
2227 
external_account_creds_httpcli_post_success(const grpc_http_request * request,const URI & uri,absl::string_view body,Timestamp,grpc_closure * on_done,grpc_http_response * response)2228 int external_account_creds_httpcli_post_success(
2229     const grpc_http_request* request, const URI& uri, absl::string_view body,
2230     Timestamp /*deadline*/, grpc_closure* on_done,
2231     grpc_http_response* response) {
2232   if (uri.path() == "/token") {
2233     validate_external_account_creds_token_exchange_request(request, uri, body);
2234     *response = http_response(
2235         200, valid_external_account_creds_token_exchange_response);
2236   } else if (uri.path() == "/service_account_impersonation") {
2237     validate_external_account_creds_service_account_impersonation_request(
2238         request, uri, body);
2239     *response = http_response(
2240         200,
2241         valid_external_account_creds_service_account_impersonation_response);
2242   } else if (uri.path() == "/token_url_encode") {
2243     validate_external_account_creds_token_exchange_request_with_url_encode(
2244         request, uri, body);
2245     *response = http_response(
2246         200, valid_external_account_creds_token_exchange_response);
2247   }
2248   ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus());
2249   return 1;
2250 }
2251 
external_account_creds_httpcli_post_failure_token_exchange_response_missing_access_token(const grpc_http_request *,const URI & uri,absl::string_view,Timestamp,grpc_closure * on_done,grpc_http_response * response)2252 int external_account_creds_httpcli_post_failure_token_exchange_response_missing_access_token(
2253     const grpc_http_request* /*request*/, const URI& uri,
2254     absl::string_view /*body*/, Timestamp /*deadline*/, grpc_closure* on_done,
2255     grpc_http_response* response) {
2256   if (uri.path() == "/token") {
2257     *response = http_response(200,
2258                               "{\"not_access_token\":\"not_access_token\","
2259                               "\"expires_in\":3599,"
2260                               " \"token_type\":\"Bearer\"}");
2261   } else if (uri.path() == "/service_account_impersonation") {
2262     *response = http_response(
2263         200,
2264         valid_external_account_creds_service_account_impersonation_response);
2265   }
2266   ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus());
2267   return 1;
2268 }
2269 
url_external_account_creds_httpcli_get_success(const grpc_http_request *,const URI & uri,Timestamp,grpc_closure * on_done,grpc_http_response * response)2270 int url_external_account_creds_httpcli_get_success(
2271     const grpc_http_request* /*request*/, const URI& uri,
2272     Timestamp /*deadline*/, grpc_closure* on_done,
2273     grpc_http_response* response) {
2274   if (uri.path() == "/generate_subject_token_format_text") {
2275     *response = http_response(
2276         200,
2277         valid_url_external_account_creds_retrieve_subject_token_response_format_text);
2278   } else if (uri.path() == "/path/to/url/creds?p1=v1&p2=v2") {
2279     *response = http_response(
2280         200,
2281         valid_url_external_account_creds_retrieve_subject_token_response_format_text);
2282   } else if (uri.path() == "/generate_subject_token_format_json") {
2283     *response = http_response(
2284         200,
2285         valid_url_external_account_creds_retrieve_subject_token_response_format_json);
2286   }
2287   ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus());
2288   return 1;
2289 }
2290 
validate_aws_external_account_creds_token_exchange_request(const grpc_http_request * request,const URI & request_uri,absl::string_view body)2291 void validate_aws_external_account_creds_token_exchange_request(
2292     const grpc_http_request* request, const URI& request_uri,
2293     absl::string_view body) {
2294   // Check that the regional_cred_verification_url got constructed
2295   // with the correct AWS Region ("test_regionz" or "test_region").
2296   EXPECT_NE(body.find("regional_cred_verification_url_test_region"), body.npos);
2297   std::string get_url_equivalent =
2298       absl::StrFormat("%s?%s", "https://foo.com:5555/token", body);
2299   absl::StatusOr<URI> uri = URI::Parse(get_url_equivalent);
2300   CHECK_OK(uri);
2301   assert_query_parameters(*uri, "audience", "audience");
2302   assert_query_parameters(*uri, "grant_type",
2303                           "urn:ietf:params:oauth:grant-type:token-exchange");
2304   assert_query_parameters(*uri, "requested_token_type",
2305                           "urn:ietf:params:oauth:token-type:access_token");
2306   assert_query_parameters(*uri, "subject_token_type", "subject_token_type");
2307   assert_query_parameters(*uri, "scope",
2308                           "https://www.googleapis.com/auth/cloud-platform");
2309   // Check the rest of the request.
2310   EXPECT_EQ(request_uri.authority(), "foo.com:5555");
2311   EXPECT_EQ(request_uri.path(), "/token");
2312   ASSERT_EQ(request->hdr_count, 3);
2313   EXPECT_EQ(absl::string_view(request->hdrs[0].key), "Content-Type");
2314   EXPECT_EQ(absl::string_view(request->hdrs[0].value),
2315             "application/x-www-form-urlencoded");
2316   EXPECT_EQ(absl::string_view(request->hdrs[1].key), "x-goog-api-client");
2317   EXPECT_EQ(
2318       absl::string_view(request->hdrs[1].value),
2319       absl::StrFormat("gl-cpp/unknown auth/%s google-byoid-sdk source/aws "
2320                       "sa-impersonation/false config-lifetime/false",
2321                       grpc_version_string()));
2322   EXPECT_EQ(absl::string_view(request->hdrs[2].key), "Authorization");
2323   EXPECT_EQ(absl::string_view(request->hdrs[2].value),
2324             "Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=");
2325 }
2326 
aws_external_account_creds_httpcli_get_success(const grpc_http_request *,const URI & uri,Timestamp,grpc_closure * on_done,grpc_http_response * response)2327 int aws_external_account_creds_httpcli_get_success(
2328     const grpc_http_request* /*request*/, const URI& uri,
2329     Timestamp /*deadline*/, grpc_closure* on_done,
2330     grpc_http_response* response) {
2331   if (uri.path() == "/region_url") {
2332     *response = http_response(200, "test_regionz");
2333   } else if (uri.path() == "/url") {
2334     *response = http_response(200, "test_role_name");
2335   } else if (uri.path() == "/url_no_role_name") {
2336     *response = http_response(200, "");
2337   } else if (uri.path() == "/url/test_role_name") {
2338     *response = http_response(
2339         200, valid_aws_external_account_creds_retrieve_signing_keys_response);
2340   }
2341   ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus());
2342   return 1;
2343 }
2344 
aws_imdsv2_external_account_creds_httpcli_get_success(const grpc_http_request * request,const URI & uri,Timestamp deadline,grpc_closure * on_done,grpc_http_response * response)2345 int aws_imdsv2_external_account_creds_httpcli_get_success(
2346     const grpc_http_request* request, const URI& uri, Timestamp deadline,
2347     grpc_closure* on_done, grpc_http_response* response) {
2348   EXPECT_EQ(request->hdr_count, 1);
2349   if (request->hdr_count == 1) {
2350     EXPECT_EQ(absl::string_view(request->hdrs[0].key),
2351               "x-aws-ec2-metadata-token");
2352     EXPECT_EQ(absl::string_view(request->hdrs[0].value),
2353               aws_imdsv2_session_token);
2354   }
2355   return aws_external_account_creds_httpcli_get_success(request, uri, deadline,
2356                                                         on_done, response);
2357 }
2358 
aws_imdsv2_external_account_creds_httpcli_put_success(const grpc_http_request * request,const URI & uri,absl::string_view,Timestamp,grpc_closure * on_done,grpc_http_response * response)2359 int aws_imdsv2_external_account_creds_httpcli_put_success(
2360     const grpc_http_request* request, const URI& uri,
2361     absl::string_view /*body*/, Timestamp /*deadline*/, grpc_closure* on_done,
2362     grpc_http_response* response) {
2363   EXPECT_EQ(request->hdr_count, 1);
2364   if (request->hdr_count == 1) {
2365     EXPECT_EQ(absl::string_view(request->hdrs[0].key),
2366               "x-aws-ec2-metadata-token-ttl-seconds");
2367     EXPECT_EQ(absl::string_view(request->hdrs[0].value), "300");
2368   }
2369   EXPECT_EQ(uri.path(), "/imdsv2_session_token_url");
2370   *response = http_response(200, aws_imdsv2_session_token);
2371   ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus());
2372   return 1;
2373 }
2374 
aws_external_account_creds_httpcli_post_success(const grpc_http_request * request,const URI & uri,absl::string_view body,Timestamp,grpc_closure * on_done,grpc_http_response * response)2375 int aws_external_account_creds_httpcli_post_success(
2376     const grpc_http_request* request, const URI& uri, absl::string_view body,
2377     Timestamp /*deadline*/, grpc_closure* on_done,
2378     grpc_http_response* response) {
2379   if (uri.path() == "/token") {
2380     validate_aws_external_account_creds_token_exchange_request(request, uri,
2381                                                                body);
2382     *response = http_response(
2383         200, valid_external_account_creds_token_exchange_response);
2384   }
2385   ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus());
2386   return 1;
2387 }
2388 
2389 class TokenFetcherCredentialsTest : public ::testing::Test {
2390  protected:
2391   class TestTokenFetcherCredentials final : public TokenFetcherCredentials {
2392    public:
TestTokenFetcherCredentials(std::shared_ptr<grpc_event_engine::experimental::EventEngine> event_engine=nullptr)2393     explicit TestTokenFetcherCredentials(
2394         std::shared_ptr<grpc_event_engine::experimental::EventEngine>
2395             event_engine = nullptr)
2396         : TokenFetcherCredentials(std::move(event_engine),
2397                                   /*test_only_use_backoff_jitter=*/false) {}
2398 
~TestTokenFetcherCredentials()2399     ~TestTokenFetcherCredentials() override { CHECK_EQ(queue_.size(), 0); }
2400 
AddResult(absl::StatusOr<RefCountedPtr<Token>> result)2401     void AddResult(absl::StatusOr<RefCountedPtr<Token>> result) {
2402       MutexLock lock(&mu_);
2403       queue_.push_front(std::move(result));
2404     }
2405 
num_fetches() const2406     size_t num_fetches() const { return num_fetches_; }
2407 
2408    private:
2409     class TestFetchRequest final : public FetchRequest {
2410      public:
TestFetchRequest(grpc_event_engine::experimental::EventEngine & event_engine,absl::AnyInvocable<void (absl::StatusOr<RefCountedPtr<Token>>)> on_done,absl::StatusOr<RefCountedPtr<Token>> result)2411       TestFetchRequest(
2412           grpc_event_engine::experimental::EventEngine& event_engine,
2413           absl::AnyInvocable<void(absl::StatusOr<RefCountedPtr<Token>>)>
2414               on_done,
2415           absl::StatusOr<RefCountedPtr<Token>> result) {
2416         event_engine.Run([on_done = std::move(on_done),
2417                           result = std::move(result)]() mutable {
2418           ApplicationCallbackExecCtx application_exec_ctx;
2419           ExecCtx exec_ctx;
2420           std::exchange(on_done, nullptr)(std::move(result));
2421         });
2422       }
2423 
Orphan()2424       void Orphan() override { Unref(); }
2425     };
2426 
FetchToken(Timestamp deadline,absl::AnyInvocable<void (absl::StatusOr<RefCountedPtr<Token>>)> on_done)2427     OrphanablePtr<FetchRequest> FetchToken(
2428         Timestamp deadline,
2429         absl::AnyInvocable<void(absl::StatusOr<RefCountedPtr<Token>>)> on_done)
2430         override {
2431       absl::StatusOr<RefCountedPtr<Token>> result;
2432       {
2433         MutexLock lock(&mu_);
2434         CHECK(!queue_.empty());
2435         result = std::move(queue_.back());
2436         queue_.pop_back();
2437       }
2438       num_fetches_.fetch_add(1);
2439       return MakeOrphanable<TestFetchRequest>(
2440           event_engine(), std::move(on_done), std::move(result));
2441     }
2442 
debug_string()2443     std::string debug_string() override {
2444       return "TestTokenFetcherCredentials";
2445     }
2446 
type() const2447     UniqueTypeName type() const override {
2448       static UniqueTypeName::Factory kFactory("TestTokenFetcherCredentials");
2449       return kFactory.Create();
2450     }
2451 
2452     Mutex mu_;
2453     std::deque<absl::StatusOr<RefCountedPtr<Token>>> queue_
2454         ABSL_GUARDED_BY(&mu_);
2455 
2456     std::atomic<size_t> num_fetches_{0};
2457   };
2458 
SetUp()2459   void SetUp() override {
2460     event_engine_ = std::make_shared<FuzzingEventEngine>(
2461         FuzzingEventEngine::Options(), fuzzing_event_engine::Actions());
2462     grpc_timer_manager_set_start_threaded(false);
2463     grpc_init();
2464     creds_ = MakeRefCounted<TestTokenFetcherCredentials>(event_engine_);
2465   }
2466 
TearDown()2467   void TearDown() override {
2468     event_engine_->FuzzingDone();
2469     event_engine_->TickUntilIdle();
2470     event_engine_->UnsetGlobalHooks();
2471     creds_.reset();
2472     grpc_event_engine::experimental::WaitForSingleOwner(
2473         std::move(event_engine_));
2474     grpc_shutdown_blocking();
2475   }
2476 
MakeToken(absl::string_view token,Timestamp expiration=Timestamp::InfFuture ())2477   static RefCountedPtr<TokenFetcherCredentials::Token> MakeToken(
2478       absl::string_view token, Timestamp expiration = Timestamp::InfFuture()) {
2479     return MakeRefCounted<TokenFetcherCredentials::Token>(
2480         Slice::FromCopiedString(token), expiration);
2481   }
2482 
2483   std::shared_ptr<FuzzingEventEngine> event_engine_;
2484   RefCountedPtr<TestTokenFetcherCredentials> creds_;
2485 };
2486 
TEST_F(TokenFetcherCredentialsTest,Basic)2487 TEST_F(TokenFetcherCredentialsTest, Basic) {
2488   const auto kExpirationTime = Timestamp::Now() + Duration::Hours(1);
2489   ExecCtx exec_ctx;
2490   creds_->AddResult(MakeToken("foo", kExpirationTime));
2491   // First request will trigger a fetch.
2492   LOG(INFO) << "First request";
2493   auto state = RequestMetadataState::NewInstance(
2494       absl::OkStatus(), "authorization: foo", /*expect_delay=*/true);
2495   state->RunRequestMetadataTest(creds_.get(), kTestUrlScheme, kTestAuthority,
2496                                 kTestPath);
2497   EXPECT_EQ(creds_->num_fetches(), 1);
2498   // Second request while fetch is still outstanding will be delayed but
2499   // will not trigger a new fetch.
2500   LOG(INFO) << "Second request";
2501   state = RequestMetadataState::NewInstance(
2502       absl::OkStatus(), "authorization: foo", /*expect_delay=*/true);
2503   state->RunRequestMetadataTest(creds_.get(), kTestUrlScheme, kTestAuthority,
2504                                 kTestPath);
2505   EXPECT_EQ(creds_->num_fetches(), 1);
2506   // Now tick to finish the fetch.
2507   event_engine_->TickUntilIdle();
2508   // Next request will be served from cache with no delay.
2509   LOG(INFO) << "Third request";
2510   state = RequestMetadataState::NewInstance(
2511       absl::OkStatus(), "authorization: foo", /*expect_delay=*/false);
2512   state->RunRequestMetadataTest(creds_.get(), kTestUrlScheme, kTestAuthority,
2513                                 kTestPath);
2514   EXPECT_EQ(creds_->num_fetches(), 1);
2515   // Advance time to expiration minus expiration adjustment and prefetch time.
2516   exec_ctx.TestOnlySetNow(kExpirationTime - Duration::Seconds(90));
2517   // No new fetch yet.
2518   EXPECT_EQ(creds_->num_fetches(), 1);
2519   // Next request will trigger a new fetch but will still use the
2520   // cached token.
2521   creds_->AddResult(MakeToken("bar"));
2522   LOG(INFO) << "Fourth request";
2523   state = RequestMetadataState::NewInstance(
2524       absl::OkStatus(), "authorization: foo", /*expect_delay=*/false);
2525   state->RunRequestMetadataTest(creds_.get(), kTestUrlScheme, kTestAuthority,
2526                                 kTestPath);
2527   EXPECT_EQ(creds_->num_fetches(), 2);
2528   event_engine_->TickUntilIdle();
2529   // Next request will use the new data.
2530   LOG(INFO) << "Fifth request";
2531   state = RequestMetadataState::NewInstance(
2532       absl::OkStatus(), "authorization: bar", /*expect_delay=*/false);
2533   state->RunRequestMetadataTest(creds_.get(), kTestUrlScheme, kTestAuthority,
2534                                 kTestPath);
2535   EXPECT_EQ(creds_->num_fetches(), 2);
2536 }
2537 
TEST_F(TokenFetcherCredentialsTest,Expires30SecondsEarly)2538 TEST_F(TokenFetcherCredentialsTest, Expires30SecondsEarly) {
2539   const auto kExpirationTime = Timestamp::Now() + Duration::Hours(1);
2540   ExecCtx exec_ctx;
2541   creds_->AddResult(MakeToken("foo", kExpirationTime));
2542   // First request will trigger a fetch.
2543   auto state = RequestMetadataState::NewInstance(
2544       absl::OkStatus(), "authorization: foo", /*expect_delay=*/true);
2545   state->RunRequestMetadataTest(creds_.get(), kTestUrlScheme, kTestAuthority,
2546                                 kTestPath);
2547   EXPECT_EQ(creds_->num_fetches(), 1);
2548   event_engine_->TickUntilIdle();
2549   // Advance time to expiration minus 30 seconds.
2550   exec_ctx.TestOnlySetNow(kExpirationTime - Duration::Seconds(30));
2551   // No new fetch yet.
2552   EXPECT_EQ(creds_->num_fetches(), 1);
2553   // Next request will trigger a new fetch and will delay the call until
2554   // the fetch completes.
2555   creds_->AddResult(MakeToken("bar"));
2556   state = RequestMetadataState::NewInstance(
2557       absl::OkStatus(), "authorization: bar", /*expect_delay=*/true);
2558   state->RunRequestMetadataTest(creds_.get(), kTestUrlScheme, kTestAuthority,
2559                                 kTestPath);
2560   EXPECT_EQ(creds_->num_fetches(), 2);
2561   event_engine_->TickUntilIdle();
2562 }
2563 
TEST_F(TokenFetcherCredentialsTest,FetchFails)2564 TEST_F(TokenFetcherCredentialsTest, FetchFails) {
2565   const absl::Status kExpectedError = absl::UnavailableError("bummer, dude");
2566   absl::optional<FuzzingEventEngine::Duration> run_after_duration;
2567   event_engine_->SetRunAfterDurationCallback(
2568       [&](FuzzingEventEngine::Duration duration) {
2569         run_after_duration = duration;
2570       });
2571   ExecCtx exec_ctx;
2572   // First request will trigger a fetch, which will fail.
2573   LOG(INFO) << "Sending first RPC.";
2574   creds_->AddResult(kExpectedError);
2575   auto state = RequestMetadataState::NewInstance(kExpectedError, "",
2576                                                  /*expect_delay=*/true);
2577   state->RunRequestMetadataTest(creds_.get(), kTestUrlScheme, kTestAuthority,
2578                                 kTestPath);
2579   EXPECT_EQ(creds_->num_fetches(), 1);
2580   while (!run_after_duration.has_value()) event_engine_->Tick();
2581   // Make sure backoff was set for the right period.
2582   EXPECT_EQ(run_after_duration, std::chrono::seconds(1));
2583   run_after_duration.reset();
2584   // Start a new call now, which will fail because we're in backoff.
2585   LOG(INFO) << "Sending second RPC.";
2586   state = RequestMetadataState::NewInstance(
2587       kExpectedError, "authorization: foo", /*expect_delay=*/false);
2588   state->RunRequestMetadataTest(creds_.get(), kTestUrlScheme, kTestAuthority,
2589                                 kTestPath);
2590   EXPECT_EQ(creds_->num_fetches(), 1);
2591   // Tick until backoff expires.
2592   LOG(INFO) << "Waiting for backoff.";
2593   event_engine_->TickUntilIdle();
2594   EXPECT_EQ(creds_->num_fetches(), 1);
2595   // Starting another call should trigger a new fetch, which will
2596   // succeed this time.
2597   LOG(INFO) << "Sending third RPC.";
2598   creds_->AddResult(MakeToken("foo"));
2599   state = RequestMetadataState::NewInstance(
2600       absl::OkStatus(), "authorization: foo", /*expect_delay=*/true);
2601   state->RunRequestMetadataTest(creds_.get(), kTestUrlScheme, kTestAuthority,
2602                                 kTestPath);
2603   EXPECT_EQ(creds_->num_fetches(), 2);
2604 }
2605 
TEST_F(TokenFetcherCredentialsTest,Backoff)2606 TEST_F(TokenFetcherCredentialsTest, Backoff) {
2607   const absl::Status kExpectedError = absl::UnavailableError("bummer, dude");
2608   absl::optional<FuzzingEventEngine::Duration> run_after_duration;
2609   event_engine_->SetRunAfterDurationCallback(
2610       [&](FuzzingEventEngine::Duration duration) {
2611         run_after_duration = duration;
2612       });
2613   ExecCtx exec_ctx;
2614   // First request will trigger a fetch, which will fail.
2615   LOG(INFO) << "Sending first RPC.";
2616   creds_->AddResult(kExpectedError);
2617   auto state = RequestMetadataState::NewInstance(kExpectedError, "",
2618                                                  /*expect_delay=*/true);
2619   state->RunRequestMetadataTest(creds_.get(), kTestUrlScheme, kTestAuthority,
2620                                 kTestPath);
2621   EXPECT_EQ(creds_->num_fetches(), 1);
2622   while (!run_after_duration.has_value()) event_engine_->Tick();
2623   // Make sure backoff was set for the right period.
2624   EXPECT_EQ(run_after_duration, std::chrono::seconds(1));
2625   run_after_duration.reset();
2626   // Start a new call now, which will fail because we're in backoff.
2627   LOG(INFO) << "Sending second RPC.";
2628   state = RequestMetadataState::NewInstance(kExpectedError, "",
2629                                             /*expect_delay=*/false);
2630   state->RunRequestMetadataTest(creds_.get(), kTestUrlScheme, kTestAuthority,
2631                                 kTestPath);
2632   EXPECT_EQ(creds_->num_fetches(), 1);
2633   // Tick until backoff expires.
2634   LOG(INFO) << "Waiting for backoff.";
2635   event_engine_->TickUntilIdle();
2636   EXPECT_EQ(creds_->num_fetches(), 1);
2637   // Starting another call should trigger a new fetch, which will again fail.
2638   LOG(INFO) << "Sending third RPC.";
2639   creds_->AddResult(kExpectedError);
2640   state = RequestMetadataState::NewInstance(kExpectedError, "",
2641                                             /*expect_delay=*/true);
2642   state->RunRequestMetadataTest(creds_.get(), kTestUrlScheme, kTestAuthority,
2643                                 kTestPath);
2644   EXPECT_EQ(creds_->num_fetches(), 2);
2645   while (!run_after_duration.has_value()) event_engine_->Tick();
2646   // The backoff time should be longer now.
2647   EXPECT_EQ(run_after_duration, std::chrono::milliseconds(1600))
2648       << "actual: " << run_after_duration->count();
2649   run_after_duration.reset();
2650   // Start a new call now, which will fail because we're in backoff.
2651   LOG(INFO) << "Sending fourth RPC.";
2652   state = RequestMetadataState::NewInstance(kExpectedError, "",
2653                                             /*expect_delay=*/false);
2654   state->RunRequestMetadataTest(creds_.get(), kTestUrlScheme, kTestAuthority,
2655                                 kTestPath);
2656   EXPECT_EQ(creds_->num_fetches(), 2);
2657   // Tick until backoff expires.
2658   LOG(INFO) << "Waiting for backoff.";
2659   event_engine_->TickUntilIdle();
2660   EXPECT_EQ(creds_->num_fetches(), 2);
2661   // Starting another call should trigger a new fetch, which will again fail.
2662   LOG(INFO) << "Sending fifth RPC.";
2663   creds_->AddResult(kExpectedError);
2664   state = RequestMetadataState::NewInstance(kExpectedError, "",
2665                                             /*expect_delay=*/true);
2666   state->RunRequestMetadataTest(creds_.get(), kTestUrlScheme, kTestAuthority,
2667                                 kTestPath);
2668   EXPECT_EQ(creds_->num_fetches(), 3);
2669   while (!run_after_duration.has_value()) event_engine_->Tick();
2670   // The backoff time should be longer now.
2671   EXPECT_EQ(run_after_duration, std::chrono::milliseconds(2560))
2672       << "actual: " << run_after_duration->count();
2673 }
2674 
TEST_F(TokenFetcherCredentialsTest,ShutdownWhileBackoffTimerPending)2675 TEST_F(TokenFetcherCredentialsTest, ShutdownWhileBackoffTimerPending) {
2676   const absl::Status kExpectedError = absl::UnavailableError("bummer, dude");
2677   absl::optional<FuzzingEventEngine::Duration> run_after_duration;
2678   event_engine_->SetRunAfterDurationCallback(
2679       [&](FuzzingEventEngine::Duration duration) {
2680         run_after_duration = duration;
2681       });
2682   ExecCtx exec_ctx;
2683   creds_->AddResult(kExpectedError);
2684   // First request will trigger a fetch, which will fail.
2685   auto state = RequestMetadataState::NewInstance(kExpectedError, "",
2686                                                  /*expect_delay=*/true);
2687   state->RunRequestMetadataTest(creds_.get(), kTestUrlScheme, kTestAuthority,
2688                                 kTestPath);
2689   EXPECT_EQ(creds_->num_fetches(), 1);
2690   while (!run_after_duration.has_value()) event_engine_->Tick();
2691   // Make sure backoff was set for the right period.
2692   EXPECT_EQ(run_after_duration, std::chrono::seconds(1));
2693   run_after_duration.reset();
2694   // Do nothing else.  Make sure the creds shut down correctly.
2695 }
2696 
2697 // The subclass of ExternalAccountCredentials for testing.
2698 // ExternalAccountCredentials is an abstract class so we can't directly test
2699 // against it.
2700 class TestExternalAccountCredentials final : public ExternalAccountCredentials {
2701  public:
TestExternalAccountCredentials(Options options,std::vector<std::string> scopes,std::shared_ptr<grpc_event_engine::experimental::EventEngine> event_engine=nullptr)2702   TestExternalAccountCredentials(
2703       Options options, std::vector<std::string> scopes,
2704       std::shared_ptr<grpc_event_engine::experimental::EventEngine>
2705           event_engine = nullptr)
2706       : ExternalAccountCredentials(std::move(options), std::move(scopes),
2707                                    std::move(event_engine)) {}
2708 
GetMetricsValue()2709   std::string GetMetricsValue() { return MetricsHeaderValue(); }
2710 
debug_string()2711   std::string debug_string() override {
2712     return "TestExternalAccountCredentials";
2713   }
2714 
type() const2715   UniqueTypeName type() const override {
2716     static UniqueTypeName::Factory kFactory("TestExternalAccountCredentials");
2717     return kFactory.Create();
2718   }
2719 
2720  private:
RetrieveSubjectToken(Timestamp,absl::AnyInvocable<void (absl::StatusOr<std::string>)> on_done)2721   OrphanablePtr<FetchBody> RetrieveSubjectToken(
2722       Timestamp /*deadline*/,
2723       absl::AnyInvocable<void(absl::StatusOr<std::string>)> on_done) override {
2724     return MakeOrphanable<NoOpFetchBody>(event_engine(), std::move(on_done),
2725                                          "test_subject_token");
2726   }
2727 };
2728 
TEST_F(CredentialsTest,TestExternalAccountCredsMetricsHeader)2729 TEST_F(CredentialsTest, TestExternalAccountCredsMetricsHeader) {
2730   Json credential_source = Json::FromString("");
2731   TestExternalAccountCredentials::ServiceAccountImpersonation
2732       service_account_impersonation;
2733   service_account_impersonation.token_lifetime_seconds = 3600;
2734   TestExternalAccountCredentials::Options options = {
2735       "external_account",                 // type;
2736       "audience",                         // audience;
2737       "subject_token_type",               // subject_token_type;
2738       "",                                 // service_account_impersonation_url;
2739       service_account_impersonation,      // service_account_impersonation;
2740       "https://foo.com:5555/token",       // token_url;
2741       "https://foo.com:5555/token_info",  // token_info_url;
2742       credential_source,                  // credential_source;
2743       "quota_project_id",                 // quota_project_id;
2744       "client_id",                        // client_id;
2745       "client_secret",                    // client_secret;
2746       "",                                 // workforce_pool_user_project;
2747   };
2748   TestExternalAccountCredentials creds(options, {});
2749   EXPECT_EQ(
2750       creds.GetMetricsValue(),
2751       absl::StrFormat("gl-cpp/unknown auth/%s google-byoid-sdk source/unknown "
2752                       "sa-impersonation/false config-lifetime/false",
2753                       grpc_version_string()));
2754 }
2755 
TEST_F(CredentialsTest,TestExternalAccountCredsMetricsHeaderWithServiceAccountImpersonation)2756 TEST_F(CredentialsTest,
2757        TestExternalAccountCredsMetricsHeaderWithServiceAccountImpersonation) {
2758   Json credential_source = Json::FromString("");
2759   TestExternalAccountCredentials::ServiceAccountImpersonation
2760       service_account_impersonation;
2761   service_account_impersonation.token_lifetime_seconds = 3600;
2762   TestExternalAccountCredentials::Options options = {
2763       "external_account",    // type;
2764       "audience",            // audience;
2765       "subject_token_type",  // subject_token_type;
2766       "https://foo.com:5555/service_account_impersonation",  // service_account_impersonation_url;
2767       service_account_impersonation,      // service_account_impersonation;
2768       "https://foo.com:5555/token",       // token_url;
2769       "https://foo.com:5555/token_info",  // token_info_url;
2770       credential_source,                  // credential_source;
2771       "quota_project_id",                 // quota_project_id;
2772       "client_id",                        // client_id;
2773       "client_secret",                    // client_secret;
2774       "",                                 // workforce_pool_user_project;
2775   };
2776   TestExternalAccountCredentials creds(options, {});
2777   EXPECT_EQ(
2778       creds.GetMetricsValue(),
2779       absl::StrFormat("gl-cpp/unknown auth/%s google-byoid-sdk source/unknown "
2780                       "sa-impersonation/true config-lifetime/false",
2781                       grpc_version_string()));
2782 }
2783 
TEST_F(CredentialsTest,TestExternalAccountCredsMetricsHeaderWithConfigLifetime)2784 TEST_F(CredentialsTest,
2785        TestExternalAccountCredsMetricsHeaderWithConfigLifetime) {
2786   Json credential_source = Json::FromString("");
2787   TestExternalAccountCredentials::ServiceAccountImpersonation
2788       service_account_impersonation;
2789   service_account_impersonation.token_lifetime_seconds = 5000;
2790   TestExternalAccountCredentials::Options options = {
2791       "external_account",    // type;
2792       "audience",            // audience;
2793       "subject_token_type",  // subject_token_type;
2794       "https://foo.com:5555/service_account_impersonation",  // service_account_impersonation_url;
2795       service_account_impersonation,      // service_account_impersonation;
2796       "https://foo.com:5555/token",       // token_url;
2797       "https://foo.com:5555/token_info",  // token_info_url;
2798       credential_source,                  // credential_source;
2799       "quota_project_id",                 // quota_project_id;
2800       "client_id",                        // client_id;
2801       "client_secret",                    // client_secret;
2802       "",                                 // workforce_pool_user_project;
2803   };
2804   TestExternalAccountCredentials creds(options, {});
2805   EXPECT_EQ(
2806       creds.GetMetricsValue(),
2807       absl::StrFormat("gl-cpp/unknown auth/%s google-byoid-sdk source/unknown "
2808                       "sa-impersonation/true config-lifetime/true",
2809                       grpc_version_string()));
2810 }
2811 
2812 class ExternalAccountCredentialsTest : public ::testing::Test {
2813  protected:
SetUp()2814   void SetUp() override {
2815     grpc_timer_manager_set_start_threaded(false);
2816     grpc_init();
2817   }
2818 
TearDown()2819   void TearDown() override {
2820     event_engine_->FuzzingDone();
2821     event_engine_->TickUntilIdle();
2822     event_engine_->UnsetGlobalHooks();
2823     grpc_event_engine::experimental::WaitForSingleOwner(
2824         std::move(event_engine_));
2825     grpc_shutdown_blocking();
2826   }
2827 
2828   std::shared_ptr<FuzzingEventEngine> event_engine_ =
2829       std::make_shared<FuzzingEventEngine>(FuzzingEventEngine::Options(),
2830                                            fuzzing_event_engine::Actions());
2831 };
2832 
TEST_F(ExternalAccountCredentialsTest,Success)2833 TEST_F(ExternalAccountCredentialsTest, Success) {
2834   ExecCtx exec_ctx;
2835   Json credential_source = Json::FromString("");
2836   TestExternalAccountCredentials::ServiceAccountImpersonation
2837       service_account_impersonation;
2838   service_account_impersonation.token_lifetime_seconds = 3600;
2839   TestExternalAccountCredentials::Options options = {
2840       "external_account",                 // type;
2841       "audience",                         // audience;
2842       "subject_token_type",               // subject_token_type;
2843       "",                                 // service_account_impersonation_url;
2844       service_account_impersonation,      // service_account_impersonation;
2845       "https://foo.com:5555/token",       // token_url;
2846       "https://foo.com:5555/token_info",  // token_info_url;
2847       credential_source,                  // credential_source;
2848       "quota_project_id",                 // quota_project_id;
2849       "client_id",                        // client_id;
2850       "client_secret",                    // client_secret;
2851       "",                                 // workforce_pool_user_project;
2852   };
2853   auto creds = MakeRefCounted<TestExternalAccountCredentials>(
2854       options, std::vector<std::string>(), event_engine_);
2855   // Check security level.
2856   EXPECT_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
2857   // First request: http post should be called.
2858   auto state = RequestMetadataState::NewInstance(
2859       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
2860   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
2861                            external_account_creds_httpcli_post_success,
2862                            httpcli_put_should_not_be_called);
2863   state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority,
2864                                 kTestPath);
2865   ExecCtx::Get()->Flush();
2866   event_engine_->TickUntilIdle();
2867   // Second request: the cached token should be served directly.
2868   state = RequestMetadataState::NewInstance(
2869       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
2870   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
2871                            httpcli_post_should_not_be_called,
2872                            httpcli_put_should_not_be_called);
2873   state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority,
2874                                 kTestPath);
2875   ExecCtx::Get()->Flush();
2876   event_engine_->TickUntilIdle();
2877   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
2878 }
2879 
TEST_F(ExternalAccountCredentialsTest,SuccessWithUrlEncode)2880 TEST_F(ExternalAccountCredentialsTest, SuccessWithUrlEncode) {
2881   std::map<std::string, std::string> emd = {
2882       {"authorization", "Bearer token_exchange_access_token"}};
2883   ExecCtx exec_ctx;
2884   Json credential_source = Json::FromString("");
2885   TestExternalAccountCredentials::ServiceAccountImpersonation
2886       service_account_impersonation;
2887   service_account_impersonation.token_lifetime_seconds = 3600;
2888   TestExternalAccountCredentials::Options options = {
2889       "external_account",             // type;
2890       "audience_!@#$",                // audience;
2891       "subject_token_type_!@#$",      // subject_token_type;
2892       "",                             // service_account_impersonation_url;
2893       service_account_impersonation,  // service_account_impersonation;
2894       "https://foo.com:5555/token_url_encode",  // token_url;
2895       "https://foo.com:5555/token_info",        // token_info_url;
2896       credential_source,                        // credential_source;
2897       "quota_project_id",                       // quota_project_id;
2898       "client_id",                              // client_id;
2899       "client_secret",                          // client_secret;
2900       "",                                       // workforce_pool_user_project;
2901   };
2902   auto creds = MakeRefCounted<TestExternalAccountCredentials>(
2903       options, std::vector<std::string>(), event_engine_);
2904   auto state = RequestMetadataState::NewInstance(
2905       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
2906   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
2907                            external_account_creds_httpcli_post_success,
2908                            httpcli_put_should_not_be_called);
2909   state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority,
2910                                 kTestPath);
2911   ExecCtx::Get()->Flush();
2912   event_engine_->TickUntilIdle();
2913   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
2914 }
2915 
TEST_F(ExternalAccountCredentialsTest,SuccessWithServiceAccountImpersonation)2916 TEST_F(ExternalAccountCredentialsTest, SuccessWithServiceAccountImpersonation) {
2917   ExecCtx exec_ctx;
2918   Json credential_source = Json::FromString("");
2919   TestExternalAccountCredentials::ServiceAccountImpersonation
2920       service_account_impersonation;
2921   service_account_impersonation.token_lifetime_seconds = 3600;
2922   TestExternalAccountCredentials::Options options = {
2923       "external_account",    // type;
2924       "audience",            // audience;
2925       "subject_token_type",  // subject_token_type;
2926       "https://foo.com:5555/service_account_impersonation",  // service_account_impersonation_url;
2927       service_account_impersonation,      // service_account_impersonation;
2928       "https://foo.com:5555/token",       // token_url;
2929       "https://foo.com:5555/token_info",  // token_info_url;
2930       credential_source,                  // credential_source;
2931       "quota_project_id",                 // quota_project_id;
2932       "client_id",                        // client_id;
2933       "client_secret",                    // client_secret;
2934       "",                                 // workforce_pool_user_project;
2935   };
2936   auto creds = MakeRefCounted<TestExternalAccountCredentials>(
2937       options, std::vector<std::string>{"scope_1", "scope_2"}, event_engine_);
2938   // Check security level.
2939   EXPECT_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
2940   // First request: http put should be called.
2941   auto state = RequestMetadataState::NewInstance(
2942       absl::OkStatus(),
2943       "authorization: Bearer service_account_impersonation_access_token");
2944   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
2945                            external_account_creds_httpcli_post_success,
2946                            httpcli_put_should_not_be_called);
2947   state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority,
2948                                 kTestPath);
2949   ExecCtx::Get()->Flush();
2950   event_engine_->TickUntilIdle();
2951   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
2952 }
2953 
TEST_F(ExternalAccountCredentialsTest,SuccessWithServiceAccountImpersonationAndCustomTokenLifetime)2954 TEST_F(ExternalAccountCredentialsTest,
2955        SuccessWithServiceAccountImpersonationAndCustomTokenLifetime) {
2956   ExecCtx exec_ctx;
2957   Json credential_source = Json::FromString("");
2958   TestExternalAccountCredentials::ServiceAccountImpersonation
2959       service_account_impersonation;
2960   service_account_impersonation.token_lifetime_seconds = 1800;
2961   TestExternalAccountCredentials::Options options = {
2962       "external_account",    // type;
2963       "audience",            // audience;
2964       "subject_token_type",  // subject_token_type;
2965       "https://foo.com:5555/service_account_impersonation",  // service_account_impersonation_url;
2966       service_account_impersonation,      // service_account_impersonation;
2967       "https://foo.com:5555/token",       // token_url;
2968       "https://foo.com:5555/token_info",  // token_info_url;
2969       credential_source,                  // credential_source;
2970       "quota_project_id",                 // quota_project_id;
2971       "client_id",                        // client_id;
2972       "client_secret",                    // client_secret;
2973       "",                                 // workforce_pool_user_project;
2974   };
2975   auto creds = MakeRefCounted<TestExternalAccountCredentials>(
2976       options, std::vector<std::string>{"scope_1", "scope_2"}, event_engine_);
2977   // Check security level.
2978   EXPECT_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
2979   // First request: http put should be called.
2980   auto state = RequestMetadataState::NewInstance(
2981       absl::OkStatus(),
2982       "authorization: Bearer service_account_impersonation_access_token");
2983   HttpRequest::SetOverride(
2984       httpcli_get_should_not_be_called,
2985       external_acc_creds_serv_acc_imp_custom_lifetime_httpcli_post_success,
2986       httpcli_put_should_not_be_called);
2987   state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority,
2988                                 kTestPath);
2989   ExecCtx::Get()->Flush();
2990   event_engine_->TickUntilIdle();
2991   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
2992 }
2993 
TEST_F(ExternalAccountCredentialsTest,FailureWithServiceAccountImpersonationAndInvalidCustomTokenLifetime)2994 TEST_F(ExternalAccountCredentialsTest,
2995        FailureWithServiceAccountImpersonationAndInvalidCustomTokenLifetime) {
2996   const char* options_string1 =
2997       "{\"type\":\"external_account\",\"audience\":\"audience\","
2998       "\"subject_token_type\":\"subject_token_type\","
2999       "\"service_account_impersonation_url\":\"service_account_impersonation_"
3000       "url\",\"service_account_impersonation\":"
3001       "{\"token_lifetime_seconds\":599},"
3002       "\"token_url\":\"https://foo.com:5555/token\","
3003       "\"token_info_url\":\"https://foo.com:5555/token_info\","
3004       "\"credential_source\":{\"url\":\"https://foo.com:5555/"
3005       "generate_subject_token_format_json\","
3006       "\"headers\":{\"Metadata-Flavor\":\"Google\"},"
3007       "\"format\":{\"type\":\"json\",\"subject_token_field_name\":\"access_"
3008       "token\"}},\"quota_project_id\":\"quota_project_id\","
3009       "\"client_id\":\"client_id\",\"client_secret\":\"client_secret\"}";
3010   auto json = JsonParse(options_string1);
3011   ASSERT_TRUE(json.ok()) << json.status();
3012   auto creds = ExternalAccountCredentials::Create(*json, {"scope1", "scope2"});
3013   std::string actual_error;
3014   grpc_error_get_str(creds.status(), StatusStrProperty::kDescription,
3015                      &actual_error);
3016   EXPECT_EQ("token_lifetime_seconds must be more than 600s", actual_error);
3017 
3018   const char* options_string2 =
3019       "{\"type\":\"external_account\",\"audience\":\"audience\","
3020       "\"subject_token_type\":\"subject_token_type\","
3021       "\"service_account_impersonation_url\":\"service_account_impersonation_"
3022       "url\",\"service_account_impersonation\":"
3023       "{\"token_lifetime_seconds\":43201},"
3024       "\"token_url\":\"https://foo.com:5555/token\","
3025       "\"token_info_url\":\"https://foo.com:5555/token_info\","
3026       "\"credential_source\":{\"url\":\"https://foo.com:5555/"
3027       "generate_subject_token_format_json\","
3028       "\"headers\":{\"Metadata-Flavor\":\"Google\"},"
3029       "\"format\":{\"type\":\"json\",\"subject_token_field_name\":\"access_"
3030       "token\"}},\"quota_project_id\":\"quota_project_id\","
3031       "\"client_id\":\"client_id\",\"client_secret\":\"client_secret\"}";
3032   json = JsonParse(options_string2);
3033   ASSERT_TRUE(json.ok()) << json.status();
3034   creds = ExternalAccountCredentials::Create(*json, {"scope1", "scope2"});
3035   grpc_error_get_str(creds.status(), StatusStrProperty::kDescription,
3036                      &actual_error);
3037   EXPECT_EQ("token_lifetime_seconds must be less than 43200s", actual_error);
3038 }
3039 
TEST_F(ExternalAccountCredentialsTest,FailureInvalidTokenUrl)3040 TEST_F(ExternalAccountCredentialsTest, FailureInvalidTokenUrl) {
3041   ExecCtx exec_ctx;
3042   Json credential_source = Json::FromString("");
3043   TestExternalAccountCredentials::ServiceAccountImpersonation
3044       service_account_impersonation;
3045   service_account_impersonation.token_lifetime_seconds = 3600;
3046   TestExternalAccountCredentials::Options options = {
3047       "external_account",    // type;
3048       "audience",            // audience;
3049       "subject_token_type",  // subject_token_type;
3050       "https://foo.com:5555/service_account_impersonation",  // service_account_impersonation_url;
3051       service_account_impersonation,      // service_account_impersonation;
3052       "invalid_token_url",                // token_url;
3053       "https://foo.com:5555/token_info",  // token_info_url;
3054       credential_source,                  // credential_source;
3055       "quota_project_id",                 // quota_project_id;
3056       "client_id",                        // client_id;
3057       "client_secret",                    // client_secret;
3058       "",                                 // workforce_pool_user_project;
3059   };
3060   auto creds = MakeRefCounted<TestExternalAccountCredentials>(
3061       options, std::vector<std::string>(), event_engine_);
3062   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
3063                            httpcli_post_should_not_be_called,
3064                            httpcli_put_should_not_be_called);
3065   // TODO(roth): This should return UNAUTHENTICATED.
3066   grpc_error_handle expected_error = absl::UnknownError(
3067       "error fetching oauth2 token: Invalid token url: "
3068       "invalid_token_url. Error: INVALID_ARGUMENT: Could not parse "
3069       "'scheme' from uri 'invalid_token_url'. Scheme not found.");
3070   auto state = RequestMetadataState::NewInstance(expected_error, {});
3071   state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority,
3072                                 kTestPath);
3073   ExecCtx::Get()->Flush();
3074   event_engine_->TickUntilIdle();
3075   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3076 }
3077 
TEST_F(ExternalAccountCredentialsTest,FailureInvalidServiceAccountImpersonationUrl)3078 TEST_F(ExternalAccountCredentialsTest,
3079        FailureInvalidServiceAccountImpersonationUrl) {
3080   ExecCtx exec_ctx;
3081   Json credential_source = Json::FromString("");
3082   TestExternalAccountCredentials::ServiceAccountImpersonation
3083       service_account_impersonation;
3084   service_account_impersonation.token_lifetime_seconds = 3600;
3085   TestExternalAccountCredentials::Options options = {
3086       "external_account",                           // type;
3087       "audience",                                   // audience;
3088       "subject_token_type",                         // subject_token_type;
3089       "invalid_service_account_impersonation_url",  // service_account_impersonation_url;
3090       service_account_impersonation,      // service_account_impersonation;
3091       "https://foo.com:5555/token",       // token_url;
3092       "https://foo.com:5555/token_info",  // token_info_url;
3093       credential_source,                  // credential_source;
3094       "quota_project_id",                 // quota_project_id;
3095       "client_id",                        // client_id;
3096       "client_secret",                    // client_secret;
3097       "",                                 // workforce_pool_user_project;
3098   };
3099   auto creds = MakeRefCounted<TestExternalAccountCredentials>(
3100       options, std::vector<std::string>(), event_engine_);
3101   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
3102                            external_account_creds_httpcli_post_success,
3103                            httpcli_put_should_not_be_called);
3104   // TODO(roth): This should return UNAUTHENTICATED.
3105   grpc_error_handle expected_error = absl::UnknownError(
3106       "error fetching oauth2 token: Invalid service account impersonation url: "
3107       "invalid_service_account_impersonation_url. Error: INVALID_ARGUMENT: "
3108       "Could not parse 'scheme' from uri "
3109       "'invalid_service_account_impersonation_url'. Scheme not found.");
3110   auto state = RequestMetadataState::NewInstance(expected_error, {});
3111   state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority,
3112                                 kTestPath);
3113   ExecCtx::Get()->Flush();
3114   event_engine_->TickUntilIdle();
3115   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3116 }
3117 
TEST_F(ExternalAccountCredentialsTest,FailureTokenExchangeResponseMissingAccessToken)3118 TEST_F(ExternalAccountCredentialsTest,
3119        FailureTokenExchangeResponseMissingAccessToken) {
3120   ExecCtx exec_ctx;
3121   Json credential_source = Json::FromString("");
3122   TestExternalAccountCredentials::ServiceAccountImpersonation
3123       service_account_impersonation;
3124   service_account_impersonation.token_lifetime_seconds = 3600;
3125   TestExternalAccountCredentials::Options options = {
3126       "external_account",    // type;
3127       "audience",            // audience;
3128       "subject_token_type",  // subject_token_type;
3129       "https://foo.com:5555/service_account_impersonation",  // service_account_impersonation_url;
3130       service_account_impersonation,      // service_account_impersonation;
3131       "https://foo.com:5555/token",       // token_url;
3132       "https://foo.com:5555/token_info",  // token_info_url;
3133       credential_source,                  // credential_source
3134       "quota_project_id",                 // quota_project_id;
3135       "client_id",                        // client_id;
3136       "client_secret",                    // client_secret;
3137       "",                                 // workforce_pool_user_project;
3138   };
3139   auto creds = MakeRefCounted<TestExternalAccountCredentials>(
3140       options, std::vector<std::string>(), event_engine_);
3141   HttpRequest::SetOverride(
3142       httpcli_get_should_not_be_called,
3143       external_account_creds_httpcli_post_failure_token_exchange_response_missing_access_token,
3144       httpcli_put_should_not_be_called);
3145   // TODO(roth): This should return UNAUTHENTICATED.
3146   grpc_error_handle expected_error = absl::UnknownError(
3147       "error fetching oauth2 token: Missing or invalid access_token in "
3148       "{\"not_access_token\":\"not_access_token\",\"expires_in\":3599, "
3149       "\"token_type\":\"Bearer\"}.");
3150   auto state = RequestMetadataState::NewInstance(expected_error, {});
3151   state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority,
3152                                 kTestPath);
3153   ExecCtx::Get()->Flush();
3154   event_engine_->TickUntilIdle();
3155   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3156 }
3157 
TEST_F(ExternalAccountCredentialsTest,UrlExternalAccountCredsSuccessFormatText)3158 TEST_F(ExternalAccountCredentialsTest,
3159        UrlExternalAccountCredsSuccessFormatText) {
3160   ExecCtx exec_ctx;
3161   auto credential_source = JsonParse(
3162       valid_url_external_account_creds_options_credential_source_format_text);
3163   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3164   TestExternalAccountCredentials::ServiceAccountImpersonation
3165       service_account_impersonation;
3166   service_account_impersonation.token_lifetime_seconds = 3600;
3167   ExternalAccountCredentials::Options options = {
3168       "external_account",                 // type;
3169       "audience",                         // audience;
3170       "subject_token_type",               // subject_token_type;
3171       "",                                 // service_account_impersonation_url;
3172       service_account_impersonation,      // service_account_impersonation;
3173       "https://foo.com:5555/token",       // token_url;
3174       "https://foo.com:5555/token_info",  // token_info_url;
3175       *credential_source,                 // credential_source;
3176       "quota_project_id",                 // quota_project_id;
3177       "client_id",                        // client_id;
3178       "client_secret",                    // client_secret;
3179       "",                                 // workforce_pool_user_project;
3180   };
3181   auto creds =
3182       UrlExternalAccountCredentials::Create(options, {}, event_engine_);
3183   ASSERT_TRUE(creds.ok()) << creds.status();
3184   ASSERT_NE(*creds, nullptr);
3185   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
3186   auto state = RequestMetadataState::NewInstance(
3187       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
3188   HttpRequest::SetOverride(url_external_account_creds_httpcli_get_success,
3189                            external_account_creds_httpcli_post_success,
3190                            httpcli_put_should_not_be_called);
3191   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3192                                 kTestPath);
3193   ExecCtx::Get()->Flush();
3194   event_engine_->TickUntilIdle();
3195   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3196 }
3197 
TEST_F(ExternalAccountCredentialsTest,UrlExternalAccountCredsSuccessWithQueryParamsFormatText)3198 TEST_F(ExternalAccountCredentialsTest,
3199        UrlExternalAccountCredsSuccessWithQueryParamsFormatText) {
3200   std::map<std::string, std::string> emd = {
3201       {"authorization", "Bearer token_exchange_access_token"}};
3202   ExecCtx exec_ctx;
3203   auto credential_source = JsonParse(
3204       valid_url_external_account_creds_options_credential_source_with_query_params_format_text);
3205   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3206   TestExternalAccountCredentials::ServiceAccountImpersonation
3207       service_account_impersonation;
3208   service_account_impersonation.token_lifetime_seconds = 3600;
3209   ExternalAccountCredentials::Options options = {
3210       "external_account",                 // type;
3211       "audience",                         // audience;
3212       "subject_token_type",               // subject_token_type;
3213       "",                                 // service_account_impersonation_url;
3214       service_account_impersonation,      // service_account_impersonation;
3215       "https://foo.com:5555/token",       // token_url;
3216       "https://foo.com:5555/token_info",  // token_info_url;
3217       *credential_source,                 // credential_source;
3218       "quota_project_id",                 // quota_project_id;
3219       "client_id",                        // client_id;
3220       "client_secret",                    // client_secret;
3221       "",                                 // workforce_pool_user_project;
3222   };
3223   auto creds =
3224       UrlExternalAccountCredentials::Create(options, {}, event_engine_);
3225   ASSERT_TRUE(creds.ok()) << creds.status();
3226   ASSERT_NE(*creds, nullptr);
3227   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
3228   auto state = RequestMetadataState::NewInstance(
3229       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
3230   HttpRequest::SetOverride(url_external_account_creds_httpcli_get_success,
3231                            external_account_creds_httpcli_post_success,
3232                            httpcli_put_should_not_be_called);
3233   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3234                                 kTestPath);
3235   ExecCtx::Get()->Flush();
3236   event_engine_->TickUntilIdle();
3237   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3238 }
3239 
TEST_F(ExternalAccountCredentialsTest,UrlExternalAccountCredsSuccessFormatJson)3240 TEST_F(ExternalAccountCredentialsTest,
3241        UrlExternalAccountCredsSuccessFormatJson) {
3242   ExecCtx exec_ctx;
3243   auto credential_source = JsonParse(
3244       valid_url_external_account_creds_options_credential_source_format_json);
3245   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3246   TestExternalAccountCredentials::ServiceAccountImpersonation
3247       service_account_impersonation;
3248   service_account_impersonation.token_lifetime_seconds = 3600;
3249   ExternalAccountCredentials::Options options = {
3250       "external_account",                 // type;
3251       "audience",                         // audience;
3252       "subject_token_type",               // subject_token_type;
3253       "",                                 // service_account_impersonation_url;
3254       service_account_impersonation,      // service_account_impersonation;
3255       "https://foo.com:5555/token",       // token_url;
3256       "https://foo.com:5555/token_info",  // token_info_url;
3257       *credential_source,                 // credential_source;
3258       "quota_project_id",                 // quota_project_id;
3259       "client_id",                        // client_id;
3260       "client_secret",                    // client_secret;
3261       "",                                 // workforce_pool_user_project;
3262   };
3263   auto creds =
3264       UrlExternalAccountCredentials::Create(options, {}, event_engine_);
3265   ASSERT_TRUE(creds.ok()) << creds.status();
3266   ASSERT_NE(*creds, nullptr);
3267   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
3268   auto state = RequestMetadataState::NewInstance(
3269       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
3270   HttpRequest::SetOverride(url_external_account_creds_httpcli_get_success,
3271                            external_account_creds_httpcli_post_success,
3272                            httpcli_put_should_not_be_called);
3273   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3274                                 kTestPath);
3275   ExecCtx::Get()->Flush();
3276   event_engine_->TickUntilIdle();
3277   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3278 }
3279 
TEST_F(ExternalAccountCredentialsTest,UrlExternalAccountCredsFailureInvalidCredentialSourceUrl)3280 TEST_F(ExternalAccountCredentialsTest,
3281        UrlExternalAccountCredsFailureInvalidCredentialSourceUrl) {
3282   auto credential_source =
3283       JsonParse(invalid_url_external_account_creds_options_credential_source);
3284   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3285   TestExternalAccountCredentials::ServiceAccountImpersonation
3286       service_account_impersonation;
3287   service_account_impersonation.token_lifetime_seconds = 3600;
3288   ExternalAccountCredentials::Options options = {
3289       "external_account",                 // type;
3290       "audience",                         // audience;
3291       "subject_token_type",               // subject_token_type;
3292       "",                                 // service_account_impersonation_url;
3293       service_account_impersonation,      // service_account_impersonation;
3294       "https://foo.com:5555/token",       // token_url;
3295       "https://foo.com:5555/token_info",  // token_info_url;
3296       *credential_source,                 // credential_source;
3297       "quota_project_id",                 // quota_project_id;
3298       "client_id",                        // client_id;
3299       "client_secret",                    // client_secret;
3300       "",                                 // workforce_pool_user_project;
3301   };
3302   auto creds = UrlExternalAccountCredentials::Create(options, {});
3303   ASSERT_FALSE(creds.ok());
3304   std::string actual_error;
3305   grpc_error_get_str(creds.status(), StatusStrProperty::kDescription,
3306                      &actual_error);
3307   EXPECT_THAT(actual_error,
3308               ::testing::StartsWith("Invalid credential source url."));
3309 }
3310 
TEST_F(ExternalAccountCredentialsTest,FileExternalAccountCredsSuccessFormatText)3311 TEST_F(ExternalAccountCredentialsTest,
3312        FileExternalAccountCredsSuccessFormatText) {
3313   ExecCtx exec_ctx;
3314   char* subject_token_path = write_tmp_jwt_file("test_subject_token");
3315   auto credential_source = JsonParse(absl::StrFormat(
3316       "{\"file\":\"%s\"}",
3317       absl::StrReplaceAll(subject_token_path, {{"\\", "\\\\"}})));
3318   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3319   TestExternalAccountCredentials::ServiceAccountImpersonation
3320       service_account_impersonation;
3321   service_account_impersonation.token_lifetime_seconds = 3600;
3322   ExternalAccountCredentials::Options options = {
3323       "external_account",                 // type;
3324       "audience",                         // audience;
3325       "subject_token_type",               // subject_token_type;
3326       "",                                 // service_account_impersonation_url;
3327       service_account_impersonation,      // service_account_impersonation;
3328       "https://foo.com:5555/token",       // token_url;
3329       "https://foo.com:5555/token_info",  // token_info_url;
3330       *credential_source,                 // credential_source;
3331       "quota_project_id",                 // quota_project_id;
3332       "client_id",                        // client_id;
3333       "client_secret",                    // client_secret;
3334       "",                                 // workforce_pool_user_project;
3335   };
3336   auto creds =
3337       FileExternalAccountCredentials::Create(options, {}, event_engine_);
3338   ASSERT_TRUE(creds.ok()) << creds.status();
3339   ASSERT_NE(*creds, nullptr);
3340   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
3341   auto state = RequestMetadataState::NewInstance(
3342       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
3343   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
3344                            external_account_creds_httpcli_post_success,
3345                            httpcli_put_should_not_be_called);
3346   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3347                                 kTestPath);
3348   ExecCtx::Get()->Flush();
3349   event_engine_->TickUntilIdle();
3350   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3351   gpr_free(subject_token_path);
3352 }
3353 
TEST_F(ExternalAccountCredentialsTest,FileExternalAccountCredsSuccessFormatJson)3354 TEST_F(ExternalAccountCredentialsTest,
3355        FileExternalAccountCredsSuccessFormatJson) {
3356   ExecCtx exec_ctx;
3357   char* subject_token_path =
3358       write_tmp_jwt_file("{\"access_token\":\"test_subject_token\"}");
3359   auto credential_source = JsonParse(absl::StrFormat(
3360       "{\n"
3361       "\"file\":\"%s\",\n"
3362       "\"format\":\n"
3363       "{\n"
3364       "\"type\":\"json\",\n"
3365       "\"subject_token_field_name\":\"access_token\"\n"
3366       "}\n"
3367       "}",
3368       absl::StrReplaceAll(subject_token_path, {{"\\", "\\\\"}})));
3369   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3370   TestExternalAccountCredentials::ServiceAccountImpersonation
3371       service_account_impersonation;
3372   service_account_impersonation.token_lifetime_seconds = 3600;
3373   ExternalAccountCredentials::Options options = {
3374       "external_account",                 // type;
3375       "audience",                         // audience;
3376       "subject_token_type",               // subject_token_type;
3377       "",                                 // service_account_impersonation_url;
3378       service_account_impersonation,      // service_account_impersonation;
3379       "https://foo.com:5555/token",       // token_url;
3380       "https://foo.com:5555/token_info",  // token_info_url;
3381       *credential_source,                 // credential_source;
3382       "quota_project_id",                 // quota_project_id;
3383       "client_id",                        // client_id;
3384       "client_secret",                    // client_secret;
3385       "",                                 // workforce_pool_user_project;
3386   };
3387   auto creds =
3388       FileExternalAccountCredentials::Create(options, {}, event_engine_);
3389   ASSERT_TRUE(creds.ok()) << creds.status();
3390   ASSERT_NE(*creds, nullptr);
3391   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
3392   auto state = RequestMetadataState::NewInstance(
3393       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
3394   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
3395                            external_account_creds_httpcli_post_success,
3396                            httpcli_put_should_not_be_called);
3397   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3398                                 kTestPath);
3399   ExecCtx::Get()->Flush();
3400   event_engine_->TickUntilIdle();
3401   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3402   gpr_free(subject_token_path);
3403 }
3404 
TEST_F(ExternalAccountCredentialsTest,FileExternalAccountCredsFailureFileNotFound)3405 TEST_F(ExternalAccountCredentialsTest,
3406        FileExternalAccountCredsFailureFileNotFound) {
3407   ExecCtx exec_ctx;
3408   auto credential_source = JsonParse("{\"file\":\"non_exisiting_file\"}");
3409   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3410   TestExternalAccountCredentials::ServiceAccountImpersonation
3411       service_account_impersonation;
3412   service_account_impersonation.token_lifetime_seconds = 3600;
3413   ExternalAccountCredentials::Options options = {
3414       "external_account",                 // type;
3415       "audience",                         // audience;
3416       "subject_token_type",               // subject_token_type;
3417       "",                                 // service_account_impersonation_url;
3418       service_account_impersonation,      // service_account_impersonation;
3419       "https://foo.com:5555/token",       // token_url;
3420       "https://foo.com:5555/token_info",  // token_info_url;
3421       *credential_source,                 // credential_source;
3422       "quota_project_id",                 // quota_project_id;
3423       "client_id",                        // client_id;
3424       "client_secret",                    // client_secret;
3425       "",                                 // workforce_pool_user_project;
3426   };
3427   auto creds =
3428       FileExternalAccountCredentials::Create(options, {}, event_engine_);
3429   ASSERT_TRUE(creds.ok()) << creds.status();
3430   ASSERT_NE(*creds, nullptr);
3431   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
3432                            httpcli_post_should_not_be_called,
3433                            httpcli_put_should_not_be_called);
3434   // TODO(roth): This should return UNAVAILABLE.
3435   grpc_error_handle expected_error = absl::InternalError(
3436       "error fetching oauth2 token: Failed to load file: "
3437       "non_exisiting_file due to error(fdopen): No such file or directory");
3438   auto state = RequestMetadataState::NewInstance(expected_error, {});
3439   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3440                                 kTestPath);
3441   ExecCtx::Get()->Flush();
3442   event_engine_->TickUntilIdle();
3443   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3444 }
3445 
TEST_F(ExternalAccountCredentialsTest,FileExternalAccountCredsFailureInvalidJsonContent)3446 TEST_F(ExternalAccountCredentialsTest,
3447        FileExternalAccountCredsFailureInvalidJsonContent) {
3448   ExecCtx exec_ctx;
3449   char* subject_token_path = write_tmp_jwt_file("not_a_valid_json_file");
3450   auto credential_source = JsonParse(absl::StrFormat(
3451       "{\n"
3452       "\"file\":\"%s\",\n"
3453       "\"format\":\n"
3454       "{\n"
3455       "\"type\":\"json\",\n"
3456       "\"subject_token_field_name\":\"access_token\"\n"
3457       "}\n"
3458       "}",
3459       absl::StrReplaceAll(subject_token_path, {{"\\", "\\\\"}})));
3460   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3461   TestExternalAccountCredentials::ServiceAccountImpersonation
3462       service_account_impersonation;
3463   service_account_impersonation.token_lifetime_seconds = 3600;
3464   ExternalAccountCredentials::Options options = {
3465       "external_account",                 // type;
3466       "audience",                         // audience;
3467       "subject_token_type",               // subject_token_type;
3468       "",                                 // service_account_impersonation_url;
3469       service_account_impersonation,      // service_account_impersonation;
3470       "https://foo.com:5555/token",       // token_url;
3471       "https://foo.com:5555/token_info",  // token_info_url;
3472       *credential_source,                 // credential_source;
3473       "quota_project_id",                 // quota_project_id;
3474       "client_id",                        // client_id;
3475       "client_secret",                    // client_secret;
3476       "",                                 // workforce_pool_user_project;
3477   };
3478   auto creds =
3479       FileExternalAccountCredentials::Create(options, {}, event_engine_);
3480   ASSERT_TRUE(creds.ok()) << creds.status();
3481   ASSERT_NE(*creds, nullptr);
3482   HttpRequest::SetOverride(httpcli_get_should_not_be_called,
3483                            httpcli_post_should_not_be_called,
3484                            httpcli_put_should_not_be_called);
3485   // TODO(roth): This should return UNAUTHENTICATED.
3486   grpc_error_handle expected_error = absl::UnknownError(
3487       "error fetching oauth2 token: The content of the file is not a "
3488       "valid json object.");
3489   auto state = RequestMetadataState::NewInstance(expected_error, {});
3490   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3491                                 kTestPath);
3492   ExecCtx::Get()->Flush();
3493   event_engine_->TickUntilIdle();
3494   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3495   gpr_free(subject_token_path);
3496 }
3497 
TEST_F(ExternalAccountCredentialsTest,AwsExternalAccountCredsSuccess)3498 TEST_F(ExternalAccountCredentialsTest, AwsExternalAccountCredsSuccess) {
3499   ExecCtx exec_ctx;
3500   auto credential_source =
3501       JsonParse(valid_aws_external_account_creds_options_credential_source);
3502   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3503   TestExternalAccountCredentials::ServiceAccountImpersonation
3504       service_account_impersonation;
3505   service_account_impersonation.token_lifetime_seconds = 3600;
3506   ExternalAccountCredentials::Options options = {
3507       "external_account",                 // type;
3508       "audience",                         // audience;
3509       "subject_token_type",               // subject_token_type;
3510       "",                                 // service_account_impersonation_url;
3511       service_account_impersonation,      // service_account_impersonation;
3512       "https://foo.com:5555/token",       // token_url;
3513       "https://foo.com:5555/token_info",  // token_info_url;
3514       *credential_source,                 // credential_source;
3515       "quota_project_id",                 // quota_project_id;
3516       "client_id",                        // client_id;
3517       "client_secret",                    // client_secret;
3518       "",                                 // workforce_pool_user_project;
3519   };
3520   auto creds =
3521       AwsExternalAccountCredentials::Create(options, {}, event_engine_);
3522   ASSERT_TRUE(creds.ok()) << creds.status();
3523   ASSERT_NE(*creds, nullptr);
3524   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
3525   auto state = RequestMetadataState::NewInstance(
3526       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
3527   HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success,
3528                            aws_external_account_creds_httpcli_post_success,
3529                            httpcli_put_should_not_be_called);
3530   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3531                                 kTestPath);
3532   ExecCtx::Get()->Flush();
3533   event_engine_->TickUntilIdle();
3534   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3535 }
3536 
TEST_F(ExternalAccountCredentialsTest,AwsImdsv2ExternalAccountCredsSuccess)3537 TEST_F(ExternalAccountCredentialsTest, AwsImdsv2ExternalAccountCredsSuccess) {
3538   ExecCtx exec_ctx;
3539   auto credential_source = JsonParse(
3540       valid_aws_imdsv2_external_account_creds_options_credential_source);
3541   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3542   TestExternalAccountCredentials::ServiceAccountImpersonation
3543       service_account_impersonation;
3544   service_account_impersonation.token_lifetime_seconds = 3600;
3545   ExternalAccountCredentials::Options options = {
3546       "external_account",                 // type;
3547       "audience",                         // audience;
3548       "subject_token_type",               // subject_token_type;
3549       "",                                 // service_account_impersonation_url;
3550       service_account_impersonation,      // service_account_impersonation;
3551       "https://foo.com:5555/token",       // token_url;
3552       "https://foo.com:5555/token_info",  // token_info_url;
3553       *credential_source,                 // credential_source;
3554       "quota_project_id",                 // quota_project_id;
3555       "client_id",                        // client_id;
3556       "client_secret",                    // client_secret;
3557       "",                                 // workforce_pool_user_project;
3558   };
3559   auto creds =
3560       AwsExternalAccountCredentials::Create(options, {}, event_engine_);
3561   ASSERT_TRUE(creds.ok()) << creds.status();
3562   ASSERT_NE(*creds, nullptr);
3563   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
3564   auto state = RequestMetadataState::NewInstance(
3565       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
3566   HttpRequest::SetOverride(
3567       aws_imdsv2_external_account_creds_httpcli_get_success,
3568       aws_external_account_creds_httpcli_post_success,
3569       aws_imdsv2_external_account_creds_httpcli_put_success);
3570   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3571                                 kTestPath);
3572   ExecCtx::Get()->Flush();
3573   event_engine_->TickUntilIdle();
3574   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3575 }
3576 
TEST_F(ExternalAccountCredentialsTest,AwsImdsv2ExternalAccountCredShouldNotUseMetadataServer)3577 TEST_F(ExternalAccountCredentialsTest,
3578        AwsImdsv2ExternalAccountCredShouldNotUseMetadataServer) {
3579   ExecCtx exec_ctx;
3580   SetEnv("AWS_REGION", "test_regionz");
3581   SetEnv("AWS_ACCESS_KEY_ID", "test_access_key_id");
3582   SetEnv("AWS_SECRET_ACCESS_KEY", "test_secret_access_key");
3583   SetEnv("AWS_SESSION_TOKEN", "test_token");
3584   auto credential_source = JsonParse(
3585       valid_aws_imdsv2_external_account_creds_options_credential_source);
3586   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3587   TestExternalAccountCredentials::ServiceAccountImpersonation
3588       service_account_impersonation;
3589   service_account_impersonation.token_lifetime_seconds = 3600;
3590   ExternalAccountCredentials::Options options = {
3591       "external_account",                 // type;
3592       "audience",                         // audience;
3593       "subject_token_type",               // subject_token_type;
3594       "",                                 // service_account_impersonation_url;
3595       service_account_impersonation,      // service_account_impersonation;
3596       "https://foo.com:5555/token",       // token_url;
3597       "https://foo.com:5555/token_info",  // token_info_url;
3598       *credential_source,                 // credential_source;
3599       "quota_project_id",                 // quota_project_id;
3600       "client_id",                        // client_id;
3601       "client_secret",                    // client_secret;
3602       "",                                 // workforce_pool_user_project;
3603   };
3604   auto creds =
3605       AwsExternalAccountCredentials::Create(options, {}, event_engine_);
3606   ASSERT_TRUE(creds.ok()) << creds.status();
3607   ASSERT_NE(*creds, nullptr);
3608   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
3609   auto state = RequestMetadataState::NewInstance(
3610       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
3611   HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success,
3612                            aws_external_account_creds_httpcli_post_success,
3613                            httpcli_put_should_not_be_called);
3614   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3615                                 kTestPath);
3616   ExecCtx::Get()->Flush();
3617   event_engine_->TickUntilIdle();
3618   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3619   UnsetEnv("AWS_REGION");
3620   UnsetEnv("AWS_ACCESS_KEY_ID");
3621   UnsetEnv("AWS_SECRET_ACCESS_KEY");
3622   UnsetEnv("AWS_SESSION_TOKEN");
3623 }
3624 
TEST_F(ExternalAccountCredentialsTest,AwsImdsv2ExternalAccountCredShouldNotUseMetadataServerOptionalTokenMissing)3625 TEST_F(
3626     ExternalAccountCredentialsTest,
3627     AwsImdsv2ExternalAccountCredShouldNotUseMetadataServerOptionalTokenMissing) {
3628   ExecCtx exec_ctx;
3629   SetEnv("AWS_REGION", "test_regionz");
3630   SetEnv("AWS_ACCESS_KEY_ID", "test_access_key_id");
3631   SetEnv("AWS_SECRET_ACCESS_KEY", "test_secret_access_key");
3632   auto credential_source = JsonParse(
3633       valid_aws_imdsv2_external_account_creds_options_credential_source);
3634   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3635   TestExternalAccountCredentials::ServiceAccountImpersonation
3636       service_account_impersonation;
3637   service_account_impersonation.token_lifetime_seconds = 3600;
3638   ExternalAccountCredentials::Options options = {
3639       "external_account",                 // type;
3640       "audience",                         // audience;
3641       "subject_token_type",               // subject_token_type;
3642       "",                                 // service_account_impersonation_url;
3643       service_account_impersonation,      // service_account_impersonation;
3644       "https://foo.com:5555/token",       // token_url;
3645       "https://foo.com:5555/token_info",  // token_info_url;
3646       *credential_source,                 // credential_source;
3647       "quota_project_id",                 // quota_project_id;
3648       "client_id",                        // client_id;
3649       "client_secret",                    // client_secret;
3650       "",                                 // workforce_pool_user_project;
3651   };
3652   auto creds =
3653       AwsExternalAccountCredentials::Create(options, {}, event_engine_);
3654   ASSERT_TRUE(creds.ok()) << creds.status();
3655   ASSERT_NE(*creds, nullptr);
3656   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
3657   auto state = RequestMetadataState::NewInstance(
3658       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
3659   HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success,
3660                            aws_external_account_creds_httpcli_post_success,
3661                            httpcli_put_should_not_be_called);
3662   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3663                                 kTestPath);
3664   ExecCtx::Get()->Flush();
3665   event_engine_->TickUntilIdle();
3666   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3667   UnsetEnv("AWS_REGION");
3668   UnsetEnv("AWS_ACCESS_KEY_ID");
3669   UnsetEnv("AWS_SECRET_ACCESS_KEY");
3670 }
3671 
TEST_F(ExternalAccountCredentialsTest,AwsExternalAccountCredsSuccessIpv6)3672 TEST_F(ExternalAccountCredentialsTest, AwsExternalAccountCredsSuccessIpv6) {
3673   ExecCtx exec_ctx;
3674   auto credential_source = JsonParse(
3675       valid_aws_external_account_creds_options_credential_source_ipv6);
3676   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3677   TestExternalAccountCredentials::ServiceAccountImpersonation
3678       service_account_impersonation;
3679   service_account_impersonation.token_lifetime_seconds = 3600;
3680   ExternalAccountCredentials::Options options = {
3681       "external_account",                 // type;
3682       "audience",                         // audience;
3683       "subject_token_type",               // subject_token_type;
3684       "",                                 // service_account_impersonation_url;
3685       service_account_impersonation,      // service_account_impersonation;
3686       "https://foo.com:5555/token",       // token_url;
3687       "https://foo.com:5555/token_info",  // token_info_url;
3688       *credential_source,                 // credential_source;
3689       "quota_project_id",                 // quota_project_id;
3690       "client_id",                        // client_id;
3691       "client_secret",                    // client_secret;
3692       "",                                 // workforce_pool_user_project;
3693   };
3694   auto creds =
3695       AwsExternalAccountCredentials::Create(options, {}, event_engine_);
3696   ASSERT_TRUE(creds.ok()) << creds.status();
3697   ASSERT_NE(*creds, nullptr);
3698   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
3699   auto state = RequestMetadataState::NewInstance(
3700       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
3701   HttpRequest::SetOverride(
3702       aws_imdsv2_external_account_creds_httpcli_get_success,
3703       aws_external_account_creds_httpcli_post_success,
3704       aws_imdsv2_external_account_creds_httpcli_put_success);
3705   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3706                                 kTestPath);
3707   ExecCtx::Get()->Flush();
3708   event_engine_->TickUntilIdle();
3709   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3710 }
3711 
TEST_F(ExternalAccountCredentialsTest,AwsExternalAccountCredsSuccessPathRegionEnvKeysUrl)3712 TEST_F(ExternalAccountCredentialsTest,
3713        AwsExternalAccountCredsSuccessPathRegionEnvKeysUrl) {
3714   ExecCtx exec_ctx;
3715   SetEnv("AWS_REGION", "test_regionz");
3716   auto credential_source =
3717       JsonParse(valid_aws_external_account_creds_options_credential_source);
3718   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3719   TestExternalAccountCredentials::ServiceAccountImpersonation
3720       service_account_impersonation;
3721   service_account_impersonation.token_lifetime_seconds = 3600;
3722   ExternalAccountCredentials::Options options = {
3723       "external_account",                 // type;
3724       "audience",                         // audience;
3725       "subject_token_type",               // subject_token_type;
3726       "",                                 // service_account_impersonation_url;
3727       service_account_impersonation,      // service_account_impersonation;
3728       "https://foo.com:5555/token",       // token_url;
3729       "https://foo.com:5555/token_info",  // token_info_url;
3730       *credential_source,                 // credential_source;
3731       "quota_project_id",                 // quota_project_id;
3732       "client_id",                        // client_id;
3733       "client_secret",                    // client_secret;
3734       "",                                 // workforce_pool_user_project;
3735   };
3736   auto creds =
3737       AwsExternalAccountCredentials::Create(options, {}, event_engine_);
3738   ASSERT_TRUE(creds.ok()) << creds.status();
3739   ASSERT_NE(*creds, nullptr);
3740   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
3741   auto state = RequestMetadataState::NewInstance(
3742       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
3743   HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success,
3744                            aws_external_account_creds_httpcli_post_success,
3745                            httpcli_put_should_not_be_called);
3746   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3747                                 kTestPath);
3748   ExecCtx::Get()->Flush();
3749   event_engine_->TickUntilIdle();
3750   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3751   UnsetEnv("AWS_REGION");
3752 }
3753 
TEST_F(ExternalAccountCredentialsTest,AwsExternalAccountCredsSuccessPathDefaultRegionEnvKeysUrl)3754 TEST_F(ExternalAccountCredentialsTest,
3755        AwsExternalAccountCredsSuccessPathDefaultRegionEnvKeysUrl) {
3756   ExecCtx exec_ctx;
3757   SetEnv("AWS_DEFAULT_REGION", "test_regionz");
3758   auto credential_source =
3759       JsonParse(valid_aws_external_account_creds_options_credential_source);
3760   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3761   TestExternalAccountCredentials::ServiceAccountImpersonation
3762       service_account_impersonation;
3763   service_account_impersonation.token_lifetime_seconds = 3600;
3764   ExternalAccountCredentials::Options options = {
3765       "external_account",                 // type;
3766       "audience",                         // audience;
3767       "subject_token_type",               // subject_token_type;
3768       "",                                 // service_account_impersonation_url;
3769       service_account_impersonation,      // service_account_impersonation;
3770       "https://foo.com:5555/token",       // token_url;
3771       "https://foo.com:5555/token_info",  // token_info_url;
3772       *credential_source,                 // credential_source;
3773       "quota_project_id",                 // quota_project_id;
3774       "client_id",                        // client_id;
3775       "client_secret",                    // client_secret;
3776       "",                                 // workforce_pool_user_project;
3777   };
3778   auto creds =
3779       AwsExternalAccountCredentials::Create(options, {}, event_engine_);
3780   ASSERT_TRUE(creds.ok()) << creds.status();
3781   ASSERT_NE(*creds, nullptr);
3782   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
3783   auto state = RequestMetadataState::NewInstance(
3784       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
3785   HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success,
3786                            aws_external_account_creds_httpcli_post_success,
3787                            httpcli_put_should_not_be_called);
3788   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3789                                 kTestPath);
3790   ExecCtx::Get()->Flush();
3791   event_engine_->TickUntilIdle();
3792   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3793   UnsetEnv("AWS_DEFAULT_REGION");
3794 }
3795 
TEST_F(ExternalAccountCredentialsTest,AwsExternalAccountCredsSuccessPathDuplicateRegionEnvKeysUrl)3796 TEST_F(ExternalAccountCredentialsTest,
3797        AwsExternalAccountCredsSuccessPathDuplicateRegionEnvKeysUrl) {
3798   ExecCtx exec_ctx;
3799   // Make sure that AWS_REGION gets used over AWS_DEFAULT_REGION
3800   SetEnv("AWS_REGION", "test_regionz");
3801   SetEnv("AWS_DEFAULT_REGION", "ERROR_REGION");
3802   auto credential_source =
3803       JsonParse(valid_aws_external_account_creds_options_credential_source);
3804   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3805   TestExternalAccountCredentials::ServiceAccountImpersonation
3806       service_account_impersonation;
3807   service_account_impersonation.token_lifetime_seconds = 3600;
3808   ExternalAccountCredentials::Options options = {
3809       "external_account",                 // type;
3810       "audience",                         // audience;
3811       "subject_token_type",               // subject_token_type;
3812       "",                                 // service_account_impersonation_url;
3813       service_account_impersonation,      // service_account_impersonation;
3814       "https://foo.com:5555/token",       // token_url;
3815       "https://foo.com:5555/token_info",  // token_info_url;
3816       *credential_source,                 // credential_source;
3817       "quota_project_id",                 // quota_project_id;
3818       "client_id",                        // client_id;
3819       "client_secret",                    // client_secret;
3820       "",                                 // workforce_pool_user_project;
3821   };
3822   auto creds =
3823       AwsExternalAccountCredentials::Create(options, {}, event_engine_);
3824   ASSERT_TRUE(creds.ok()) << creds.status();
3825   ASSERT_NE(*creds, nullptr);
3826   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
3827   auto state = RequestMetadataState::NewInstance(
3828       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
3829   HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success,
3830                            aws_external_account_creds_httpcli_post_success,
3831                            httpcli_put_should_not_be_called);
3832   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3833                                 kTestPath);
3834   ExecCtx::Get()->Flush();
3835   event_engine_->TickUntilIdle();
3836   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3837   UnsetEnv("AWS_REGION");
3838   UnsetEnv("AWS_DEFAULT_REGION");
3839 }
3840 
TEST_F(ExternalAccountCredentialsTest,AwsExternalAccountCredsSuccessPathRegionUrlKeysEnv)3841 TEST_F(ExternalAccountCredentialsTest,
3842        AwsExternalAccountCredsSuccessPathRegionUrlKeysEnv) {
3843   ExecCtx exec_ctx;
3844   SetEnv("AWS_ACCESS_KEY_ID", "test_access_key_id");
3845   SetEnv("AWS_SECRET_ACCESS_KEY", "test_secret_access_key");
3846   SetEnv("AWS_SESSION_TOKEN", "test_token");
3847   auto credential_source =
3848       JsonParse(valid_aws_external_account_creds_options_credential_source);
3849   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3850   TestExternalAccountCredentials::ServiceAccountImpersonation
3851       service_account_impersonation;
3852   service_account_impersonation.token_lifetime_seconds = 3600;
3853   ExternalAccountCredentials::Options options = {
3854       "external_account",                 // type;
3855       "audience",                         // audience;
3856       "subject_token_type",               // subject_token_type;
3857       "",                                 // service_account_impersonation_url;
3858       service_account_impersonation,      // service_account_impersonation;
3859       "https://foo.com:5555/token",       // token_url;
3860       "https://foo.com:5555/token_info",  // token_info_url;
3861       *credential_source,                 // credential_source;
3862       "quota_project_id",                 // quota_project_id;
3863       "client_id",                        // client_id;
3864       "client_secret",                    // client_secret;
3865       "",                                 // workforce_pool_user_project;
3866   };
3867   auto creds =
3868       AwsExternalAccountCredentials::Create(options, {}, event_engine_);
3869   ASSERT_TRUE(creds.ok()) << creds.status();
3870   ASSERT_NE(*creds, nullptr);
3871   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
3872   auto state = RequestMetadataState::NewInstance(
3873       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
3874   HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success,
3875                            aws_external_account_creds_httpcli_post_success,
3876                            httpcli_put_should_not_be_called);
3877   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3878                                 kTestPath);
3879   ExecCtx::Get()->Flush();
3880   event_engine_->TickUntilIdle();
3881   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3882   UnsetEnv("AWS_ACCESS_KEY_ID");
3883   UnsetEnv("AWS_SECRET_ACCESS_KEY");
3884   UnsetEnv("AWS_SESSION_TOKEN");
3885 }
3886 
TEST_F(ExternalAccountCredentialsTest,AwsExternalAccountCredsSuccessPathRegionEnvKeysEnv)3887 TEST_F(ExternalAccountCredentialsTest,
3888        AwsExternalAccountCredsSuccessPathRegionEnvKeysEnv) {
3889   ExecCtx exec_ctx;
3890   SetEnv("AWS_REGION", "test_regionz");
3891   SetEnv("AWS_ACCESS_KEY_ID", "test_access_key_id");
3892   SetEnv("AWS_SECRET_ACCESS_KEY", "test_secret_access_key");
3893   SetEnv("AWS_SESSION_TOKEN", "test_token");
3894   auto credential_source =
3895       JsonParse(valid_aws_external_account_creds_options_credential_source);
3896   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3897   TestExternalAccountCredentials::ServiceAccountImpersonation
3898       service_account_impersonation;
3899   service_account_impersonation.token_lifetime_seconds = 3600;
3900   ExternalAccountCredentials::Options options = {
3901       "external_account",                 // type;
3902       "audience",                         // audience;
3903       "subject_token_type",               // subject_token_type;
3904       "",                                 // service_account_impersonation_url;
3905       service_account_impersonation,      // service_account_impersonation;
3906       "https://foo.com:5555/token",       // token_url;
3907       "https://foo.com:5555/token_info",  // token_info_url;
3908       *credential_source,                 // credential_source;
3909       "quota_project_id",                 // quota_project_id;
3910       "client_id",                        // client_id;
3911       "client_secret",                    // client_secret;
3912       "",                                 // workforce_pool_user_project;
3913   };
3914   auto creds =
3915       AwsExternalAccountCredentials::Create(options, {}, event_engine_);
3916   ASSERT_TRUE(creds.ok()) << creds.status();
3917   ASSERT_NE(*creds, nullptr);
3918   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
3919   auto state = RequestMetadataState::NewInstance(
3920       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
3921   HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success,
3922                            aws_external_account_creds_httpcli_post_success,
3923                            httpcli_put_should_not_be_called);
3924   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3925                                 kTestPath);
3926   ExecCtx::Get()->Flush();
3927   event_engine_->TickUntilIdle();
3928   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3929   UnsetEnv("AWS_REGION");
3930   UnsetEnv("AWS_ACCESS_KEY_ID");
3931   UnsetEnv("AWS_SECRET_ACCESS_KEY");
3932   UnsetEnv("AWS_SESSION_TOKEN");
3933 }
3934 
TEST_F(ExternalAccountCredentialsTest,AwsExternalAccountCredsSuccessPathDefaultRegionEnvKeysEnv)3935 TEST_F(ExternalAccountCredentialsTest,
3936        AwsExternalAccountCredsSuccessPathDefaultRegionEnvKeysEnv) {
3937   std::map<std::string, std::string> emd = {
3938       {"authorization", "Bearer token_exchange_access_token"}};
3939   ExecCtx exec_ctx;
3940   SetEnv("AWS_DEFAULT_REGION", "test_regionz");
3941   SetEnv("AWS_ACCESS_KEY_ID", "test_access_key_id");
3942   SetEnv("AWS_SECRET_ACCESS_KEY", "test_secret_access_key");
3943   SetEnv("AWS_SESSION_TOKEN", "test_token");
3944   auto credential_source =
3945       JsonParse(valid_aws_external_account_creds_options_credential_source);
3946   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3947   TestExternalAccountCredentials::ServiceAccountImpersonation
3948       service_account_impersonation;
3949   service_account_impersonation.token_lifetime_seconds = 3600;
3950   ExternalAccountCredentials::Options options = {
3951       "external_account",                 // type;
3952       "audience",                         // audience;
3953       "subject_token_type",               // subject_token_type;
3954       "",                                 // service_account_impersonation_url;
3955       service_account_impersonation,      // service_account_impersonation;
3956       "https://foo.com:5555/token",       // token_url;
3957       "https://foo.com:5555/token_info",  // token_info_url;
3958       *credential_source,                 // credential_source;
3959       "quota_project_id",                 // quota_project_id;
3960       "client_id",                        // client_id;
3961       "client_secret",                    // client_secret;
3962       "",                                 // workforce_pool_user_project;
3963   };
3964   auto creds =
3965       AwsExternalAccountCredentials::Create(options, {}, event_engine_);
3966   ASSERT_TRUE(creds.ok()) << creds.status();
3967   ASSERT_NE(*creds, nullptr);
3968   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
3969   auto state = RequestMetadataState::NewInstance(
3970       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
3971   HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success,
3972                            aws_external_account_creds_httpcli_post_success,
3973                            httpcli_put_should_not_be_called);
3974   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
3975                                 kTestPath);
3976   ExecCtx::Get()->Flush();
3977   event_engine_->TickUntilIdle();
3978   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
3979   UnsetEnv("AWS_DEFAULT_REGION");
3980   UnsetEnv("AWS_ACCESS_KEY_ID");
3981   UnsetEnv("AWS_SECRET_ACCESS_KEY");
3982   UnsetEnv("AWS_SESSION_TOKEN");
3983 }
3984 
TEST_F(ExternalAccountCredentialsTest,AwsExternalAccountCredsSuccessPathDuplicateRegionEnvKeysEnv)3985 TEST_F(ExternalAccountCredentialsTest,
3986        AwsExternalAccountCredsSuccessPathDuplicateRegionEnvKeysEnv) {
3987   ExecCtx exec_ctx;
3988   // Make sure that AWS_REGION gets used over AWS_DEFAULT_REGION
3989   SetEnv("AWS_REGION", "test_regionz");
3990   SetEnv("AWS_DEFAULT_REGION", "ERROR_REGION");
3991   SetEnv("AWS_ACCESS_KEY_ID", "test_access_key_id");
3992   SetEnv("AWS_SECRET_ACCESS_KEY", "test_secret_access_key");
3993   SetEnv("AWS_SESSION_TOKEN", "test_token");
3994   auto credential_source =
3995       JsonParse(valid_aws_external_account_creds_options_credential_source);
3996   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
3997   TestExternalAccountCredentials::ServiceAccountImpersonation
3998       service_account_impersonation;
3999   service_account_impersonation.token_lifetime_seconds = 3600;
4000   ExternalAccountCredentials::Options options = {
4001       "external_account",                 // type;
4002       "audience",                         // audience;
4003       "subject_token_type",               // subject_token_type;
4004       "",                                 // service_account_impersonation_url;
4005       service_account_impersonation,      // service_account_impersonation;
4006       "https://foo.com:5555/token",       // token_url;
4007       "https://foo.com:5555/token_info",  // token_info_url;
4008       *credential_source,                 // credential_source;
4009       "quota_project_id",                 // quota_project_id;
4010       "client_id",                        // client_id;
4011       "client_secret",                    // client_secret;
4012       "",                                 // workforce_pool_user_project;
4013   };
4014   auto creds =
4015       AwsExternalAccountCredentials::Create(options, {}, event_engine_);
4016   ASSERT_TRUE(creds.ok()) << creds.status();
4017   ASSERT_NE(*creds, nullptr);
4018   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
4019   auto state = RequestMetadataState::NewInstance(
4020       absl::OkStatus(), "authorization: Bearer token_exchange_access_token");
4021   HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success,
4022                            aws_external_account_creds_httpcli_post_success,
4023                            httpcli_put_should_not_be_called);
4024   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
4025                                 kTestPath);
4026   ExecCtx::Get()->Flush();
4027   event_engine_->TickUntilIdle();
4028   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
4029   UnsetEnv("AWS_REGION");
4030   UnsetEnv("AWS_DEFAULT_REGION");
4031   UnsetEnv("AWS_ACCESS_KEY_ID");
4032   UnsetEnv("AWS_SECRET_ACCESS_KEY");
4033   UnsetEnv("AWS_SESSION_TOKEN");
4034 }
4035 
TEST_F(ExternalAccountCredentialsTest,CreateSuccess)4036 TEST_F(ExternalAccountCredentialsTest, CreateSuccess) {
4037   // url credentials
4038   const char* url_options_string =
4039       "{\"type\":\"external_account\",\"audience\":\"audience\",\"subject_"
4040       "token_type\":\"subject_token_type\",\"service_account_impersonation_"
4041       "url\":\"service_account_impersonation_url\","
4042       "\"token_url\":\"https://foo.com:5555/"
4043       "token\",\"token_info_url\":\"https://foo.com:5555/"
4044       "token_info\",\"credential_source\":{\"url\":\"https://foo.com:5555/"
4045       "generate_subject_token_format_json\",\"headers\":{\"Metadata-Flavor\":"
4046       "\"Google\"},\"format\":{\"type\":\"json\",\"subject_token_field_name\":"
4047       "\"access_token\"}},\"quota_project_id\":\"quota_"
4048       "project_id\",\"client_id\":\"client_id\",\"client_secret\":\"client_"
4049       "secret\"}";
4050   const char* url_scopes_string = "scope1,scope2";
4051   grpc_call_credentials* url_creds = grpc_external_account_credentials_create(
4052       url_options_string, url_scopes_string);
4053   ASSERT_NE(url_creds, nullptr);
4054   url_creds->Unref();
4055   // file credentials
4056   const char* file_options_string =
4057       "{\"type\":\"external_account\",\"audience\":\"audience\",\"subject_"
4058       "token_type\":\"subject_token_type\",\"service_account_impersonation_"
4059       "url\":\"service_account_impersonation_url\","
4060       "\"token_url\":\"https://foo.com:5555/"
4061       "token\",\"token_info_url\":\"https://foo.com:5555/"
4062       "token_info\",\"credential_source\":{\"file\":\"credentials_file_path\"},"
4063       "\"quota_project_id\":\"quota_"
4064       "project_id\",\"client_id\":\"client_id\",\"client_secret\":\"client_"
4065       "secret\"}";
4066   const char* file_scopes_string = "scope1,scope2";
4067   grpc_call_credentials* file_creds = grpc_external_account_credentials_create(
4068       file_options_string, file_scopes_string);
4069   ASSERT_NE(file_creds, nullptr);
4070   file_creds->Unref();
4071   // aws credentials
4072   const char* aws_options_string =
4073       "{\"type\":\"external_account\",\"audience\":\"audience\",\"subject_"
4074       "token_type\":\"subject_token_type\",\"service_account_impersonation_"
4075       "url\":\"service_account_impersonation_url\","
4076       "\"token_url\":\"https://"
4077       "foo.com:5555/token\",\"token_info_url\":\"https://foo.com:5555/"
4078       "token_info\",\"credential_source\":{\"environment_id\":\"aws1\","
4079       "\"region_url\":\"https://169.254.169.254:5555/"
4080       "region_url\",\"url\":\"https://"
4081       "169.254.169.254:5555/url\",\"regional_cred_verification_url\":\"https://"
4082       "foo.com:5555/regional_cred_verification_url_{region}\"},"
4083       "\"quota_project_id\":\"quota_"
4084       "project_id\",\"client_id\":\"client_id\",\"client_secret\":\"client_"
4085       "secret\"}";
4086   const char* aws_scopes_string = "scope1,scope2";
4087   grpc_call_credentials* aws_creds = grpc_external_account_credentials_create(
4088       aws_options_string, aws_scopes_string);
4089   ASSERT_NE(aws_creds, nullptr);
4090   aws_creds->Unref();
4091 }
4092 
TEST_F(ExternalAccountCredentialsTest,AwsExternalAccountCredsFailureUnmatchedEnvironmentId)4093 TEST_F(ExternalAccountCredentialsTest,
4094        AwsExternalAccountCredsFailureUnmatchedEnvironmentId) {
4095   auto credential_source = JsonParse(
4096       invalid_aws_external_account_creds_options_credential_source_unmatched_environment_id);
4097   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
4098   TestExternalAccountCredentials::ServiceAccountImpersonation
4099       service_account_impersonation;
4100   service_account_impersonation.token_lifetime_seconds = 3600;
4101   ExternalAccountCredentials::Options options = {
4102       "external_account",                 // type;
4103       "audience",                         // audience;
4104       "subject_token_type",               // subject_token_type;
4105       "",                                 // service_account_impersonation_url;
4106       service_account_impersonation,      // service_account_impersonation;
4107       "https://foo.com:5555/token",       // token_url;
4108       "https://foo.com:5555/token_info",  // token_info_url;
4109       *credential_source,                 // credential_source;
4110       "quota_project_id",                 // quota_project_id;
4111       "client_id",                        // client_id;
4112       "client_secret",                    // client_secret;
4113       "",                                 // workforce_pool_user_project;
4114   };
4115   auto creds =
4116       AwsExternalAccountCredentials::Create(options, {}, event_engine_);
4117   ASSERT_FALSE(creds.ok());
4118   std::string actual_error;
4119   grpc_error_get_str(creds.status(), StatusStrProperty::kDescription,
4120                      &actual_error);
4121   EXPECT_EQ("environment_id does not match.", actual_error);
4122 }
4123 
TEST_F(ExternalAccountCredentialsTest,AwsExternalAccountCredsFailureInvalidRegionalCredVerificationUrl)4124 TEST_F(ExternalAccountCredentialsTest,
4125        AwsExternalAccountCredsFailureInvalidRegionalCredVerificationUrl) {
4126   ExecCtx exec_ctx;
4127   auto credential_source = JsonParse(
4128       invalid_aws_external_account_creds_options_credential_source_invalid_regional_cred_verification_url);
4129   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
4130   TestExternalAccountCredentials::ServiceAccountImpersonation
4131       service_account_impersonation;
4132   service_account_impersonation.token_lifetime_seconds = 3600;
4133   ExternalAccountCredentials::Options options = {
4134       "external_account",                 // type;
4135       "audience",                         // audience;
4136       "subject_token_type",               // subject_token_type;
4137       "",                                 // service_account_impersonation_url;
4138       service_account_impersonation,      // service_account_impersonation;
4139       "https://foo.com:5555/token",       // token_url;
4140       "https://foo.com:5555/token_info",  // token_info_url;
4141       *credential_source,                 // credential_source;
4142       "quota_project_id",                 // quota_project_id;
4143       "client_id",                        // client_id;
4144       "client_secret",                    // client_secret;
4145       "",                                 // workforce_pool_user_project;
4146   };
4147   auto creds =
4148       AwsExternalAccountCredentials::Create(options, {}, event_engine_);
4149   ASSERT_TRUE(creds.ok()) << creds.status();
4150   ASSERT_NE(*creds, nullptr);
4151   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
4152   // TODO(roth): This should return UNAUTHENTICATED.
4153   grpc_error_handle expected_error = absl::UnknownError(
4154       "error fetching oauth2 token: Creating aws request signer failed.");
4155   auto state = RequestMetadataState::NewInstance(expected_error, {});
4156   HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success,
4157                            aws_external_account_creds_httpcli_post_success,
4158                            httpcli_put_should_not_be_called);
4159   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
4160                                 kTestPath);
4161   ExecCtx::Get()->Flush();
4162   event_engine_->TickUntilIdle();
4163   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
4164 }
4165 
TEST_F(ExternalAccountCredentialsTest,AwsExternalAccountCredsFailureMissingRoleName)4166 TEST_F(ExternalAccountCredentialsTest,
4167        AwsExternalAccountCredsFailureMissingRoleName) {
4168   ExecCtx exec_ctx;
4169   auto credential_source = JsonParse(
4170       invalid_aws_external_account_creds_options_credential_source_missing_role_name);
4171   ASSERT_TRUE(credential_source.ok()) << credential_source.status();
4172   TestExternalAccountCredentials::ServiceAccountImpersonation
4173       service_account_impersonation;
4174   service_account_impersonation.token_lifetime_seconds = 3600;
4175   ExternalAccountCredentials::Options options = {
4176       "external_account",                 // type;
4177       "audience",                         // audience;
4178       "subject_token_type",               // subject_token_type;
4179       "",                                 // service_account_impersonation_url;
4180       service_account_impersonation,      // service_account_impersonation;
4181       "https://foo.com:5555/token",       // token_url;
4182       "https://foo.com:5555/token_info",  // token_info_url;
4183       *credential_source,                 // credential_source;
4184       "quota_project_id",                 // quota_project_id;
4185       "client_id",                        // client_id;
4186       "client_secret",                    // client_secret;
4187       "",                                 // workforce_pool_user_project;
4188   };
4189   auto creds =
4190       AwsExternalAccountCredentials::Create(options, {}, event_engine_);
4191   ASSERT_TRUE(creds.ok()) << creds.status();
4192   ASSERT_NE(*creds, nullptr);
4193   EXPECT_EQ((*creds)->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
4194   // TODO(roth): This should return UNAUTHENTICATED.
4195   grpc_error_handle expected_error = absl::UnknownError(
4196       "error fetching oauth2 token: "
4197       "Missing role name when retrieving signing keys.");
4198   auto state = RequestMetadataState::NewInstance(expected_error, {});
4199   HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success,
4200                            aws_external_account_creds_httpcli_post_success,
4201                            httpcli_put_should_not_be_called);
4202   state->RunRequestMetadataTest(creds->get(), kTestUrlScheme, kTestAuthority,
4203                                 kTestPath);
4204   ExecCtx::Get()->Flush();
4205   event_engine_->TickUntilIdle();
4206   HttpRequest::SetOverride(nullptr, nullptr, nullptr);
4207 }
4208 
TEST_F(ExternalAccountCredentialsTest,CreateFailureInvalidJsonFormat)4209 TEST_F(ExternalAccountCredentialsTest, CreateFailureInvalidJsonFormat) {
4210   const char* options_string = "invalid_json";
4211   grpc_call_credentials* creds =
4212       grpc_external_account_credentials_create(options_string, "");
4213   EXPECT_EQ(creds, nullptr);
4214 }
4215 
TEST_F(ExternalAccountCredentialsTest,CreateFailureInvalidOptionsFormat)4216 TEST_F(ExternalAccountCredentialsTest, CreateFailureInvalidOptionsFormat) {
4217   const char* options_string = "{\"random_key\":\"random_value\"}";
4218   grpc_call_credentials* creds =
4219       grpc_external_account_credentials_create(options_string, "");
4220   EXPECT_EQ(creds, nullptr);
4221 }
4222 
TEST_F(ExternalAccountCredentialsTest,CreateFailureInvalidOptionsCredentialSource)4223 TEST_F(ExternalAccountCredentialsTest,
4224        CreateFailureInvalidOptionsCredentialSource) {
4225   const char* options_string =
4226       "{\"type\":\"external_account\",\"audience\":\"audience\",\"subject_"
4227       "token_type\":\"subject_token_type\",\"service_account_impersonation_"
4228       "url\":\"service_account_impersonation_url\","
4229       "\"token_url\":\"https://foo.com:5555/"
4230       "token\",\"token_info_url\":\"https://foo.com:5555/"
4231       "token_info\",\"credential_source\":{\"random_key\":\"random_value\"},"
4232       "\"quota_project_id\":\"quota_"
4233       "project_id\",\"client_id\":\"client_id\",\"client_secret\":\"client_"
4234       "secret\"}";
4235   grpc_call_credentials* creds =
4236       grpc_external_account_credentials_create(options_string, "");
4237   EXPECT_EQ(creds, nullptr);
4238 }
4239 
TEST_F(ExternalAccountCredentialsTest,CreateSuccessWorkforcePool)4240 TEST_F(ExternalAccountCredentialsTest, CreateSuccessWorkforcePool) {
4241   const char* url_options_string =
4242       "{\"type\":\"external_account\",\"audience\":\"//iam.googleapis.com/"
4243       "locations/location/workforcePools/pool/providers/provider\",\"subject_"
4244       "token_type\":\"subject_token_type\",\"service_account_impersonation_"
4245       "url\":\"service_account_impersonation_url\","
4246       "\"token_url\":\"https://foo.com:5555/"
4247       "token\",\"token_info_url\":\"https://foo.com:5555/"
4248       "token_info\",\"credential_source\":{\"url\":\"https://foo.com:5555/"
4249       "generate_subject_token_format_json\",\"headers\":{\"Metadata-Flavor\":"
4250       "\"Google\"},\"format\":{\"type\":\"json\",\"subject_token_field_name\":"
4251       "\"access_token\"}},\"quota_project_id\":\"quota_"
4252       "project_id\",\"client_id\":\"client_id\",\"client_secret\":\"client_"
4253       "secret\",\"workforce_pool_user_project\":\"workforce_pool_user_"
4254       "project\"}";
4255   const char* url_scopes_string = "scope1,scope2";
4256   grpc_call_credentials* url_creds = grpc_external_account_credentials_create(
4257       url_options_string, url_scopes_string);
4258   ASSERT_NE(url_creds, nullptr);
4259   url_creds->Unref();
4260 }
4261 
TEST_F(ExternalAccountCredentialsTest,CreateFailureInvalidWorkforcePoolAudience)4262 TEST_F(ExternalAccountCredentialsTest,
4263        CreateFailureInvalidWorkforcePoolAudience) {
4264   const char* url_options_string =
4265       "{\"type\":\"external_account\",\"audience\":\"invalid_workforce_pool_"
4266       "audience\",\"subject_"
4267       "token_type\":\"subject_token_type\",\"service_account_impersonation_"
4268       "url\":\"service_account_impersonation_url\","
4269       "\"token_url\":\"https://foo.com:5555/"
4270       "token\",\"token_info_url\":\"https://foo.com:5555/"
4271       "token_info\",\"credential_source\":{\"url\":\"https://foo.com:5555/"
4272       "generate_subject_token_format_json\",\"headers\":{\"Metadata-Flavor\":"
4273       "\"Google\"},\"format\":{\"type\":\"json\",\"subject_token_field_name\":"
4274       "\"access_token\"}},\"quota_project_id\":\"quota_"
4275       "project_id\",\"client_id\":\"client_id\",\"client_secret\":\"client_"
4276       "secret\",\"workforce_pool_user_project\":\"workforce_pool_user_"
4277       "project\"}";
4278   const char* url_scopes_string = "scope1,scope2";
4279   grpc_call_credentials* url_creds = grpc_external_account_credentials_create(
4280       url_options_string, url_scopes_string);
4281   ASSERT_EQ(url_creds, nullptr);
4282 }
4283 
TEST_F(CredentialsTest,TestInsecureCredentialsCompareSuccess)4284 TEST_F(CredentialsTest, TestInsecureCredentialsCompareSuccess) {
4285   auto insecure_creds_1 = grpc_insecure_credentials_create();
4286   auto insecure_creds_2 = grpc_insecure_credentials_create();
4287   ASSERT_EQ(insecure_creds_1->cmp(insecure_creds_2), 0);
4288   grpc_arg arg_1 = grpc_channel_credentials_to_arg(insecure_creds_1);
4289   grpc_channel_args args_1 = {1, &arg_1};
4290   grpc_arg arg_2 = grpc_channel_credentials_to_arg(insecure_creds_2);
4291   grpc_channel_args args_2 = {1, &arg_2};
4292   EXPECT_EQ(grpc_channel_args_compare(&args_1, &args_2), 0);
4293   grpc_channel_credentials_release(insecure_creds_1);
4294   grpc_channel_credentials_release(insecure_creds_2);
4295 }
4296 
TEST_F(CredentialsTest,TestInsecureCredentialsCompareFailure)4297 TEST_F(CredentialsTest, TestInsecureCredentialsCompareFailure) {
4298   auto* insecure_creds = grpc_insecure_credentials_create();
4299   auto* fake_creds = grpc_fake_transport_security_credentials_create();
4300   ASSERT_NE(insecure_creds->cmp(fake_creds), 0);
4301   ASSERT_NE(fake_creds->cmp(insecure_creds), 0);
4302   grpc_arg arg_1 = grpc_channel_credentials_to_arg(insecure_creds);
4303   grpc_channel_args args_1 = {1, &arg_1};
4304   grpc_arg arg_2 = grpc_channel_credentials_to_arg(fake_creds);
4305   grpc_channel_args args_2 = {1, &arg_2};
4306   EXPECT_NE(grpc_channel_args_compare(&args_1, &args_2), 0);
4307   grpc_channel_credentials_release(fake_creds);
4308   grpc_channel_credentials_release(insecure_creds);
4309 }
4310 
TEST_F(CredentialsTest,TestInsecureCredentialsSingletonCreate)4311 TEST_F(CredentialsTest, TestInsecureCredentialsSingletonCreate) {
4312   auto* insecure_creds_1 = grpc_insecure_credentials_create();
4313   auto* insecure_creds_2 = grpc_insecure_credentials_create();
4314   EXPECT_EQ(insecure_creds_1, insecure_creds_2);
4315 }
4316 
TEST_F(CredentialsTest,TestFakeCallCredentialsCompareSuccess)4317 TEST_F(CredentialsTest, TestFakeCallCredentialsCompareSuccess) {
4318   auto call_creds = MakeRefCounted<fake_call_creds>();
4319   CHECK_EQ(call_creds->cmp(call_creds.get()), 0);
4320 }
4321 
TEST_F(CredentialsTest,TestFakeCallCredentialsCompareFailure)4322 TEST_F(CredentialsTest, TestFakeCallCredentialsCompareFailure) {
4323   auto fake_creds = MakeRefCounted<fake_call_creds>();
4324   auto* md_creds = grpc_md_only_test_credentials_create("key", "value");
4325   CHECK_NE(fake_creds->cmp(md_creds), 0);
4326   CHECK_NE(md_creds->cmp(fake_creds.get()), 0);
4327   grpc_call_credentials_release(md_creds);
4328 }
4329 
TEST_F(CredentialsTest,TestHttpRequestSSLCredentialsCompare)4330 TEST_F(CredentialsTest, TestHttpRequestSSLCredentialsCompare) {
4331   auto creds_1 = CreateHttpRequestSSLCredentials();
4332   auto creds_2 = CreateHttpRequestSSLCredentials();
4333   EXPECT_EQ(creds_1->cmp(creds_2.get()), 0);
4334   EXPECT_EQ(creds_2->cmp(creds_1.get()), 0);
4335 }
4336 
TEST_F(CredentialsTest,TestHttpRequestSSLCredentialsSingleton)4337 TEST_F(CredentialsTest, TestHttpRequestSSLCredentialsSingleton) {
4338   auto creds_1 = CreateHttpRequestSSLCredentials();
4339   auto creds_2 = CreateHttpRequestSSLCredentials();
4340   EXPECT_EQ(creds_1, creds_2);
4341 }
4342 
TEST_F(CredentialsTest,TestCompositeChannelCredsCompareSuccess)4343 TEST_F(CredentialsTest, TestCompositeChannelCredsCompareSuccess) {
4344   auto* insecure_creds = grpc_insecure_credentials_create();
4345   auto fake_creds = MakeRefCounted<fake_call_creds>();
4346   auto* composite_creds_1 = grpc_composite_channel_credentials_create(
4347       insecure_creds, fake_creds.get(), nullptr);
4348   auto* composite_creds_2 = grpc_composite_channel_credentials_create(
4349       insecure_creds, fake_creds.get(), nullptr);
4350   EXPECT_EQ(composite_creds_1->cmp(composite_creds_2), 0);
4351   EXPECT_EQ(composite_creds_2->cmp(composite_creds_1), 0);
4352   grpc_channel_credentials_release(insecure_creds);
4353   grpc_channel_credentials_release(composite_creds_1);
4354   grpc_channel_credentials_release(composite_creds_2);
4355 }
4356 
TEST_F(CredentialsTest,RecursiveCompositeCredsDuplicateWithoutCallCreds)4357 TEST_F(CredentialsTest, RecursiveCompositeCredsDuplicateWithoutCallCreds) {
4358   auto* insecure_creds = grpc_insecure_credentials_create();
4359   auto inner_fake_creds = MakeRefCounted<fake_call_creds>();
4360   auto outer_fake_creds = MakeRefCounted<fake_call_creds>();
4361   auto* inner_composite_creds = grpc_composite_channel_credentials_create(
4362       insecure_creds, inner_fake_creds.get(), nullptr);
4363   auto* outer_composite_creds = grpc_composite_channel_credentials_create(
4364       inner_composite_creds, outer_fake_creds.get(), nullptr);
4365   auto duplicate_without_call_creds =
4366       outer_composite_creds->duplicate_without_call_credentials();
4367   EXPECT_EQ(duplicate_without_call_creds.get(), insecure_creds);
4368   grpc_channel_credentials_release(insecure_creds);
4369   grpc_channel_credentials_release(inner_composite_creds);
4370   grpc_channel_credentials_release(outer_composite_creds);
4371 }
4372 
TEST_F(CredentialsTest,TestCompositeChannelCredsCompareFailureDifferentChannelCreds)4373 TEST_F(CredentialsTest,
4374        TestCompositeChannelCredsCompareFailureDifferentChannelCreds) {
4375   auto* insecure_creds = grpc_insecure_credentials_create();
4376   auto* fake_channel_creds = grpc_fake_transport_security_credentials_create();
4377   auto fake_creds = MakeRefCounted<fake_call_creds>();
4378   auto* composite_creds_1 = grpc_composite_channel_credentials_create(
4379       insecure_creds, fake_creds.get(), nullptr);
4380   auto* composite_creds_2 = grpc_composite_channel_credentials_create(
4381       fake_channel_creds, fake_creds.get(), nullptr);
4382   EXPECT_NE(composite_creds_1->cmp(composite_creds_2), 0);
4383   EXPECT_NE(composite_creds_2->cmp(composite_creds_1), 0);
4384   grpc_channel_credentials_release(insecure_creds);
4385   grpc_channel_credentials_release(fake_channel_creds);
4386   grpc_channel_credentials_release(composite_creds_1);
4387   grpc_channel_credentials_release(composite_creds_2);
4388 }
4389 
TEST_F(CredentialsTest,TestCompositeChannelCredsCompareFailureDifferentCallCreds)4390 TEST_F(CredentialsTest,
4391        TestCompositeChannelCredsCompareFailureDifferentCallCreds) {
4392   auto* insecure_creds = grpc_insecure_credentials_create();
4393   auto fake_creds = MakeRefCounted<fake_call_creds>();
4394   auto* md_creds = grpc_md_only_test_credentials_create("key", "value");
4395   auto* composite_creds_1 = grpc_composite_channel_credentials_create(
4396       insecure_creds, fake_creds.get(), nullptr);
4397   auto* composite_creds_2 = grpc_composite_channel_credentials_create(
4398       insecure_creds, md_creds, nullptr);
4399   EXPECT_NE(composite_creds_1->cmp(composite_creds_2), 0);
4400   EXPECT_NE(composite_creds_2->cmp(composite_creds_1), 0);
4401   grpc_channel_credentials_release(insecure_creds);
4402   grpc_call_credentials_release(md_creds);
4403   grpc_channel_credentials_release(composite_creds_1);
4404   grpc_channel_credentials_release(composite_creds_2);
4405 }
4406 
TEST_F(CredentialsTest,TestTlsCredentialsCompareSuccess)4407 TEST_F(CredentialsTest, TestTlsCredentialsCompareSuccess) {
4408   auto* tls_creds_1 =
4409       grpc_tls_credentials_create(grpc_tls_credentials_options_create());
4410   auto* tls_creds_2 =
4411       grpc_tls_credentials_create(grpc_tls_credentials_options_create());
4412   EXPECT_EQ(tls_creds_1->cmp(tls_creds_2), 0);
4413   EXPECT_EQ(tls_creds_2->cmp(tls_creds_1), 0);
4414   grpc_channel_credentials_release(tls_creds_1);
4415   grpc_channel_credentials_release(tls_creds_2);
4416 }
4417 
TEST_F(CredentialsTest,TestTlsCredentialsWithVerifierCompareSuccess)4418 TEST_F(CredentialsTest, TestTlsCredentialsWithVerifierCompareSuccess) {
4419   auto* options_1 = grpc_tls_credentials_options_create();
4420   options_1->set_certificate_verifier(
4421       MakeRefCounted<HostNameCertificateVerifier>());
4422   auto* tls_creds_1 = grpc_tls_credentials_create(options_1);
4423   auto* options_2 = grpc_tls_credentials_options_create();
4424   options_2->set_certificate_verifier(
4425       MakeRefCounted<HostNameCertificateVerifier>());
4426   auto* tls_creds_2 = grpc_tls_credentials_create(options_2);
4427   EXPECT_EQ(tls_creds_1->cmp(tls_creds_2), 0);
4428   EXPECT_EQ(tls_creds_2->cmp(tls_creds_1), 0);
4429   grpc_channel_credentials_release(tls_creds_1);
4430   grpc_channel_credentials_release(tls_creds_2);
4431 }
4432 
TEST_F(CredentialsTest,TestTlsCredentialsCompareFailure)4433 TEST_F(CredentialsTest, TestTlsCredentialsCompareFailure) {
4434   auto* options_1 = grpc_tls_credentials_options_create();
4435   options_1->set_check_call_host(true);
4436   auto* tls_creds_1 = grpc_tls_credentials_create(options_1);
4437   auto* options_2 = grpc_tls_credentials_options_create();
4438   options_2->set_check_call_host(false);
4439   auto* tls_creds_2 = grpc_tls_credentials_create(options_2);
4440   EXPECT_NE(tls_creds_1->cmp(tls_creds_2), 0);
4441   EXPECT_NE(tls_creds_2->cmp(tls_creds_1), 0);
4442   grpc_channel_credentials_release(tls_creds_1);
4443   grpc_channel_credentials_release(tls_creds_2);
4444 }
4445 
TEST_F(CredentialsTest,TestTlsCredentialsWithVerifierCompareFailure)4446 TEST_F(CredentialsTest, TestTlsCredentialsWithVerifierCompareFailure) {
4447   auto* options_1 = grpc_tls_credentials_options_create();
4448   options_1->set_certificate_verifier(
4449       MakeRefCounted<HostNameCertificateVerifier>());
4450   auto* tls_creds_1 = grpc_tls_credentials_create(options_1);
4451   auto* options_2 = grpc_tls_credentials_options_create();
4452   grpc_tls_certificate_verifier_external verifier = {nullptr, nullptr, nullptr,
4453                                                      nullptr};
4454   options_2->set_certificate_verifier(
4455       MakeRefCounted<ExternalCertificateVerifier>(&verifier));
4456   auto* tls_creds_2 = grpc_tls_credentials_create(options_2);
4457   EXPECT_NE(tls_creds_1->cmp(tls_creds_2), 0);
4458   EXPECT_NE(tls_creds_2->cmp(tls_creds_1), 0);
4459   grpc_channel_credentials_release(tls_creds_1);
4460   grpc_channel_credentials_release(tls_creds_2);
4461 }
4462 
TEST_F(CredentialsTest,TestXdsCredentialsCompareSuccess)4463 TEST_F(CredentialsTest, TestXdsCredentialsCompareSuccess) {
4464   auto* insecure_creds = grpc_insecure_credentials_create();
4465   auto* xds_creds_1 = grpc_xds_credentials_create(insecure_creds);
4466   auto* xds_creds_2 = grpc_xds_credentials_create(insecure_creds);
4467   EXPECT_EQ(xds_creds_1->cmp(xds_creds_2), 0);
4468   EXPECT_EQ(xds_creds_2->cmp(xds_creds_1), 0);
4469   grpc_channel_credentials_release(insecure_creds);
4470   grpc_channel_credentials_release(xds_creds_1);
4471   grpc_channel_credentials_release(xds_creds_2);
4472 }
4473 
TEST_F(CredentialsTest,TestXdsCredentialsCompareFailure)4474 TEST_F(CredentialsTest, TestXdsCredentialsCompareFailure) {
4475   auto* insecure_creds = grpc_insecure_credentials_create();
4476   auto* fake_creds = grpc_fake_transport_security_credentials_create();
4477   auto* xds_creds_1 = grpc_xds_credentials_create(insecure_creds);
4478   auto* xds_creds_2 = grpc_xds_credentials_create(fake_creds);
4479   EXPECT_NE(xds_creds_1->cmp(xds_creds_2), 0);
4480   EXPECT_NE(xds_creds_2->cmp(xds_creds_1), 0);
4481   grpc_channel_credentials_release(insecure_creds);
4482   grpc_channel_credentials_release(fake_creds);
4483   grpc_channel_credentials_release(xds_creds_1);
4484   grpc_channel_credentials_release(xds_creds_2);
4485 }
4486 
4487 class GcpServiceAccountIdentityCredentialsTest : public ::testing::Test {
4488  protected:
SetUp()4489   void SetUp() override {
4490     grpc_init();
4491     g_http_status = 200;
4492     g_audience = "";
4493     g_token = nullptr;
4494     g_on_http_request_error = nullptr;
4495     HttpRequest::SetOverride(HttpGetOverride, httpcli_post_should_not_be_called,
4496                              httpcli_put_should_not_be_called);
4497   }
4498 
TearDown()4499   void TearDown() override {
4500     HttpRequest::SetOverride(nullptr, nullptr, nullptr);
4501     grpc_shutdown_blocking();
4502   }
4503 
ValidateHttpRequest(const grpc_http_request * request,const URI & uri)4504   static void ValidateHttpRequest(const grpc_http_request* request,
4505                                   const URI& uri) {
4506     EXPECT_EQ(uri.authority(), "metadata.google.internal.");
4507     EXPECT_EQ(uri.path(),
4508               "/computeMetadata/v1/instance/service-accounts/default/identity");
4509     EXPECT_THAT(
4510         uri.query_parameter_map(),
4511         ::testing::ElementsAre(::testing::Pair("audience", g_audience)));
4512     ASSERT_EQ(request->hdr_count, 1);
4513     EXPECT_EQ(absl::string_view(request->hdrs[0].key), "Metadata-Flavor");
4514     EXPECT_EQ(absl::string_view(request->hdrs[0].value), "Google");
4515   }
4516 
HttpGetOverride(const grpc_http_request * request,const URI & uri,Timestamp,grpc_closure * on_done,grpc_http_response * response)4517   static int HttpGetOverride(const grpc_http_request* request, const URI& uri,
4518                              Timestamp /*deadline*/, grpc_closure* on_done,
4519                              grpc_http_response* response) {
4520     // Validate request.
4521     ValidateHttpRequest(request, uri);
4522     // Generate response.
4523     *response = http_response(g_http_status, g_token == nullptr ? "" : g_token);
4524     ExecCtx::Run(DEBUG_LOCATION, on_done,
4525                  g_on_http_request_error == nullptr ? absl::OkStatus()
4526                                                     : *g_on_http_request_error);
4527     return 1;
4528   }
4529 
4530   // Constructs a synthetic JWT token that's just valid enough for the
4531   // call creds to extract the expiration date.
MakeToken(Timestamp expiration)4532   static std::string MakeToken(Timestamp expiration) {
4533     gpr_timespec ts = expiration.as_timespec(GPR_CLOCK_REALTIME);
4534     std::string json = absl::StrCat("{\"exp\":", ts.tv_sec, "}");
4535     return absl::StrCat("foo.", absl::WebSafeBase64Escape(json), ".bar");
4536   }
4537 
4538   static int g_http_status;
4539   static absl::string_view g_audience;
4540   static const char* g_token;
4541   static absl::Status* g_on_http_request_error;
4542 };
4543 
4544 int GcpServiceAccountIdentityCredentialsTest::g_http_status;
4545 absl::string_view GcpServiceAccountIdentityCredentialsTest::g_audience;
4546 const char* GcpServiceAccountIdentityCredentialsTest::g_token;
4547 absl::Status* GcpServiceAccountIdentityCredentialsTest::g_on_http_request_error;
4548 
TEST_F(GcpServiceAccountIdentityCredentialsTest,Basic)4549 TEST_F(GcpServiceAccountIdentityCredentialsTest, Basic) {
4550   g_audience = "CV-6";
4551   auto token = MakeToken(Timestamp::Now() + Duration::Hours(1));
4552   g_token = token.c_str();
4553   ExecCtx exec_ctx;
4554   auto creds =
4555       MakeRefCounted<GcpServiceAccountIdentityCallCredentials>(g_audience);
4556   CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
4557   auto state = RequestMetadataState::NewInstance(absl::OkStatus(), g_token);
4558   state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority,
4559                                 kTestPath);
4560   ExecCtx::Get()->Flush();
4561 }
4562 
4563 // HTTP status 429 is mapped to UNAVAILABLE as per
4564 // https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md.
TEST_F(GcpServiceAccountIdentityCredentialsTest,FailsWithHttpStatus429)4565 TEST_F(GcpServiceAccountIdentityCredentialsTest, FailsWithHttpStatus429) {
4566   g_audience = "CV-5_Midway";
4567   g_http_status = 429;
4568   ExecCtx exec_ctx;
4569   auto creds =
4570       MakeRefCounted<GcpServiceAccountIdentityCallCredentials>(g_audience);
4571   CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
4572   auto state = RequestMetadataState::NewInstance(
4573       absl::UnavailableError("JWT fetch failed with status 429"), "");
4574   state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority,
4575                                 kTestPath);
4576   ExecCtx::Get()->Flush();
4577 }
4578 
4579 // HTTP status 400 is mapped to INTERNAL as per
4580 // https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md,
4581 // so it should be rewritten as UNAUTHENTICATED.
TEST_F(GcpServiceAccountIdentityCredentialsTest,FailsWithHttpStatus400)4582 TEST_F(GcpServiceAccountIdentityCredentialsTest, FailsWithHttpStatus400) {
4583   g_audience = "CV-8_SantaCruzIslands";
4584   g_http_status = 400;
4585   ExecCtx exec_ctx;
4586   auto creds =
4587       MakeRefCounted<GcpServiceAccountIdentityCallCredentials>(g_audience);
4588   CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
4589   auto state = RequestMetadataState::NewInstance(
4590       absl::UnauthenticatedError("JWT fetch failed with status 400"), "");
4591   state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority,
4592                                 kTestPath);
4593   ExecCtx::Get()->Flush();
4594 }
4595 
TEST_F(GcpServiceAccountIdentityCredentialsTest,FailsWithHttpIOError)4596 TEST_F(GcpServiceAccountIdentityCredentialsTest, FailsWithHttpIOError) {
4597   g_audience = "CV-2_CoralSea";
4598   absl::Status status = absl::InternalError("uh oh");
4599   g_on_http_request_error = &status;
4600   ExecCtx exec_ctx;
4601   auto creds =
4602       MakeRefCounted<GcpServiceAccountIdentityCallCredentials>(g_audience);
4603   CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
4604   auto state = RequestMetadataState::NewInstance(
4605       absl::UnavailableError("INTERNAL:uh oh"), "");
4606   state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority,
4607                                 kTestPath);
4608   ExecCtx::Get()->Flush();
4609 }
4610 
TEST_F(GcpServiceAccountIdentityCredentialsTest,TokenHasWrongNumberOfDots)4611 TEST_F(GcpServiceAccountIdentityCredentialsTest, TokenHasWrongNumberOfDots) {
4612   g_audience = "CV-7_Guadalcanal";
4613   std::string bad_token = "foo.bar";
4614   g_token = bad_token.c_str();
4615   ExecCtx exec_ctx;
4616   auto creds =
4617       MakeRefCounted<GcpServiceAccountIdentityCallCredentials>(g_audience);
4618   CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
4619   auto state = RequestMetadataState::NewInstance(
4620       absl::UnauthenticatedError("error parsing JWT token"), "");
4621   state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority,
4622                                 kTestPath);
4623   ExecCtx::Get()->Flush();
4624 }
4625 
TEST_F(GcpServiceAccountIdentityCredentialsTest,TokenPayloadNotBase64)4626 TEST_F(GcpServiceAccountIdentityCredentialsTest, TokenPayloadNotBase64) {
4627   g_audience = "CVE-56_Makin";
4628   std::string bad_token = "foo.&.bar";
4629   g_token = bad_token.c_str();
4630   ExecCtx exec_ctx;
4631   auto creds =
4632       MakeRefCounted<GcpServiceAccountIdentityCallCredentials>(g_audience);
4633   CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
4634   auto state = RequestMetadataState::NewInstance(
4635       absl::UnauthenticatedError("error parsing JWT token"), "");
4636   state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority,
4637                                 kTestPath);
4638   ExecCtx::Get()->Flush();
4639 }
4640 
TEST_F(GcpServiceAccountIdentityCredentialsTest,TokenPayloadNotJson)4641 TEST_F(GcpServiceAccountIdentityCredentialsTest, TokenPayloadNotJson) {
4642   g_audience = "CVE-73_Samar";
4643   std::string bad_token =
4644       absl::StrCat("foo.", absl::WebSafeBase64Escape("xxx"), ".bar");
4645   g_token = bad_token.c_str();
4646   ExecCtx exec_ctx;
4647   auto creds =
4648       MakeRefCounted<GcpServiceAccountIdentityCallCredentials>(g_audience);
4649   CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
4650   auto state = RequestMetadataState::NewInstance(
4651       absl::UnauthenticatedError("error parsing JWT token"), "");
4652   state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority,
4653                                 kTestPath);
4654   ExecCtx::Get()->Flush();
4655 }
4656 
TEST_F(GcpServiceAccountIdentityCredentialsTest,TokenInvalidExpiration)4657 TEST_F(GcpServiceAccountIdentityCredentialsTest, TokenInvalidExpiration) {
4658   g_audience = "CVL-23_Leyte";
4659   std::string bad_token = absl::StrCat(
4660       "foo.", absl::WebSafeBase64Escape("{\"exp\":\"foo\"}"), ".bar");
4661   g_token = bad_token.c_str();
4662   ExecCtx exec_ctx;
4663   auto creds =
4664       MakeRefCounted<GcpServiceAccountIdentityCallCredentials>(g_audience);
4665   CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY);
4666   auto state = RequestMetadataState::NewInstance(
4667       absl::UnauthenticatedError("error parsing JWT token"), "");
4668   state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority,
4669                                 kTestPath);
4670   ExecCtx::Get()->Flush();
4671 }
4672 
4673 }  // namespace
4674 }  // namespace grpc_core
4675 
main(int argc,char ** argv)4676 int main(int argc, char** argv) {
4677   testing::InitGoogleTest(&argc, argv);
4678   grpc::testing::TestEnvironment env(&argc, argv);
4679   auto result = RUN_ALL_TESTS();
4680   return result;
4681 }
4682