From c66d2cc08452f85f19ff9a1059ec72a4037d4e84 Mon Sep 17 00:00:00 2001 From: yihuaz Date: Mon, 30 Aug 2021 10:57:48 -0700 Subject: [PATCH] Allow access to Google API regional endpoints via Google Default Credentials (#27155) * fix Google API regional endpoint access issues * address 1st round of comments * fix minor nits --- .../credentials/jwt/jwt_credentials.cc | 35 ++++++++++++++----- .../credentials/jwt/jwt_credentials.h | 9 ++++- test/core/security/credentials_test.cc | 19 +++++++++- 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.cc b/src/core/lib/security/credentials/jwt/jwt_credentials.cc index 27590f4baf5..07e4cb08ba5 100644 --- a/src/core/lib/security/credentials/jwt/jwt_credentials.cc +++ b/src/core/lib/security/credentials/jwt/jwt_credentials.cc @@ -31,6 +31,8 @@ #include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/transport/error_utils.h" +#include "src/core/lib/uri/uri_parser.h" #include #include @@ -42,10 +44,7 @@ using grpc_core::Json; void grpc_service_account_jwt_access_credentials::reset_cache() { GRPC_MDELEM_UNREF(cached_.jwt_md); cached_.jwt_md = GRPC_MDNULL; - if (cached_.service_url != nullptr) { - gpr_free(cached_.service_url); - cached_.service_url = nullptr; - } + cached_.service_url.clear(); cached_.jwt_expiration = gpr_inf_past(GPR_CLOCK_REALTIME); } @@ -63,12 +62,19 @@ bool grpc_service_account_jwt_access_credentials::get_request_metadata( gpr_timespec refresh_threshold = gpr_time_from_seconds( GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN); + // Remove service name from service_url to follow the audience format + // dictated in https://google.aip.dev/auth/4111. + absl::StatusOr uri = + grpc_core::RemoveServiceNameFromJwtUri(context.service_url); + if (!uri.ok()) { + *error = absl_status_to_grpc_error(uri.status()); + return true; + } /* See if we can return a cached jwt. */ grpc_mdelem jwt_md = GRPC_MDNULL; { gpr_mu_lock(&cache_mu_); - if (cached_.service_url != nullptr && - strcmp(cached_.service_url, context.service_url) == 0 && + if (!cached_.service_url.empty() && cached_.service_url == *uri && !GRPC_MDISNULL(cached_.jwt_md) && (gpr_time_cmp( gpr_time_sub(cached_.jwt_expiration, gpr_now(GPR_CLOCK_REALTIME)), @@ -83,14 +89,13 @@ bool grpc_service_account_jwt_access_credentials::get_request_metadata( /* Generate a new jwt. */ gpr_mu_lock(&cache_mu_); reset_cache(); - jwt = grpc_jwt_encode_and_sign(&key_, context.service_url, jwt_lifetime_, - nullptr); + jwt = grpc_jwt_encode_and_sign(&key_, uri->c_str(), jwt_lifetime_, nullptr); if (jwt != nullptr) { std::string md_value = absl::StrCat("Bearer ", jwt); gpr_free(jwt); cached_.jwt_expiration = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), jwt_lifetime_); - cached_.service_url = gpr_strdup(context.service_url); + cached_.service_url = std::move(*uri); cached_.jwt_md = grpc_mdelem_from_slices( grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY), grpc_slice_from_cpp_string(std::move(md_value))); @@ -173,3 +178,15 @@ grpc_call_credentials* grpc_service_account_jwt_access_credentials_create( grpc_auth_json_key_create_from_string(json_key), token_lifetime) .release(); } + +namespace grpc_core { + +absl::StatusOr RemoveServiceNameFromJwtUri(absl::string_view uri) { + auto parsed = grpc_core::URI::Parse(uri); + if (!parsed.ok()) { + return parsed.status(); + } + return absl::StrFormat("%s://%s/", parsed->scheme(), parsed->authority()); +} + +} // namespace grpc_core diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.h b/src/core/lib/security/credentials/jwt/jwt_credentials.h index 5ae4c1f41fd..fca4487fde3 100644 --- a/src/core/lib/security/credentials/jwt/jwt_credentials.h +++ b/src/core/lib/security/credentials/jwt/jwt_credentials.h @@ -64,7 +64,7 @@ class grpc_service_account_jwt_access_credentials gpr_mu cache_mu_; struct { grpc_mdelem jwt_md = GRPC_MDNULL; - char* service_url = nullptr; + std::string service_url; gpr_timespec jwt_expiration; } cached_; @@ -78,4 +78,11 @@ grpc_core::RefCountedPtr grpc_service_account_jwt_access_credentials_create_from_auth_json_key( grpc_auth_json_key key, gpr_timespec token_lifetime); +namespace grpc_core { + +// Exposed for testing purposes only. +absl::StatusOr RemoveServiceNameFromJwtUri(absl::string_view uri); + +} // namespace grpc_core + #endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_CREDENTIALS_H */ diff --git a/test/core/security/credentials_test.cc b/test/core/security/credentials_test.cc index 8ad8aefced8..a91827e8a84 100644 --- a/test/core/security/credentials_test.cc +++ b/test/core/security/credentials_test.cc @@ -171,7 +171,9 @@ static const char test_signed_jwt_token_type2[] = static const char test_signed_jwt_path_prefix[] = "test_sign_jwt"; static const char test_service_url[] = "https://foo.com/foo.v1"; +static const char test_service_url_no_service_name[] = "https://foo.com/"; static const char other_test_service_url[] = "https://bar.com/bar.v1"; +static const char other_test_service_url_no_service_name[] = "https://bar.com/"; static const char test_sts_endpoint_url[] = "https://foo.com:5555/v1/token-exchange"; @@ -1326,9 +1328,13 @@ static void validate_jwt_encode_and_sign_params( } static char* encode_and_sign_jwt_success(const grpc_auth_json_key* json_key, - const char* /*audience*/, + const char* audience, gpr_timespec token_lifetime, const char* scope) { + if (strcmp(audience, test_service_url_no_service_name) != 0 && + strcmp(audience, other_test_service_url_no_service_name) != 0) { + return nullptr; + } validate_jwt_encode_and_sign_params(json_key, scope, token_lifetime); return gpr_strdup(test_signed_jwt); } @@ -1399,6 +1405,16 @@ static void test_jwt_creds_lifetime(void) { gpr_free(json_key_string); } +static void test_remove_service_from_jwt_uri(void) { + const char wrong_uri[] = "hello world"; + GPR_ASSERT(!grpc_core::RemoveServiceNameFromJwtUri(wrong_uri).ok()); + const char valid_uri[] = "https://foo.com/get/"; + const char expected_uri[] = "https://foo.com/"; + auto output = grpc_core::RemoveServiceNameFromJwtUri(valid_uri); + GPR_ASSERT(output.ok()); + GPR_ASSERT(strcmp(output->c_str(), expected_uri) == 0); +} + static void test_jwt_creds_success(void) { const char expected_creds_debug_string_prefix[] = "JWTAccessCredentials{ExpirationTime:"; @@ -3493,6 +3509,7 @@ int main(int argc, char** argv) { test_jwt_creds_lifetime(); test_jwt_creds_success(); test_jwt_creds_signing_failure(); + test_remove_service_from_jwt_uri(); test_google_default_creds_auth_key(); test_google_default_creds_refresh_token(); test_google_default_creds_external_account_credentials();