diff --git a/src/core/lib/http/format_request.cc b/src/core/lib/http/format_request.cc index 685d854a61f..cd2c2e9db47 100644 --- a/src/core/lib/http/format_request.cc +++ b/src/core/lib/http/format_request.cc @@ -95,6 +95,35 @@ grpc_slice grpc_httpcli_format_post_request(const grpc_http_request* request, return grpc_slice_from_copied_buffer(req.data(), req.size()); } +grpc_slice grpc_httpcli_format_put_request(const grpc_http_request* request, + const char* host, const char* path) { + std::vector out; + out.push_back("PUT "); + fill_common_header(request, host, path, true, &out); + if (request->body != nullptr) { + bool has_content_type = false; + for (size_t i = 0; i < request->hdr_count; i++) { + if (strcmp(request->hdrs[i].key, "Content-Type") == 0) { + has_content_type = true; + break; + } + } + if (!has_content_type) { + out.push_back("Content-Type: text/plain\r\n"); + } + out.push_back( + absl::StrFormat("Content-Length: %lu\r\n", + static_cast(request->body_length))); + } + out.push_back("\r\n"); + std::string req = absl::StrJoin(out, ""); + if (request->body != nullptr) { + absl::StrAppend(&req, + absl::string_view(request->body, request->body_length)); + } + return grpc_slice_from_copied_buffer(req.data(), req.size()); +} + grpc_slice grpc_httpcli_format_connect_request(const grpc_http_request* request, const char* host, const char* path) { diff --git a/src/core/lib/http/format_request.h b/src/core/lib/http/format_request.h index 440bde907e3..e493fb4859b 100644 --- a/src/core/lib/http/format_request.h +++ b/src/core/lib/http/format_request.h @@ -29,6 +29,8 @@ grpc_slice grpc_httpcli_format_get_request(const grpc_http_request* request, const char* host, const char* path); grpc_slice grpc_httpcli_format_post_request(const grpc_http_request* request, const char* host, const char* path); +grpc_slice grpc_httpcli_format_put_request(const grpc_http_request* request, + const char* host, const char* path); grpc_slice grpc_httpcli_format_connect_request(const grpc_http_request* request, const char* host, const char* path); diff --git a/src/core/lib/http/httpcli.cc b/src/core/lib/http/httpcli.cc index 2bc598b7022..ba536b5d53e 100644 --- a/src/core/lib/http/httpcli.cc +++ b/src/core/lib/http/httpcli.cc @@ -53,6 +53,7 @@ namespace { grpc_httpcli_get_override g_get_override; grpc_httpcli_post_override g_post_override; +grpc_httpcli_put_override g_put_override; void (*g_test_only_on_handshake_done_intercept)(HttpRequest* req); } // namespace @@ -107,10 +108,36 @@ OrphanablePtr HttpRequest::Post( std::move(channel_creds)); } +OrphanablePtr HttpRequest::Put( + URI uri, const grpc_channel_args* channel_args, + grpc_polling_entity* pollent, const grpc_http_request* request, + Timestamp deadline, grpc_closure* on_done, grpc_http_response* response, + RefCountedPtr channel_creds) { + absl::optional> test_only_generate_response; + if (g_put_override != nullptr) { + test_only_generate_response = [request, uri, deadline, on_done, + response]() { + g_put_override(request, uri.authority().c_str(), uri.path().c_str(), + request->body, request->body_length, deadline, on_done, + response); + }; + } + std::string name = + absl::StrFormat("HTTP:PUT:%s:%s", uri.authority(), uri.path()); + const grpc_slice request_text = grpc_httpcli_format_put_request( + request, uri.authority().c_str(), uri.path().c_str()); + return MakeOrphanable( + std::move(uri), request_text, response, deadline, channel_args, on_done, + pollent, name.c_str(), std::move(test_only_generate_response), + std::move(channel_creds)); +} + void HttpRequest::SetOverride(grpc_httpcli_get_override get, - grpc_httpcli_post_override post) { + grpc_httpcli_post_override post, + grpc_httpcli_put_override put) { g_get_override = get; g_post_override = post; + g_put_override = put; } void HttpRequest::TestOnlySetOnHandshakeDoneIntercept( diff --git a/src/core/lib/http/httpcli.h b/src/core/lib/http/httpcli.h index 46ccbd28c55..c5602ce5b49 100644 --- a/src/core/lib/http/httpcli.h +++ b/src/core/lib/http/httpcli.h @@ -50,6 +50,10 @@ typedef int (*grpc_httpcli_post_override)( const grpc_http_request* request, const char* host, const char* path, const char* body_bytes, size_t body_size, grpc_core::Timestamp deadline, grpc_closure* on_complete, grpc_http_response* response); +typedef int (*grpc_httpcli_put_override)( + const grpc_http_request* request, const char* host, const char* path, + const char* body_bytes, size_t body_size, grpc_core::Timestamp deadline, + grpc_closure* on_complete, grpc_http_response* response); namespace grpc_core { @@ -112,6 +116,32 @@ class HttpRequest : public InternallyRefCounted { RefCountedPtr channel_creds) GRPC_MUST_USE_RESULT; + // Asynchronously perform a HTTP PUT. + // 'uri' is the target to make the request to. The scheme field is used to + // determine the port number. The authority field is the target host. The + // path field determines the path of the request. No other fields are used. + // 'args' are optional channel args for the request. + // 'pollent' indicates a grpc_polling_entity that is interested in the result + // of the post - work on this entity may be used to progress the post + // operation + // 'request' contains request parameters - these are caller owned and can be + // destroyed once the call returns + // 'deadline' contains a deadline for the request (or gpr_inf_future) + // 'on_done' is a callback to report results to + // 'channel_creds' are used to configurably secure the connection. + // For insecure requests, use grpc_insecure_credentials_create. + // For secure requests, use CreateHttpRequestSSLCredentials(). + // nullptr is treated as insecure credentials. + // TODO(apolcyn): disallow nullptr as a value after unsecure builds + // are removed. + // Does not support ?var1=val1&var2=val2 in the path. + static OrphanablePtr Put( + URI uri, const grpc_channel_args* args, grpc_polling_entity* pollent, + const grpc_http_request* request, Timestamp deadline, + grpc_closure* on_done, grpc_http_response* response, + RefCountedPtr channel_creds) + GRPC_MUST_USE_RESULT; + HttpRequest(URI uri, const grpc_slice& request_text, grpc_http_response* response, Timestamp deadline, const grpc_channel_args* channel_args, grpc_closure* on_done, @@ -126,7 +156,8 @@ class HttpRequest : public InternallyRefCounted { void Orphan() override; static void SetOverride(grpc_httpcli_get_override get, - grpc_httpcli_post_override post); + grpc_httpcli_post_override post, + grpc_httpcli_put_override put); static void TestOnlySetOnHandshakeDoneIntercept( void (*intercept)(HttpRequest* req)); diff --git a/src/core/lib/security/credentials/external/aws_external_account_credentials.cc b/src/core/lib/security/credentials/external/aws_external_account_credentials.cc index 5d8a74fb5f0..612857c2fd1 100644 --- a/src/core/lib/security/credentials/external/aws_external_account_credentials.cc +++ b/src/core/lib/security/credentials/external/aws_external_account_credentials.cc @@ -119,6 +119,12 @@ AwsExternalAccountCredentials::AwsExternalAccountCredentials( return; } regional_cred_verification_url_ = it->second.string_value(); + it = + options.credential_source.object_value().find("imdsv2_session_token_url"); + if (it != options.credential_source.object_value().end() && + it->second.type() == Json::Type::STRING) { + imdsv2_session_token_url_ = it->second.string_value(); + } } void AwsExternalAccountCredentials::RetrieveSubjectToken( @@ -133,6 +139,62 @@ void AwsExternalAccountCredentials::RetrieveSubjectToken( } ctx_ = ctx; cb_ = cb; + if (!imdsv2_session_token_url_.empty()) { + RetrieveImdsV2SessionToken(); + } else if (signer_ != nullptr) { + BuildSubjectToken(); + } else { + RetrieveRegion(); + } +} + +void AwsExternalAccountCredentials::RetrieveImdsV2SessionToken() { + absl::StatusOr uri = URI::Parse(imdsv2_session_token_url_); + if (!uri.ok()) { + return; + } + grpc_http_header* headers = + static_cast(gpr_malloc(sizeof(grpc_http_header))); + headers[0].key = gpr_strdup("x-aws-ec2-metadata-token-ttl-seconds"); + headers[0].value = gpr_strdup("300"); + grpc_http_request request; + memset(&request, 0, sizeof(grpc_http_request)); + request.hdr_count = 1; + request.hdrs = headers; + grpc_http_response_destroy(&ctx_->response); + ctx_->response = {}; + GRPC_CLOSURE_INIT(&ctx_->closure, OnRetrieveImdsV2SessionToken, this, + nullptr); + RefCountedPtr http_request_creds; + if (uri->scheme() == "http") { + http_request_creds = RefCountedPtr( + grpc_insecure_credentials_create()); + } else { + http_request_creds = CreateHttpRequestSSLCredentials(); + } + http_request_ = + HttpRequest::Put(std::move(*uri), nullptr /* channel args */, + ctx_->pollent, &request, ctx_->deadline, &ctx_->closure, + &ctx_->response, std::move(http_request_creds)); + http_request_->Start(); + grpc_http_request_destroy(&request); +} + +void AwsExternalAccountCredentials::OnRetrieveImdsV2SessionToken( + void* arg, grpc_error_handle error) { + AwsExternalAccountCredentials* self = + static_cast(arg); + self->OnRetrieveImdsV2SessionTokenInternal(GRPC_ERROR_REF(error)); +} + +void AwsExternalAccountCredentials::OnRetrieveImdsV2SessionTokenInternal( + grpc_error_handle error) { + if (error != GRPC_ERROR_NONE) { + FinishRetrieveSubjectToken("", error); + return; + } + imdsv2_session_token_ = + std::string(ctx_->response.body, ctx_->response.body_length); if (signer_ != nullptr) { BuildSubjectToken(); } else { @@ -140,6 +202,20 @@ void AwsExternalAccountCredentials::RetrieveSubjectToken( } } +void AwsExternalAccountCredentials::AddMetadataRequestHeaders( + grpc_http_request* request) { + if (!imdsv2_session_token_.empty()) { + GPR_ASSERT(request->hdr_count == 0); + GPR_ASSERT(request->hdrs == nullptr); + grpc_http_header* headers = + static_cast(gpr_malloc(sizeof(grpc_http_header))); + headers[0].key = gpr_strdup("x-aws-ec2-metadata-token"); + headers[0].value = gpr_strdup(imdsv2_session_token_.c_str()); + request->hdr_count = 1; + request->hdrs = headers; + } +} + void AwsExternalAccountCredentials::RetrieveRegion() { UniquePtr region_from_env(gpr_getenv(kRegionEnvVar)); if (region_from_env == nullptr) { @@ -165,6 +241,7 @@ void AwsExternalAccountCredentials::RetrieveRegion() { memset(&request, 0, sizeof(grpc_http_request)); grpc_http_response_destroy(&ctx_->response); ctx_->response = {}; + AddMetadataRequestHeaders(&request); GRPC_CLOSURE_INIT(&ctx_->closure, OnRetrieveRegion, this, nullptr); RefCountedPtr http_request_creds; if (uri->scheme() == "http") { @@ -217,6 +294,7 @@ void AwsExternalAccountCredentials::RetrieveRoleName() { memset(&request, 0, sizeof(grpc_http_request)); grpc_http_response_destroy(&ctx_->response); ctx_->response = {}; + AddMetadataRequestHeaders(&request); GRPC_CLOSURE_INIT(&ctx_->closure, OnRetrieveRoleName, this, nullptr); // TODO(ctiller): use the caller's resource quota. RefCountedPtr http_request_creds; @@ -282,6 +360,7 @@ void AwsExternalAccountCredentials::RetrieveSigningKeys() { memset(&request, 0, sizeof(grpc_http_request)); grpc_http_response_destroy(&ctx_->response); ctx_->response = {}; + AddMetadataRequestHeaders(&request); GRPC_CLOSURE_INIT(&ctx_->closure, OnRetrieveSigningKeys, this, nullptr); // TODO(ctiller): use the caller's resource quota. RefCountedPtr http_request_creds; diff --git a/src/core/lib/security/credentials/external/aws_external_account_credentials.h b/src/core/lib/security/credentials/external/aws_external_account_credentials.h index ee47270b6a6..ef29b0f3ae4 100644 --- a/src/core/lib/security/credentials/external/aws_external_account_credentials.h +++ b/src/core/lib/security/credentials/external/aws_external_account_credentials.h @@ -43,6 +43,10 @@ class AwsExternalAccountCredentials final : public ExternalAccountCredentials { static void OnRetrieveRegion(void* arg, grpc_error_handle error); void OnRetrieveRegionInternal(grpc_error_handle error); + void RetrieveImdsV2SessionToken(); + static void OnRetrieveImdsV2SessionToken(void* arg, grpc_error_handle error); + void OnRetrieveImdsV2SessionTokenInternal(grpc_error_handle error); + void RetrieveRoleName(); static void OnRetrieveRoleName(void* arg, grpc_error_handle error); void OnRetrieveRoleNameInternal(grpc_error_handle error); @@ -55,6 +59,8 @@ class AwsExternalAccountCredentials final : public ExternalAccountCredentials { void FinishRetrieveSubjectToken(std::string subject_token, grpc_error_handle error); + void AddMetadataRequestHeaders(grpc_http_request* request); + std::string audience_; OrphanablePtr http_request_; @@ -62,6 +68,7 @@ class AwsExternalAccountCredentials final : public ExternalAccountCredentials { std::string region_url_; std::string url_; std::string regional_cred_verification_url_; + std::string imdsv2_session_token_url_; // Information required by request signer std::string region_; @@ -69,6 +76,7 @@ class AwsExternalAccountCredentials final : public ExternalAccountCredentials { std::string access_key_id_; std::string secret_access_key_; std::string token_; + std::string imdsv2_session_token_; std::unique_ptr signer_; std::string cred_verification_url_; diff --git a/test/core/security/credentials_test.cc b/test/core/security/credentials_test.cc index 6c53a397c09..b40b2fb224f 100644 --- a/test/core/security/credentials_test.cc +++ b/test/core/security/credentials_test.cc @@ -247,6 +247,8 @@ const char valid_aws_external_account_creds_retrieve_signing_keys_response[] = "{\"AccessKeyId\":\"test_access_key_id\",\"SecretAccessKey\":" "\"test_secret_access_key\",\"Token\":\"test_token\"}"; +const char aws_imdsv2_session_token[] = "imdsv2_session_token"; + const char valid_aws_external_account_creds_options_credential_source[] = "{\"environment_id\":\"aws1\"," "\"region_url\":\"https://foo.com:5555/region_url\"," @@ -254,6 +256,15 @@ const char valid_aws_external_account_creds_options_credential_source[] = "\"regional_cred_verification_url\":\"https://foo.com:5555/" "regional_cred_verification_url_{region}\"}"; +const char valid_aws_imdsv2_external_account_creds_options_credential_source[] = + "{\"environment_id\":\"aws1\"," + "\"region_url\":\"https://foo.com:5555/region_url\"," + "\"url\":\"https://foo.com:5555/url\"," + "\"imdsv2_session_token_url\":\"https://foo.com:5555/" + "imdsv2_session_token_url\"," + "\"regional_cred_verification_url\":\"https://foo.com:5555/" + "regional_cred_verification_url_{region}\"}"; + const char invalid_aws_external_account_creds_options_credential_source_unmatched_environment_id [] = "{\"environment_id\":\"unsupported_aws_version\"," @@ -761,6 +772,17 @@ int httpcli_get_should_not_be_called(const grpc_http_request* /*request*/, return 1; } +int httpcli_put_should_not_be_called(const grpc_http_request* /*request*/, + const char* /*host*/, const char* /*path*/, + const char* /*body_bytes*/, + size_t /*body_size*/, + Timestamp /*deadline*/, + grpc_closure* /*on_done*/, + grpc_http_response* /*response*/) { + GPR_ASSERT("HTTP PUT should not be called" == nullptr); + return 1; +} + TEST(CredentialsTest, TestComputeEngineCredsSuccess) { ExecCtx exec_ctx; std::string emd = "authorization: Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_"; @@ -775,7 +797,8 @@ TEST(CredentialsTest, TestComputeEngineCredsSuccess) { /* First request: http get should be called. */ auto state = RequestMetadataState::NewInstance(GRPC_ERROR_NONE, emd); HttpRequest::SetOverride(compute_engine_httpcli_get_success_override, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); @@ -783,7 +806,8 @@ TEST(CredentialsTest, TestComputeEngineCredsSuccess) { /* Second request: the cached token should be served directly. */ state = RequestMetadataState::NewInstance(GRPC_ERROR_NONE, emd); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); @@ -791,7 +815,7 @@ TEST(CredentialsTest, TestComputeEngineCredsSuccess) { GPR_ASSERT( strcmp(creds->debug_string().c_str(), expected_creds_debug_string) == 0); creds->Unref(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } TEST(CredentialsTest, TestComputeEngineCredsFailure) { @@ -806,13 +830,14 @@ TEST(CredentialsTest, TestComputeEngineCredsFailure) { grpc_call_credentials* creds = grpc_google_compute_engine_credentials_create(nullptr); HttpRequest::SetOverride(compute_engine_httpcli_get_failure_override, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, kTestPath); GPR_ASSERT( strcmp(creds->debug_string().c_str(), expected_creds_debug_string) == 0); creds->Unref(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } void validate_refresh_token_http_request(const grpc_http_request* request, @@ -872,7 +897,8 @@ TEST(CredentialsTest, TestRefreshTokenCredsSuccess) { /* First request: http put should be called. */ auto state = RequestMetadataState::NewInstance(GRPC_ERROR_NONE, emd); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - refresh_token_httpcli_post_success); + refresh_token_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); @@ -880,7 +906,8 @@ TEST(CredentialsTest, TestRefreshTokenCredsSuccess) { /* Second request: the cached token should be served directly. */ state = RequestMetadataState::NewInstance(GRPC_ERROR_NONE, emd); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); @@ -888,7 +915,7 @@ TEST(CredentialsTest, TestRefreshTokenCredsSuccess) { strcmp(creds->debug_string().c_str(), expected_creds_debug_string) == 0); creds->Unref(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } TEST(CredentialsTest, TestRefreshTokenCredsFailure) { @@ -903,14 +930,15 @@ TEST(CredentialsTest, TestRefreshTokenCredsFailure) { grpc_call_credentials* creds = grpc_google_refresh_token_credentials_create( test_refresh_token_str, nullptr); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - token_httpcli_post_failure); + token_httpcli_post_failure, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, kTestPath); GPR_ASSERT( strcmp(creds->debug_string().c_str(), expected_creds_debug_string) == 0); creds->Unref(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } TEST(CredentialsTest, TestValidStsCredsOptions) { @@ -1120,7 +1148,8 @@ TEST(CredentialsTest, TestStsCredsSuccess) { /* First request: http put should be called. */ auto state = RequestMetadataState::NewInstance(GRPC_ERROR_NONE, emd); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - sts_token_httpcli_post_success); + sts_token_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); @@ -1128,7 +1157,8 @@ TEST(CredentialsTest, TestStsCredsSuccess) { /* Second request: the cached token should be served directly. */ state = RequestMetadataState::NewInstance(GRPC_ERROR_NONE, emd); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); @@ -1136,7 +1166,7 @@ TEST(CredentialsTest, TestStsCredsSuccess) { strcmp(creds->debug_string().c_str(), expected_creds_debug_string) == 0); creds->Unref(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); gpr_free(subject_token_path); gpr_free(actor_token_path); } @@ -1165,14 +1195,15 @@ TEST(CredentialsTest, TestStsCredsTokenFileNotFound) { "Error occurred when fetching oauth2 token."), {}); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); /* Cleanup. */ creds->Unref(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } TEST(CredentialsTest, TestStsCredsNoActorTokenSuccess) { @@ -1202,7 +1233,8 @@ TEST(CredentialsTest, TestStsCredsNoActorTokenSuccess) { /* First request: http put should be called. */ auto state = RequestMetadataState::NewInstance(GRPC_ERROR_NONE, emd); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - sts_token_httpcli_post_success_no_actor_token); + sts_token_httpcli_post_success_no_actor_token, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); @@ -1210,7 +1242,8 @@ TEST(CredentialsTest, TestStsCredsNoActorTokenSuccess) { /* Second request: the cached token should be served directly. */ state = RequestMetadataState::NewInstance(GRPC_ERROR_NONE, emd); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); @@ -1218,7 +1251,7 @@ TEST(CredentialsTest, TestStsCredsNoActorTokenSuccess) { strcmp(creds->debug_string().c_str(), expected_creds_debug_string) == 0); creds->Unref(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); gpr_free(subject_token_path); } @@ -1245,14 +1278,15 @@ TEST(CredentialsTest, TestStsCredsLoadTokenFailure) { }; grpc_call_credentials* creds = grpc_sts_credentials_create(&options, nullptr); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, kTestPath); GPR_ASSERT( strcmp(creds->debug_string().c_str(), expected_creds_debug_string) == 0); creds->Unref(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); gpr_free(test_signed_jwt_path); } @@ -1280,13 +1314,14 @@ TEST(CredentialsTest, TestStsCredsHttpFailure) { grpc_call_credentials* creds = grpc_sts_credentials_create(&valid_options, nullptr); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - token_httpcli_post_failure); + token_httpcli_post_failure, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, kTestPath); GPR_ASSERT( strcmp(creds->debug_string().c_str(), expected_creds_debug_string) == 0); creds->Unref(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); gpr_free(test_signed_jwt_path); } @@ -1655,7 +1690,8 @@ TEST(CredentialsTest, TestGoogleDefaultCredsGce) { GPR_ASSERT(creds != nullptr); GPR_ASSERT(creds->call_creds() != nullptr); HttpRequest::SetOverride(compute_engine_httpcli_get_success_override, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds->mutable_call_creds(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); @@ -1664,7 +1700,7 @@ TEST(CredentialsTest, TestGoogleDefaultCredsGce) { /* Cleanup. */ creds->Unref(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); grpc_override_well_known_credentials_path_getter(nullptr); } @@ -1683,7 +1719,7 @@ TEST(CredentialsTest, TestGoogleDefaultCredsNonGce) { /* Simulate a successful detection of metadata server. */ HttpRequest::SetOverride( default_creds_metadata_server_detection_httpcli_get_success_override, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, httpcli_put_should_not_be_called); grpc_composite_channel_credentials* creds = reinterpret_cast( grpc_google_default_credentials_create(nullptr)); @@ -1691,14 +1727,15 @@ TEST(CredentialsTest, TestGoogleDefaultCredsNonGce) { GPR_ASSERT(creds != nullptr); GPR_ASSERT(creds->call_creds() != nullptr); HttpRequest::SetOverride(compute_engine_httpcli_get_success_override, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds->mutable_call_creds(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); GPR_ASSERT(g_test_gce_tenancy_checker_called == true); /* Cleanup. */ creds->Unref(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); grpc_override_well_known_credentials_path_getter(nullptr); } @@ -1724,7 +1761,7 @@ TEST(CredentialsTest, TestNoGoogleDefaultCreds) { g_test_is_on_gce = false; HttpRequest::SetOverride( default_creds_gce_detection_httpcli_get_failure_override, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, httpcli_put_should_not_be_called); /* Simulate a successful detection of GCE. */ GPR_ASSERT(grpc_google_default_credentials_create(nullptr) == nullptr); /* Try a second one. GCE detection should occur again. */ @@ -1733,7 +1770,7 @@ TEST(CredentialsTest, TestNoGoogleDefaultCreds) { GPR_ASSERT(g_test_gce_tenancy_checker_called == true); /* Cleanup. */ grpc_override_well_known_credentials_path_getter(nullptr); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } TEST(CredentialsTest, TestGoogleDefaultCredsCallCredsSpecified) { @@ -1749,7 +1786,7 @@ TEST(CredentialsTest, TestGoogleDefaultCredsCallCredsSpecified) { g_test_is_on_gce = true; HttpRequest::SetOverride( default_creds_metadata_server_detection_httpcli_get_success_override, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, httpcli_put_should_not_be_called); grpc_composite_channel_credentials* channel_creds = reinterpret_cast( grpc_google_default_credentials_create(call_creds)); @@ -1757,12 +1794,13 @@ TEST(CredentialsTest, TestGoogleDefaultCredsCallCredsSpecified) { GPR_ASSERT(channel_creds != nullptr); GPR_ASSERT(channel_creds->call_creds() != nullptr); HttpRequest::SetOverride(compute_engine_httpcli_get_success_override, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(channel_creds->mutable_call_creds(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); channel_creds->Unref(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } struct fake_call_creds : public grpc_call_credentials { @@ -1795,7 +1833,7 @@ TEST(CredentialsTest, TestGoogleDefaultCredsNotDefault) { g_test_is_on_gce = true; HttpRequest::SetOverride( default_creds_metadata_server_detection_httpcli_get_success_override, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, httpcli_put_should_not_be_called); grpc_composite_channel_credentials* channel_creds = reinterpret_cast( grpc_google_default_credentials_create(call_creds.release())); @@ -1806,7 +1844,7 @@ TEST(CredentialsTest, TestGoogleDefaultCredsNotDefault) { kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); channel_creds->Unref(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } typedef enum { @@ -2315,6 +2353,30 @@ int aws_external_account_creds_httpcli_get_success( return 1; } +int aws_imdsv2_external_account_creds_httpcli_get_success( + const grpc_http_request* request, const char* host, const char* path, + Timestamp deadline, grpc_closure* on_done, grpc_http_response* response) { + GPR_ASSERT(request->hdr_count == 1); + GPR_ASSERT(strcmp(request->hdrs[0].key, "x-aws-ec2-metadata-token") == 0); + GPR_ASSERT(strcmp(request->hdrs[0].value, aws_imdsv2_session_token) == 0); + return aws_external_account_creds_httpcli_get_success( + request, host, path, deadline, on_done, response); +} + +int aws_imdsv2_external_account_creds_httpcli_put_success( + const grpc_http_request* request, const char* /*host*/, const char* path, + const char* /*body*/, size_t /*body_size*/, Timestamp /*deadline*/, + grpc_closure* on_done, grpc_http_response* response) { + GPR_ASSERT(request->hdr_count == 1); + GPR_ASSERT(strcmp(request->hdrs[0].key, + "x-aws-ec2-metadata-token-ttl-seconds") == 0); + GPR_ASSERT(strcmp(request->hdrs[0].value, "300") == 0); + GPR_ASSERT(strcmp(path, "/imdsv2_session_token_url") == 0); + *response = http_response(200, aws_imdsv2_session_token); + ExecCtx::Run(DEBUG_LOCATION, on_done, GRPC_ERROR_NONE); + return 1; +} + int aws_external_account_creds_httpcli_post_success( const grpc_http_request* request, const char* host, const char* path, const char* body, size_t body_size, Timestamp /*deadline*/, @@ -2369,7 +2431,8 @@ TEST(CredentialsTest, TestExternalAccountCredsSuccess) { auto state = RequestMetadataState::NewInstance( GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - external_account_creds_httpcli_post_success); + external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(&creds, kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); @@ -2377,11 +2440,12 @@ TEST(CredentialsTest, TestExternalAccountCredsSuccess) { state = RequestMetadataState::NewInstance( GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(&creds, kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } TEST(CredentialsTest, TestExternalAccountCredsSuccessWithUrlEncode) { @@ -2406,11 +2470,12 @@ TEST(CredentialsTest, TestExternalAccountCredsSuccessWithUrlEncode) { auto state = RequestMetadataState::NewInstance( GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - external_account_creds_httpcli_post_success); + external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(&creds, kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } TEST(CredentialsTest, @@ -2438,11 +2503,12 @@ TEST(CredentialsTest, GRPC_ERROR_NONE, "authorization: Bearer service_account_impersonation_access_token"); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - external_account_creds_httpcli_post_success); + external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(&creds, kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } TEST(CredentialsTest, TestExternalAccountCredsFailureInvalidTokenUrl) { @@ -2463,7 +2529,8 @@ TEST(CredentialsTest, TestExternalAccountCredsFailureInvalidTokenUrl) { }; TestExternalAccountCredentials creds(options, {}); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); grpc_error_handle error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( "Invalid token url: invalid_token_url."); grpc_error_handle expected_error = @@ -2474,7 +2541,7 @@ TEST(CredentialsTest, TestExternalAccountCredsFailureInvalidTokenUrl) { kTestPath); GRPC_ERROR_UNREF(error); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } TEST(CredentialsTest, @@ -2496,7 +2563,8 @@ TEST(CredentialsTest, }; TestExternalAccountCredentials creds(options, {}); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - external_account_creds_httpcli_post_success); + external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); grpc_error_handle error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( "Invalid service account impersonation url: " "invalid_service_account_impersonation_url."); @@ -2508,7 +2576,7 @@ TEST(CredentialsTest, kTestPath); GRPC_ERROR_UNREF(error); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } TEST(CredentialsTest, @@ -2531,7 +2599,8 @@ TEST(CredentialsTest, TestExternalAccountCredentials creds(options, {}); HttpRequest::SetOverride( httpcli_get_should_not_be_called, - external_account_creds_httpcli_post_failure_token_exchange_response_missing_access_token); + external_account_creds_httpcli_post_failure_token_exchange_response_missing_access_token, + httpcli_put_should_not_be_called); grpc_error_handle error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( "Missing or invalid access_token in " "{\"not_access_token\":\"not_access_token\",\"expires_in\":3599,\"token_" @@ -2544,7 +2613,7 @@ TEST(CredentialsTest, kTestPath); GRPC_ERROR_UNREF(error); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } TEST(CredentialsTest, TestUrlExternalAccountCredsSuccessFormatText) { @@ -2574,11 +2643,12 @@ TEST(CredentialsTest, TestUrlExternalAccountCredsSuccessFormatText) { auto state = RequestMetadataState::NewInstance( GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(url_external_account_creds_httpcli_get_success, - external_account_creds_httpcli_post_success); + external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } TEST(CredentialsTest, @@ -2611,11 +2681,12 @@ TEST(CredentialsTest, auto state = RequestMetadataState::NewInstance( GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(url_external_account_creds_httpcli_get_success, - external_account_creds_httpcli_post_success); + external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } TEST(CredentialsTest, TestUrlExternalAccountCredsSuccessFormatJson) { @@ -2645,11 +2716,12 @@ TEST(CredentialsTest, TestUrlExternalAccountCredsSuccessFormatJson) { auto state = RequestMetadataState::NewInstance( GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(url_external_account_creds_httpcli_get_success, - external_account_creds_httpcli_post_success); + external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } TEST(CredentialsTest, @@ -2710,11 +2782,12 @@ TEST(CredentialsTest, TestFileExternalAccountCredsSuccessFormatText) { auto state = RequestMetadataState::NewInstance( GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - external_account_creds_httpcli_post_success); + external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); GRPC_ERROR_UNREF(error); gpr_free(subject_token_path); } @@ -2757,11 +2830,12 @@ TEST(CredentialsTest, TestFileExternalAccountCredsSuccessFormatJson) { auto state = RequestMetadataState::NewInstance( GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - external_account_creds_httpcli_post_success); + external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); GRPC_ERROR_UNREF(error); gpr_free(subject_token_path); } @@ -2789,7 +2863,8 @@ TEST(CredentialsTest, TestFileExternalAccountCredsFailureFileNotFound) { GPR_ASSERT(creds != nullptr); GPR_ASSERT(error == GRPC_ERROR_NONE); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to load file"); grpc_error_handle expected_error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( @@ -2798,7 +2873,7 @@ TEST(CredentialsTest, TestFileExternalAccountCredsFailureFileNotFound) { state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); GRPC_ERROR_UNREF(error); } @@ -2836,7 +2911,8 @@ TEST(CredentialsTest, TestFileExternalAccountCredsFailureInvalidJsonContent) { GPR_ASSERT(creds != nullptr); GPR_ASSERT(error == GRPC_ERROR_NONE); HttpRequest::SetOverride(httpcli_get_should_not_be_called, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( "The content of the file is not a valid json object."); grpc_error_handle expected_error = @@ -2846,7 +2922,7 @@ TEST(CredentialsTest, TestFileExternalAccountCredsFailureInvalidJsonContent) { state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); GRPC_ERROR_UNREF(error); gpr_free(subject_token_path); } @@ -2877,11 +2953,48 @@ TEST(CredentialsTest, TestAwsExternalAccountCredsSuccess) { auto state = RequestMetadataState::NewInstance( GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success, - aws_external_account_creds_httpcli_post_success); + aws_external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); +} + +TEST(CredentialsTest, TestAwsImdsv2ExternalAccountCredsSuccess) { + ExecCtx exec_ctx; + grpc_error_handle error = GRPC_ERROR_NONE; + Json credential_source = Json::Parse( + valid_aws_imdsv2_external_account_creds_options_credential_source, + &error); + GPR_ASSERT(error == GRPC_ERROR_NONE); + ExternalAccountCredentials::Options options = { + "external_account", // type; + "audience", // audience; + "subject_token_type", // subject_token_type; + "", // service_account_impersonation_url; + "https://foo.com:5555/token", // token_url; + "https://foo.com:5555/token_info", // token_info_url; + credential_source, // credential_source; + "quota_project_id", // quota_project_id; + "client_id", // client_id; + "client_secret", // client_secret; + "", // workforce_pool_user_project; + }; + auto creds = AwsExternalAccountCredentials::Create(options, {}, &error); + GPR_ASSERT(creds != nullptr); + GPR_ASSERT(error == GRPC_ERROR_NONE); + GPR_ASSERT(creds->min_security_level() == GRPC_PRIVACY_AND_INTEGRITY); + auto state = RequestMetadataState::NewInstance( + GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); + HttpRequest::SetOverride( + aws_imdsv2_external_account_creds_httpcli_get_success, + aws_external_account_creds_httpcli_post_success, + aws_imdsv2_external_account_creds_httpcli_put_success); + state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, + kTestPath); + ExecCtx::Get()->Flush(); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); } TEST(CredentialsTest, TestAwsExternalAccountCredsSuccessPathRegionEnvKeysUrl) { @@ -2911,11 +3024,12 @@ TEST(CredentialsTest, TestAwsExternalAccountCredsSuccessPathRegionEnvKeysUrl) { auto state = RequestMetadataState::NewInstance( GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success, - aws_external_account_creds_httpcli_post_success); + aws_external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); gpr_unsetenv("AWS_REGION"); } @@ -2947,11 +3061,12 @@ TEST(CredentialsTest, auto state = RequestMetadataState::NewInstance( GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success, - aws_external_account_creds_httpcli_post_success); + aws_external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); gpr_unsetenv("AWS_DEFAULT_REGION"); } @@ -2985,11 +3100,12 @@ TEST(CredentialsTest, auto state = RequestMetadataState::NewInstance( GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success, - aws_external_account_creds_httpcli_post_success); + aws_external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); gpr_unsetenv("AWS_REGION"); gpr_unsetenv("AWS_DEFAULT_REGION"); } @@ -3023,11 +3139,12 @@ TEST(CredentialsTest, TestAwsExternalAccountCredsSuccessPathRegionUrlKeysEnv) { auto state = RequestMetadataState::NewInstance( GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success, - aws_external_account_creds_httpcli_post_success); + aws_external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); gpr_unsetenv("AWS_ACCESS_KEY_ID"); gpr_unsetenv("AWS_SECRET_ACCESS_KEY"); gpr_unsetenv("AWS_SESSION_TOKEN"); @@ -3063,11 +3180,12 @@ TEST(CredentialsTest, TestAwsExternalAccountCredsSuccessPathRegionEnvKeysEnv) { auto state = RequestMetadataState::NewInstance( GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success, - aws_external_account_creds_httpcli_post_success); + aws_external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); gpr_unsetenv("AWS_REGION"); gpr_unsetenv("AWS_ACCESS_KEY_ID"); gpr_unsetenv("AWS_SECRET_ACCESS_KEY"); @@ -3107,11 +3225,12 @@ TEST(CredentialsTest, auto state = RequestMetadataState::NewInstance( GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success, - aws_external_account_creds_httpcli_post_success); + aws_external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); gpr_unsetenv("AWS_DEFAULT_REGION"); gpr_unsetenv("AWS_ACCESS_KEY_ID"); gpr_unsetenv("AWS_SECRET_ACCESS_KEY"); @@ -3151,11 +3270,12 @@ TEST(CredentialsTest, auto state = RequestMetadataState::NewInstance( GRPC_ERROR_NONE, "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success, - aws_external_account_creds_httpcli_post_success); + aws_external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); gpr_unsetenv("AWS_REGION"); gpr_unsetenv("AWS_DEFAULT_REGION"); gpr_unsetenv("AWS_ACCESS_KEY_ID"); @@ -3224,11 +3344,12 @@ TEST(CredentialsTest, TestAwsExternalAccountCredsFailureInvalidRegionUrl) { "Error occurred when fetching oauth2 token.", &error, 1); auto state = RequestMetadataState::NewInstance(expected_error, {}); HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success, - aws_external_account_creds_httpcli_post_success); + aws_external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); GRPC_ERROR_UNREF(error); } @@ -3262,11 +3383,12 @@ TEST(CredentialsTest, TestAwsExternalAccountCredsFailureInvalidUrl) { "Error occurred when fetching oauth2 token.", &error, 1); auto state = RequestMetadataState::NewInstance(expected_error, {}); HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success, - aws_external_account_creds_httpcli_post_success); + aws_external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); GRPC_ERROR_UNREF(error); } @@ -3301,11 +3423,12 @@ TEST(CredentialsTest, TestAwsExternalAccountCredsFailureMissingRoleName) { "Error occurred when fetching oauth2 token.", &error, 1); auto state = RequestMetadataState::NewInstance(expected_error, {}); HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success, - aws_external_account_creds_httpcli_post_success); + aws_external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); GRPC_ERROR_UNREF(error); } @@ -3341,11 +3464,12 @@ TEST(CredentialsTest, "Error occurred when fetching oauth2 token.", &error, 1); auto state = RequestMetadataState::NewInstance(expected_error, {}); HttpRequest::SetOverride(aws_external_account_creds_httpcli_get_success, - aws_external_account_creds_httpcli_post_success); + aws_external_account_creds_httpcli_post_success, + httpcli_put_should_not_be_called); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); - HttpRequest::SetOverride(nullptr, nullptr); + HttpRequest::SetOverride(nullptr, nullptr, nullptr); GRPC_ERROR_UNREF(error); } diff --git a/test/core/security/jwt_verifier_test.cc b/test/core/security/jwt_verifier_test.cc index 7bd7cabdfa5..03f1baaed75 100644 --- a/test/core/security/jwt_verifier_test.cc +++ b/test/core/security/jwt_verifier_test.cc @@ -348,6 +348,15 @@ static int httpcli_post_should_not_be_called( return 1; } +static int httpcli_put_should_not_be_called( + const grpc_http_request* /*request*/, const char* /*host*/, + const char* /*path*/, const char* /*body_bytes*/, size_t /*body_size*/, + grpc_core::Timestamp /*deadline*/, grpc_closure* /*on_done*/, + grpc_http_response* /*response*/) { + GPR_ASSERT("HTTP PUT should not be called" == nullptr); + return 1; +} + static int httpcli_get_google_keys_for_email( const grpc_http_request* /*request*/, const char* host, const char* path, grpc_core::Timestamp /*deadline*/, grpc_closure* on_done, @@ -381,7 +390,8 @@ static void test_jwt_verifier_google_email_issuer_success(void) { gpr_free(key_str); GPR_ASSERT(grpc_auth_json_key_is_valid(&key)); grpc_core::HttpRequest::SetOverride(httpcli_get_google_keys_for_email, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime, nullptr); grpc_auth_json_key_destruct(&key); @@ -392,7 +402,7 @@ static void test_jwt_verifier_google_email_issuer_success(void) { grpc_jwt_verifier_destroy(verifier); grpc_core::ExecCtx::Get()->Flush(); gpr_free(jwt); - grpc_core::HttpRequest::SetOverride(nullptr, nullptr); + grpc_core::HttpRequest::SetOverride(nullptr, nullptr, nullptr); } static int httpcli_get_custom_keys_for_email( @@ -415,7 +425,8 @@ static void test_jwt_verifier_custom_email_issuer_success(void) { gpr_free(key_str); GPR_ASSERT(grpc_auth_json_key_is_valid(&key)); grpc_core::HttpRequest::SetOverride(httpcli_get_custom_keys_for_email, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime, nullptr); grpc_auth_json_key_destruct(&key); @@ -426,7 +437,7 @@ static void test_jwt_verifier_custom_email_issuer_success(void) { grpc_jwt_verifier_destroy(verifier); grpc_core::ExecCtx::Get()->Flush(); gpr_free(jwt); - grpc_core::HttpRequest::SetOverride(nullptr, nullptr); + grpc_core::HttpRequest::SetOverride(nullptr, nullptr, nullptr); } static int httpcli_get_jwk_set(const grpc_http_request* /*request*/, @@ -450,7 +461,8 @@ static int httpcli_get_openid_config(const grpc_http_request* /*request*/, GPR_ASSERT(strcmp(host, "accounts.google.com") == 0); GPR_ASSERT(strcmp(path, GRPC_OPENID_CONFIG_URL_SUFFIX) == 0); grpc_core::HttpRequest::SetOverride(httpcli_get_jwk_set, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); grpc_core::ExecCtx::Run(DEBUG_LOCATION, on_done, GRPC_ERROR_NONE); return 1; } @@ -464,7 +476,8 @@ static void test_jwt_verifier_url_issuer_success(void) { gpr_free(key_str); GPR_ASSERT(grpc_auth_json_key_is_valid(&key)); grpc_core::HttpRequest::SetOverride(httpcli_get_openid_config, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime, nullptr); grpc_auth_json_key_destruct(&key); @@ -475,7 +488,7 @@ static void test_jwt_verifier_url_issuer_success(void) { grpc_jwt_verifier_destroy(verifier); grpc_core::ExecCtx::Get()->Flush(); gpr_free(jwt); - grpc_core::HttpRequest::SetOverride(nullptr, nullptr); + grpc_core::HttpRequest::SetOverride(nullptr, nullptr, nullptr); } static void on_verification_key_retrieval_error(void* user_data, @@ -505,7 +518,8 @@ static void test_jwt_verifier_url_issuer_bad_config(void) { gpr_free(key_str); GPR_ASSERT(grpc_auth_json_key_is_valid(&key)); grpc_core::HttpRequest::SetOverride(httpcli_get_bad_json, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime, nullptr); grpc_auth_json_key_destruct(&key); @@ -516,7 +530,7 @@ static void test_jwt_verifier_url_issuer_bad_config(void) { grpc_jwt_verifier_destroy(verifier); grpc_core::ExecCtx::Get()->Flush(); gpr_free(jwt); - grpc_core::HttpRequest::SetOverride(nullptr, nullptr); + grpc_core::HttpRequest::SetOverride(nullptr, nullptr, nullptr); } static void test_jwt_verifier_bad_json_key(void) { @@ -528,7 +542,8 @@ static void test_jwt_verifier_bad_json_key(void) { gpr_free(key_str); GPR_ASSERT(grpc_auth_json_key_is_valid(&key)); grpc_core::HttpRequest::SetOverride(httpcli_get_bad_json, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime, nullptr); grpc_auth_json_key_destruct(&key); @@ -539,7 +554,7 @@ static void test_jwt_verifier_bad_json_key(void) { grpc_jwt_verifier_destroy(verifier); grpc_core::ExecCtx::Get()->Flush(); gpr_free(jwt); - grpc_core::HttpRequest::SetOverride(nullptr, nullptr); + grpc_core::HttpRequest::SetOverride(nullptr, nullptr, nullptr); } static void corrupt_jwt_sig(char* jwt) { @@ -579,7 +594,8 @@ static void test_jwt_verifier_bad_signature(void) { gpr_free(key_str); GPR_ASSERT(grpc_auth_json_key_is_valid(&key)); grpc_core::HttpRequest::SetOverride(httpcli_get_openid_config, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); jwt = grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime, nullptr); grpc_auth_json_key_destruct(&key); @@ -591,7 +607,7 @@ static void test_jwt_verifier_bad_signature(void) { gpr_free(jwt); grpc_jwt_verifier_destroy(verifier); grpc_core::ExecCtx::Get()->Flush(); - grpc_core::HttpRequest::SetOverride(nullptr, nullptr); + grpc_core::HttpRequest::SetOverride(nullptr, nullptr, nullptr); } static int httpcli_get_should_not_be_called( @@ -614,13 +630,14 @@ static void test_jwt_verifier_bad_format(void) { grpc_core::ExecCtx exec_ctx; grpc_jwt_verifier* verifier = grpc_jwt_verifier_create(nullptr, 0); grpc_core::HttpRequest::SetOverride(httpcli_get_should_not_be_called, - httpcli_post_should_not_be_called); + httpcli_post_should_not_be_called, + httpcli_put_should_not_be_called); grpc_jwt_verifier_verify(verifier, nullptr, "bad jwt", expected_audience, on_verification_bad_format, const_cast(expected_user_data)); grpc_jwt_verifier_destroy(verifier); grpc_core::ExecCtx::Get()->Flush(); - grpc_core::HttpRequest::SetOverride(nullptr, nullptr); + grpc_core::HttpRequest::SetOverride(nullptr, nullptr, nullptr); } /* find verification key: bad jks, cannot find key in jks */