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
pull/27186/head
yihuaz 4 years ago committed by GitHub
parent 71a9410fc7
commit c66d2cc084
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 35
      src/core/lib/security/credentials/jwt/jwt_credentials.cc
  2. 9
      src/core/lib/security/credentials/jwt/jwt_credentials.h
  3. 19
      test/core/security/credentials_test.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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
@ -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<std::string> 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<std::string> 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

@ -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_call_credentials>
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<std::string> RemoveServiceNameFromJwtUri(absl::string_view uri);
} // namespace grpc_core
#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_CREDENTIALS_H */

@ -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();

Loading…
Cancel
Save