From 4c93d36d085df0c18a2008054a2f1523448ceef9 Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Wed, 14 Oct 2020 03:39:05 -0700 Subject: [PATCH] Add url-sourced external credentials --- BUILD | 2 + BUILD.gn | 2 + CMakeLists.txt | 1 + Makefile | 2 + build_autogenerated.yaml | 2 + config.m4 | 1 + config.w32 | 1 + gRPC-C++.podspec | 2 + gRPC-Core.podspec | 3 + grpc.gemspec | 2 + grpc.gyp | 1 + package.xml | 2 + .../external/external_account_credentials.h | 3 +- .../url_external_account_credentials.cc | 211 ++++++++++++++++++ .../url_external_account_credentials.h | 59 +++++ src/python/grpcio/grpc_core_dependencies.py | 1 + test/core/security/credentials_test.cc | 154 ++++++++++++- tools/doxygen/Doxyfile.c++.internal | 2 + tools/doxygen/Doxyfile.core.internal | 2 + 19 files changed, 449 insertions(+), 4 deletions(-) create mode 100644 src/core/lib/security/credentials/external/url_external_account_credentials.cc create mode 100644 src/core/lib/security/credentials/external/url_external_account_credentials.h diff --git a/BUILD b/BUILD index b9e9a22ea30..5f2e538da87 100644 --- a/BUILD +++ b/BUILD @@ -1760,6 +1760,7 @@ grpc_cc_library( "src/core/lib/security/credentials/credentials.cc", "src/core/lib/security/credentials/credentials_metadata.cc", "src/core/lib/security/credentials/external/external_account_credentials.cc", + "src/core/lib/security/credentials/external/url_external_account_credentials.cc", "src/core/lib/security/credentials/fake/fake_credentials.cc", "src/core/lib/security/credentials/google_default/credentials_generic.cc", "src/core/lib/security/credentials/google_default/google_default_credentials.cc", @@ -1803,6 +1804,7 @@ grpc_cc_library( "src/core/lib/security/credentials/composite/composite_credentials.h", "src/core/lib/security/credentials/credentials.h", "src/core/lib/security/credentials/external/external_account_credentials.h", + "src/core/lib/security/credentials/external/url_external_account_credentials.h", "src/core/lib/security/credentials/fake/fake_credentials.h", "src/core/lib/security/credentials/google_default/google_default_credentials.h", "src/core/lib/security/credentials/iam/iam_credentials.h", diff --git a/BUILD.gn b/BUILD.gn index 5cdda5afbb7..18ca612b00f 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -996,6 +996,8 @@ config("grpc_config") { "src/core/lib/security/credentials/credentials_metadata.cc", "src/core/lib/security/credentials/external/external_account_credentials.cc", "src/core/lib/security/credentials/external/external_account_credentials.h", + "src/core/lib/security/credentials/external/url_external_account_credentials.cc", + "src/core/lib/security/credentials/external/url_external_account_credentials.h", "src/core/lib/security/credentials/fake/fake_credentials.cc", "src/core/lib/security/credentials/fake/fake_credentials.h", "src/core/lib/security/credentials/google_default/credentials_generic.cc", diff --git a/CMakeLists.txt b/CMakeLists.txt index 68826a146a1..d4fd32b7144 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1836,6 +1836,7 @@ add_library(grpc src/core/lib/security/credentials/credentials.cc src/core/lib/security/credentials/credentials_metadata.cc src/core/lib/security/credentials/external/external_account_credentials.cc + src/core/lib/security/credentials/external/url_external_account_credentials.cc src/core/lib/security/credentials/fake/fake_credentials.cc src/core/lib/security/credentials/google_default/credentials_generic.cc src/core/lib/security/credentials/google_default/google_default_credentials.cc diff --git a/Makefile b/Makefile index 5f8cba099bf..66b2c63291c 100644 --- a/Makefile +++ b/Makefile @@ -2250,6 +2250,7 @@ LIBGRPC_SRC = \ src/core/lib/security/credentials/credentials.cc \ src/core/lib/security/credentials/credentials_metadata.cc \ src/core/lib/security/credentials/external/external_account_credentials.cc \ + src/core/lib/security/credentials/external/url_external_account_credentials.cc \ src/core/lib/security/credentials/fake/fake_credentials.cc \ src/core/lib/security/credentials/google_default/credentials_generic.cc \ src/core/lib/security/credentials/google_default/google_default_credentials.cc \ @@ -4784,6 +4785,7 @@ src/core/lib/security/credentials/composite/composite_credentials.cc: $(OPENSSL_ src/core/lib/security/credentials/credentials.cc: $(OPENSSL_DEP) src/core/lib/security/credentials/credentials_metadata.cc: $(OPENSSL_DEP) src/core/lib/security/credentials/external/external_account_credentials.cc: $(OPENSSL_DEP) +src/core/lib/security/credentials/external/url_external_account_credentials.cc: $(OPENSSL_DEP) src/core/lib/security/credentials/fake/fake_credentials.cc: $(OPENSSL_DEP) src/core/lib/security/credentials/google_default/credentials_generic.cc: $(OPENSSL_DEP) src/core/lib/security/credentials/google_default/google_default_credentials.cc: $(OPENSSL_DEP) diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index d53cad44d56..e34a9adf1ab 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -756,6 +756,7 @@ libs: - src/core/lib/security/credentials/composite/composite_credentials.h - src/core/lib/security/credentials/credentials.h - src/core/lib/security/credentials/external/external_account_credentials.h + - src/core/lib/security/credentials/external/url_external_account_credentials.h - src/core/lib/security/credentials/fake/fake_credentials.h - src/core/lib/security/credentials/google_default/google_default_credentials.h - src/core/lib/security/credentials/iam/iam_credentials.h @@ -1264,6 +1265,7 @@ libs: - src/core/lib/security/credentials/credentials.cc - src/core/lib/security/credentials/credentials_metadata.cc - src/core/lib/security/credentials/external/external_account_credentials.cc + - src/core/lib/security/credentials/external/url_external_account_credentials.cc - src/core/lib/security/credentials/fake/fake_credentials.cc - src/core/lib/security/credentials/google_default/credentials_generic.cc - src/core/lib/security/credentials/google_default/google_default_credentials.cc diff --git a/config.m4 b/config.m4 index 01c06c697e5..3b14062da70 100644 --- a/config.m4 +++ b/config.m4 @@ -497,6 +497,7 @@ if test "$PHP_GRPC" != "no"; then src/core/lib/security/credentials/credentials.cc \ src/core/lib/security/credentials/credentials_metadata.cc \ src/core/lib/security/credentials/external/external_account_credentials.cc \ + src/core/lib/security/credentials/external/url_external_account_credentials.cc \ src/core/lib/security/credentials/fake/fake_credentials.cc \ src/core/lib/security/credentials/google_default/credentials_generic.cc \ src/core/lib/security/credentials/google_default/google_default_credentials.cc \ diff --git a/config.w32 b/config.w32 index 22f9f51578d..f13666cba4f 100644 --- a/config.w32 +++ b/config.w32 @@ -464,6 +464,7 @@ if (PHP_GRPC != "no") { "src\\core\\lib\\security\\credentials\\credentials.cc " + "src\\core\\lib\\security\\credentials\\credentials_metadata.cc " + "src\\core\\lib\\security\\credentials\\external\\external_account_credentials.cc " + + "src\\core\\lib\\security\\credentials\\external\\url_external_account_credentials.cc " + "src\\core\\lib\\security\\credentials\\fake\\fake_credentials.cc " + "src\\core\\lib\\security\\credentials\\google_default\\credentials_generic.cc " + "src\\core\\lib\\security\\credentials\\google_default\\google_default_credentials.cc " + diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index 529290a66e4..312ad18c379 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -604,6 +604,7 @@ Pod::Spec.new do |s| 'src/core/lib/security/credentials/composite/composite_credentials.h', 'src/core/lib/security/credentials/credentials.h', 'src/core/lib/security/credentials/external/external_account_credentials.h', + 'src/core/lib/security/credentials/external/url_external_account_credentials.h', 'src/core/lib/security/credentials/fake/fake_credentials.h', 'src/core/lib/security/credentials/google_default/google_default_credentials.h', 'src/core/lib/security/credentials/iam/iam_credentials.h', @@ -1201,6 +1202,7 @@ Pod::Spec.new do |s| 'src/core/lib/security/credentials/composite/composite_credentials.h', 'src/core/lib/security/credentials/credentials.h', 'src/core/lib/security/credentials/external/external_account_credentials.h', + 'src/core/lib/security/credentials/external/url_external_account_credentials.h', 'src/core/lib/security/credentials/fake/fake_credentials.h', 'src/core/lib/security/credentials/google_default/google_default_credentials.h', 'src/core/lib/security/credentials/iam/iam_credentials.h', diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 3d9e973702f..fb70cab2aab 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -1053,6 +1053,8 @@ Pod::Spec.new do |s| 'src/core/lib/security/credentials/credentials_metadata.cc', 'src/core/lib/security/credentials/external/external_account_credentials.cc', 'src/core/lib/security/credentials/external/external_account_credentials.h', + 'src/core/lib/security/credentials/external/url_external_account_credentials.cc', + 'src/core/lib/security/credentials/external/url_external_account_credentials.h', 'src/core/lib/security/credentials/fake/fake_credentials.cc', 'src/core/lib/security/credentials/fake/fake_credentials.h', 'src/core/lib/security/credentials/google_default/credentials_generic.cc', @@ -1725,6 +1727,7 @@ Pod::Spec.new do |s| 'src/core/lib/security/credentials/composite/composite_credentials.h', 'src/core/lib/security/credentials/credentials.h', 'src/core/lib/security/credentials/external/external_account_credentials.h', + 'src/core/lib/security/credentials/external/url_external_account_credentials.h', 'src/core/lib/security/credentials/fake/fake_credentials.h', 'src/core/lib/security/credentials/google_default/google_default_credentials.h', 'src/core/lib/security/credentials/iam/iam_credentials.h', diff --git a/grpc.gemspec b/grpc.gemspec index a7543928c98..ecd75e11674 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -971,6 +971,8 @@ Gem::Specification.new do |s| s.files += %w( src/core/lib/security/credentials/credentials_metadata.cc ) s.files += %w( src/core/lib/security/credentials/external/external_account_credentials.cc ) s.files += %w( src/core/lib/security/credentials/external/external_account_credentials.h ) + s.files += %w( src/core/lib/security/credentials/external/url_external_account_credentials.cc ) + s.files += %w( src/core/lib/security/credentials/external/url_external_account_credentials.h ) s.files += %w( src/core/lib/security/credentials/fake/fake_credentials.cc ) s.files += %w( src/core/lib/security/credentials/fake/fake_credentials.h ) s.files += %w( src/core/lib/security/credentials/google_default/credentials_generic.cc ) diff --git a/grpc.gyp b/grpc.gyp index 135a0eedf39..ae8969606ff 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -860,6 +860,7 @@ 'src/core/lib/security/credentials/credentials.cc', 'src/core/lib/security/credentials/credentials_metadata.cc', 'src/core/lib/security/credentials/external/external_account_credentials.cc', + 'src/core/lib/security/credentials/external/url_external_account_credentials.cc', 'src/core/lib/security/credentials/fake/fake_credentials.cc', 'src/core/lib/security/credentials/google_default/credentials_generic.cc', 'src/core/lib/security/credentials/google_default/google_default_credentials.cc', diff --git a/package.xml b/package.xml index 7fa2f205ddd..10a466a0d6a 100644 --- a/package.xml +++ b/package.xml @@ -951,6 +951,8 @@ + + diff --git a/src/core/lib/security/credentials/external/external_account_credentials.h b/src/core/lib/security/credentials/external/external_account_credentials.h index 96a02232739..04f09d4a25d 100644 --- a/src/core/lib/security/credentials/external/external_account_credentials.h +++ b/src/core/lib/security/credentials/external/external_account_credentials.h @@ -81,8 +81,7 @@ class ExternalAccountCredentials // the callback function (cb) to pass the subject token (or error) // back. virtual void RetrieveSubjectToken( - const HTTPRequestContext* ctx, - const ExternalAccountCredentialsOptions& options, + HTTPRequestContext* ctx, const ExternalAccountCredentialsOptions& options, std::function cb) = 0; private: diff --git a/src/core/lib/security/credentials/external/url_external_account_credentials.cc b/src/core/lib/security/credentials/external/url_external_account_credentials.cc new file mode 100644 index 00000000000..a752d7d3152 --- /dev/null +++ b/src/core/lib/security/credentials/external/url_external_account_credentials.cc @@ -0,0 +1,211 @@ +// +// Copyright 2020 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include + +#include "src/core/lib/security/credentials/external/url_external_account_credentials.h" + +#include "absl/strings/str_format.h" + +namespace grpc_core { + +RefCountedPtr +UrlExternalAccountCredentials::Create(ExternalAccountCredentialsOptions options, + std::vector scopes, + grpc_error** error) { + auto creds = MakeRefCounted( + std::move(options), std::move(scopes), error); + if (*error == GRPC_ERROR_NONE) { + return creds; + } else { + return nullptr; + } +} + +UrlExternalAccountCredentials::UrlExternalAccountCredentials( + ExternalAccountCredentialsOptions options, std::vector scopes, + grpc_error** error) + : ExternalAccountCredentials(options, std::move(scopes)) { + auto it = options.credential_source.object_value().find("url"); + if (it == options.credential_source.object_value().end()) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("url field not present."); + return; + } + if (it->second.type() != Json::Type::STRING) { + *error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("url field must be a string."); + return; + } + grpc_uri* url = grpc_uri_parse(it->second.string_value(), false); + if (url == nullptr) { + *error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Invalid credential source url."); + return; + } + url_ = url; + it = options.credential_source.object_value().find("headers"); + if (it != options.credential_source.object_value().end()) { + if (it->second.type() != Json::Type::OBJECT) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "The JSON value of credential source headers is not an object."); + return; + } + for (auto const& header : it->second.object_value()) { + headers_[header.first] = header.second.string_value(); + } + } + it = options.credential_source.object_value().find("format"); + if (it != options.credential_source.object_value().end()) { + const Json& format_json = it->second; + if (format_json.type() != Json::Type::OBJECT) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "The JSON value of credential source format is not an object."); + return; + } + auto format_it = format_json.object_value().find("type"); + if (format_it == format_json.object_value().end()) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "format.type field not present."); + return; + } + if (format_it->second.type() != Json::Type::STRING) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "format.type field must be a string."); + return; + } + format_type_ = format_it->second.string_value(); + if (format_type_ == "json") { + format_it = format_json.object_value().find("subject_token_field_name"); + if (format_it == format_json.object_value().end()) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "format.subject_token_field_name field must be present if the " + "format is in Json."); + return; + } + if (format_it->second.type() != Json::Type::STRING) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "format.subject_token_field_name field must be a string."); + return; + } + format_subject_token_field_name_ = format_it->second.string_value(); + } + } +} + +UrlExternalAccountCredentials::~UrlExternalAccountCredentials() { + grpc_uri_destroy(url_); +} + +void UrlExternalAccountCredentials::RetrieveSubjectToken( + HTTPRequestContext* ctx, const ExternalAccountCredentialsOptions& options, + std::function cb) { + if (ctx == nullptr) { + FinishRetrieveSubjectToken( + "", + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Missing HTTPRequestContext to start subject token retrieval.")); + return; + } + ctx_ = ctx; + cb_ = cb; + grpc_httpcli_request request; + memset(&request, 0, sizeof(grpc_httpcli_request)); + request.host = const_cast(url_->authority); + request.http.path = gpr_strdup(url_->path); + grpc_http_header* headers = nullptr; + request.http.hdr_count = headers_.size(); + headers = static_cast( + gpr_malloc(sizeof(grpc_http_header) * request.http.hdr_count)); + int i = 0; + for (auto const& header : headers_) { + headers[i].key = gpr_strdup(header.first.c_str()); + headers[i].value = gpr_strdup(header.second.c_str()); + ++i; + } + request.http.hdrs = headers; + request.handshaker = (strcmp(url_->scheme, "https") == 0) + ? &grpc_httpcli_ssl + : &grpc_httpcli_plaintext; + grpc_resource_quota* resource_quota = + grpc_resource_quota_create("external_account_credentials"); + grpc_http_response_destroy(&ctx_->response); + ctx_->response = {}; + GRPC_CLOSURE_INIT(&ctx_->closure, OnRetrieveSubjectToken, this, nullptr); + grpc_httpcli_get(ctx_->httpcli_context, ctx_->pollent, resource_quota, + &request, ctx_->deadline, &ctx_->closure, &ctx_->response); + grpc_resource_quota_unref_internal(resource_quota); + grpc_http_request_destroy(&request.http); +} + +void UrlExternalAccountCredentials::OnRetrieveSubjectToken(void* arg, + grpc_error* error) { + UrlExternalAccountCredentials* self = + static_cast(arg); + self->OnRetrieveSubjectTokenInternal(GRPC_ERROR_REF(error)); +} + +void UrlExternalAccountCredentials::OnRetrieveSubjectTokenInternal( + grpc_error* error) { + if (error != GRPC_ERROR_NONE) { + FinishRetrieveSubjectToken("", error); + return; + } + absl::string_view response_body(ctx_->response.body, + ctx_->response.body_length); + if (format_type_ == "json") { + grpc_error* error = GRPC_ERROR_NONE; + Json response_json = Json::Parse(response_body, &error); + if (error != GRPC_ERROR_NONE || + response_json.type() != Json::Type::OBJECT) { + FinishRetrieveSubjectToken( + "", GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "The format of response is not a valid json object.")); + return; + } + auto response_it = + response_json.object_value().find(format_subject_token_field_name_); + if (response_it == response_json.object_value().end()) { + FinishRetrieveSubjectToken("", GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Subject token field not present.")); + return; + } + if (response_it->second.type() != Json::Type::STRING) { + FinishRetrieveSubjectToken("", + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Subject token field must be a string.")); + return; + } + FinishRetrieveSubjectToken(response_it->second.string_value(), error); + return; + } + FinishRetrieveSubjectToken(std::string(response_body), GRPC_ERROR_NONE); +} + +void UrlExternalAccountCredentials::FinishRetrieveSubjectToken( + std::string subject_token, grpc_error* error) { + // Reset context + ctx_ = nullptr; + // Move object state into local variables. + auto cb = cb_; + cb_ = nullptr; + // Invoke the callback. + if (error != GRPC_ERROR_NONE) { + cb("", error); + } else { + cb(subject_token, GRPC_ERROR_NONE); + } +} + +} // namespace grpc_core \ No newline at end of file diff --git a/src/core/lib/security/credentials/external/url_external_account_credentials.h b/src/core/lib/security/credentials/external/url_external_account_credentials.h new file mode 100644 index 00000000000..8383ab523a7 --- /dev/null +++ b/src/core/lib/security/credentials/external/url_external_account_credentials.h @@ -0,0 +1,59 @@ +// +// Copyright 2020 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_EXTERNAL_URL_EXTERNAL_ACCOUNT_CREDENTIALS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_EXTERNAL_URL_EXTERNAL_ACCOUNT_CREDENTIALS_H + +#include + +#include "src/core/lib/security/credentials/external/external_account_credentials.h" + +namespace grpc_core { + +class UrlExternalAccountCredentials final : public ExternalAccountCredentials { + public: + static RefCountedPtr Create( + ExternalAccountCredentialsOptions options, + std::vector scopes, grpc_error** error); + + UrlExternalAccountCredentials(ExternalAccountCredentialsOptions options, + std::vector scopes, + grpc_error** error); + ~UrlExternalAccountCredentials() override; + + private: + void RetrieveSubjectToken( + HTTPRequestContext* ctx, const ExternalAccountCredentialsOptions& options, + std::function cb) override; + + static void OnRetrieveSubjectToken(void* arg, grpc_error* error); + void OnRetrieveSubjectTokenInternal(grpc_error* error); + + void FinishRetrieveSubjectToken(std::string subject_token, grpc_error* error); + + // Fields of credential source + grpc_uri* url_ = nullptr; + std::map headers_; + std::string format_type_; + std::string format_subject_token_field_name_; + + HTTPRequestContext* ctx_ = nullptr; + std::function cb_ = nullptr; +}; + +} // namespace grpc_core + +#endif // GRPC_CORE_LIB_SECURITY_CREDENTIALS_EXTERNAL_URL_EXTERNAL_ACCOUNT_CREDENTIALS_H diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 3b16ad78734..6f4867da791 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -473,6 +473,7 @@ CORE_SOURCE_FILES = [ 'src/core/lib/security/credentials/credentials.cc', 'src/core/lib/security/credentials/credentials_metadata.cc', 'src/core/lib/security/credentials/external/external_account_credentials.cc', + 'src/core/lib/security/credentials/external/url_external_account_credentials.cc', 'src/core/lib/security/credentials/fake/fake_credentials.cc', 'src/core/lib/security/credentials/google_default/credentials_generic.cc', 'src/core/lib/security/credentials/google_default/google_default_credentials.cc', diff --git a/test/core/security/credentials_test.cc b/test/core/security/credentials_test.cc index 308b6fd0f35..ef6e326fc17 100644 --- a/test/core/security/credentials_test.cc +++ b/test/core/security/credentials_test.cc @@ -44,6 +44,7 @@ #include "src/core/lib/iomgr/error.h" #include "src/core/lib/security/credentials/composite/composite_credentials.h" #include "src/core/lib/security/credentials/external/external_account_credentials.h" +#include "src/core/lib/security/credentials/external/url_external_account_credentials.h" #include "src/core/lib/security/credentials/fake/fake_credentials.h" #include "src/core/lib/security/credentials/google_default/google_default_credentials.h" #include "src/core/lib/security/credentials/jwt/jwt_credentials.h" @@ -146,6 +147,31 @@ static const char "{\"accessToken\":\"service_account_impersonation_access_token\"," " \"expireTime\":\"2050-01-01T00:00:00Z\"}"; +static const char + valid_url_external_account_creds_options_credential_source_format_text[] = + "{\"url\":\"https://foo.com:5555/generate_subject_token_format_text\"," + "\"headers\":{\"Metadata-Flavor\":\"Google\"}}"; + +static const char + valid_url_external_account_creds_retrieve_subject_token_response_format_text + [] = "test_subject_token"; + +static const char + valid_url_external_account_creds_options_credential_source_format_json[] = + "{\"url\":\"https://foo.com:5555/generate_subject_token_format_json\"," + "\"headers\":{\"Metadata-Flavor\":\"Google\"}," + "\"format\":{\"type\":\"json\",\"subject_token_field_name\":\"access_" + "token\"}}"; + +static const char + valid_url_external_account_creds_retrieve_subject_token_response_format_json + [] = "{\"access_token\":\"test_subject_token\"}"; + +static const char + invalid_url_external_account_creds_options_credential_source[] = + "{\"url\":\"invalid_credential_source_url\"," + "\"headers\":{\"Metadata-Flavor\":\"Google\"}}"; + /* -- Global state flags. -- */ static bool g_test_is_on_gce = false; @@ -1990,6 +2016,23 @@ external_account_creds_httpcli_post_failure_token_exchange_response_missing_acce return 1; } +static int url_external_account_creds_httpcli_get_success( + const grpc_httpcli_request* request, grpc_millis /*deadline*/, + grpc_closure* on_done, grpc_httpcli_response* response) { + if (strcmp(request->http.path, "/generate_subject_token_format_text") == 0) { + *response = http_response( + 200, + valid_url_external_account_creds_retrieve_subject_token_response_format_text); + } else if (strcmp(request->http.path, + "/generate_subject_token_format_json") == 0) { + *response = http_response( + 200, + valid_url_external_account_creds_retrieve_subject_token_response_format_json); + } + grpc_core::ExecCtx::Run(DEBUG_LOCATION, on_done, GRPC_ERROR_NONE); + return 1; +} + // The subclass of ExternalAccountCredentials for testing. // ExternalAccountCredentials is an abstract class so we can't directly test // against it. @@ -2002,8 +2045,7 @@ class TestExternalAccountCredentials final protected: void RetrieveSubjectToken( - const HTTPRequestContext* ctx, - const ExternalAccountCredentialsOptions& options, + HTTPRequestContext* ctx, const ExternalAccountCredentialsOptions& options, std::function cb) override { cb("test_subject_token", GRPC_ERROR_NONE); } @@ -2184,6 +2226,111 @@ test_external_account_creds_faiure_token_exchange_response_missing_access_token( grpc_httpcli_set_override(nullptr, nullptr); } +static void test_url_external_account_creds_success_format_text(void) { + expected_md emd[] = {{"authorization", "Bearer token_exchange_access_token"}}; + grpc_core::ExecCtx exec_ctx; + grpc_auth_metadata_context auth_md_ctx = {test_service_url, test_method, + nullptr, nullptr}; + grpc_error* error = GRPC_ERROR_NONE; + grpc_core::Json credential_source = grpc_core::Json::Parse( + valid_url_external_account_creds_options_credential_source_format_text, + &error); + GPR_ASSERT(error == GRPC_ERROR_NONE); + grpc_core::ExternalAccountCredentials::ExternalAccountCredentialsOptions + 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; + }; + auto creds = + grpc_core::UrlExternalAccountCredentials::Create(options, {}, &error); + GPR_ASSERT(creds != nullptr); + GPR_ASSERT(error == GRPC_ERROR_NONE); + GPR_ASSERT(creds->min_security_level() == GRPC_PRIVACY_AND_INTEGRITY); + request_metadata_state* state = + make_request_metadata_state(GRPC_ERROR_NONE, emd, GPR_ARRAY_SIZE(emd)); + grpc_httpcli_set_override(url_external_account_creds_httpcli_get_success, + external_account_creds_httpcli_post_success); + run_request_metadata_test(creds.get(), auth_md_ctx, state); + grpc_core::ExecCtx::Get()->Flush(); + grpc_httpcli_set_override(nullptr, nullptr); +} + +static void test_url_external_account_creds_success_format_json(void) { + expected_md emd[] = {{"authorization", "Bearer token_exchange_access_token"}}; + grpc_core::ExecCtx exec_ctx; + grpc_auth_metadata_context auth_md_ctx = {test_service_url, test_method, + nullptr, nullptr}; + grpc_error* error = GRPC_ERROR_NONE; + grpc_core::Json credential_source = grpc_core::Json::Parse( + valid_url_external_account_creds_options_credential_source_format_json, + &error); + GPR_ASSERT(error == GRPC_ERROR_NONE); + grpc_core::ExternalAccountCredentials::ExternalAccountCredentialsOptions + 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; + }; + auto creds = + grpc_core::UrlExternalAccountCredentials::Create(options, {}, &error); + GPR_ASSERT(creds != nullptr); + GPR_ASSERT(error == GRPC_ERROR_NONE); + GPR_ASSERT(creds->min_security_level() == GRPC_PRIVACY_AND_INTEGRITY); + request_metadata_state* state = + make_request_metadata_state(GRPC_ERROR_NONE, emd, GPR_ARRAY_SIZE(emd)); + grpc_httpcli_set_override(url_external_account_creds_httpcli_get_success, + external_account_creds_httpcli_post_success); + run_request_metadata_test(creds.get(), auth_md_ctx, state); + grpc_core::ExecCtx::Get()->Flush(); + grpc_httpcli_set_override(nullptr, nullptr); +} + +static void +test_url_external_account_creds_faiure_invalid_credential_source_url(void) { + grpc_error* error = GRPC_ERROR_NONE; + grpc_core::Json credential_source = grpc_core::Json::Parse( + invalid_url_external_account_creds_options_credential_source, &error); + GPR_ASSERT(error == GRPC_ERROR_NONE); + grpc_core::ExternalAccountCredentials::ExternalAccountCredentialsOptions + 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; + }; + auto creds = + grpc_core::UrlExternalAccountCredentials::Create(options, {}, &error); + GPR_ASSERT(creds == nullptr); + grpc_slice expected_error_slice = + grpc_slice_from_static_string("Invalid credential source url."); + grpc_slice actual_error_slice; + GPR_ASSERT(grpc_error_get_str(error, GRPC_ERROR_STR_DESCRIPTION, + &actual_error_slice)); + GPR_ASSERT(grpc_slice_cmp(expected_error_slice, actual_error_slice) == 0); + GRPC_ERROR_UNREF(error); +} + int main(int argc, char** argv) { grpc::testing::TestEnvironment env(argc, argv); grpc_init(); @@ -2233,6 +2380,9 @@ int main(int argc, char** argv) { test_external_account_creds_faiure_invalid_token_url(); test_external_account_creds_faiure_invalid_service_account_impersonation_url(); test_external_account_creds_faiure_token_exchange_response_missing_access_token(); + test_url_external_account_creds_success_format_text(); + test_url_external_account_creds_success_format_json(); + test_url_external_account_creds_faiure_invalid_credential_source_url(); grpc_shutdown(); return 0; } diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index e83d37d9d3a..e8d33d12846 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -1902,6 +1902,8 @@ src/core/lib/security/credentials/credentials.h \ src/core/lib/security/credentials/credentials_metadata.cc \ src/core/lib/security/credentials/external/external_account_credentials.cc \ src/core/lib/security/credentials/external/external_account_credentials.h \ +src/core/lib/security/credentials/external/url_external_account_credentials.cc \ +src/core/lib/security/credentials/external/url_external_account_credentials.h \ src/core/lib/security/credentials/fake/fake_credentials.cc \ src/core/lib/security/credentials/fake/fake_credentials.h \ src/core/lib/security/credentials/google_default/credentials_generic.cc \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index a3c1cbf9e2b..c73d3a21a20 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -1745,6 +1745,8 @@ src/core/lib/security/credentials/credentials.h \ src/core/lib/security/credentials/credentials_metadata.cc \ src/core/lib/security/credentials/external/external_account_credentials.cc \ src/core/lib/security/credentials/external/external_account_credentials.h \ +src/core/lib/security/credentials/external/url_external_account_credentials.cc \ +src/core/lib/security/credentials/external/url_external_account_credentials.h \ src/core/lib/security/credentials/fake/fake_credentials.cc \ src/core/lib/security/credentials/fake/fake_credentials.h \ src/core/lib/security/credentials/google_default/credentials_generic.cc \