From 2b1265b42fc35611f406eb1c2bfcc0872ba3ddfb Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Wed, 21 Oct 2020 17:56:48 -0700 Subject: [PATCH] Add file-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 + .../file_external_account_credentials.cc | 136 ++++++++++++ .../file_external_account_credentials.h | 49 +++++ src/python/grpcio/grpc_core_dependencies.py | 1 + test/core/security/credentials_test.cc | 204 +++++++++++++++++- tools/doxygen/Doxyfile.c++.internal | 2 + tools/doxygen/Doxyfile.core.internal | 2 + 18 files changed, 407 insertions(+), 8 deletions(-) create mode 100644 src/core/lib/security/credentials/external/file_external_account_credentials.cc create mode 100644 src/core/lib/security/credentials/external/file_external_account_credentials.h diff --git a/BUILD b/BUILD index 57f7abe8458..d07546280d3 100644 --- a/BUILD +++ b/BUILD @@ -1794,6 +1794,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/file_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", @@ -1838,6 +1839,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/file_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", diff --git a/BUILD.gn b/BUILD.gn index 9de54fe9a7a..0c0f30d5654 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1002,6 +1002,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/file_external_account_credentials.cc", + "src/core/lib/security/credentials/external/file_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", diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a25f4552b0..c8f1e9e2f82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1846,6 +1846,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/file_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 diff --git a/Makefile b/Makefile index a63a1891989..94f8cc39489 100644 --- a/Makefile +++ b/Makefile @@ -2254,6 +2254,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/file_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 \ @@ -4795,6 +4796,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/file_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) diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index eb99f9a9cba..f73e9b9cb4a 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -760,6 +760,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/file_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 @@ -1272,6 +1273,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/file_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 diff --git a/config.m4 b/config.m4 index 42bf1f5d476..97800cc15e9 100644 --- a/config.m4 +++ b/config.m4 @@ -501,6 +501,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/file_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 \ diff --git a/config.w32 b/config.w32 index 9485624f91b..8f2e6bc7827 100644 --- a/config.w32 +++ b/config.w32 @@ -468,6 +468,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\\file_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 " + diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index d5af45ef9e0..f832e89f829 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -607,6 +607,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/file_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', @@ -1209,6 +1210,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/file_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', diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 2175c6066ba..1a84b55e45d 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -1059,6 +1059,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/file_external_account_credentials.cc', + 'src/core/lib/security/credentials/external/file_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', @@ -1736,6 +1738,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/file_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', diff --git a/grpc.gemspec b/grpc.gemspec index 9d39899c137..d4ade6aa93d 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -976,6 +976,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/file_external_account_credentials.cc ) + s.files += %w( src/core/lib/security/credentials/external/file_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 ) diff --git a/grpc.gyp b/grpc.gyp index 8699f0fb5b4..db3c7e9686a 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -866,6 +866,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/file_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', diff --git a/package.xml b/package.xml index 6a2a5df29c2..25fa57adc08 100644 --- a/package.xml +++ b/package.xml @@ -956,6 +956,8 @@ + + diff --git a/src/core/lib/security/credentials/external/file_external_account_credentials.cc b/src/core/lib/security/credentials/external/file_external_account_credentials.cc new file mode 100644 index 00000000000..f2724f07751 --- /dev/null +++ b/src/core/lib/security/credentials/external/file_external_account_credentials.cc @@ -0,0 +1,136 @@ +// +// 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/file_external_account_credentials.h" + +#include + +#include "src/core/lib/iomgr/load_file.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_utils.h" + +namespace grpc_core { + +RefCountedPtr +FileExternalAccountCredentials::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; + } +} + +FileExternalAccountCredentials::FileExternalAccountCredentials( + ExternalAccountCredentialsOptions options, std::vector scopes, + grpc_error** error) + : ExternalAccountCredentials(options, std::move(scopes)) { + auto it = options.credential_source.object_value().find("file"); + if (it == options.credential_source.object_value().end()) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("file field not present."); + return; + } + if (it->second.type() != Json::Type::STRING) { + *error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("file field must be a string."); + return; + } + file_ = it->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(); + } + } +} + +void FileExternalAccountCredentials::RetrieveSubjectToken( + HTTPRequestContext* ctx, const ExternalAccountCredentialsOptions& options, + std::function cb) { + struct SliceWrapper { + ~SliceWrapper() { grpc_slice_unref_internal(slice); } + grpc_slice slice = grpc_empty_slice(); + }; + SliceWrapper content_slice; + // To retrieve the subject token, we read the file every time we make a + // request because it may have changed since the last request. + grpc_error* error = grpc_load_file(file_.c_str(), 0, &content_slice.slice); + if (error != GRPC_ERROR_NONE) { + cb("", error); + return; + } + absl::string_view content = StringViewFromSlice(content_slice.slice); + if (format_type_ == "json") { + Json content_json = Json::Parse(content, &error); + if (error != GRPC_ERROR_NONE || content_json.type() != Json::Type::OBJECT) { + cb("", GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "The content of the file is not a valid json object.")); + GRPC_ERROR_UNREF(error); + return; + } + auto content_it = + content_json.object_value().find(format_subject_token_field_name_); + if (content_it == content_json.object_value().end()) { + cb("", GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Subject token field not present.")); + return; + } + if (content_it->second.type() != Json::Type::STRING) { + cb("", GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Subject token field must be a string.")); + return; + } + cb(content_it->second.string_value(), GRPC_ERROR_NONE); + return; + } + cb(std::string(content), GRPC_ERROR_NONE); +} + +} // namespace grpc_core diff --git a/src/core/lib/security/credentials/external/file_external_account_credentials.h b/src/core/lib/security/credentials/external/file_external_account_credentials.h new file mode 100644 index 00000000000..49b4d318895 --- /dev/null +++ b/src/core/lib/security/credentials/external/file_external_account_credentials.h @@ -0,0 +1,49 @@ +// +// 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_FILE_EXTERNAL_ACCOUNT_CREDENTIALS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_EXTERNAL_FILE_EXTERNAL_ACCOUNT_CREDENTIALS_H + +#include + +#include "src/core/lib/security/credentials/external/external_account_credentials.h" + +namespace grpc_core { + +class FileExternalAccountCredentials final : public ExternalAccountCredentials { + public: + static RefCountedPtr Create( + ExternalAccountCredentialsOptions options, + std::vector scopes, grpc_error** error); + + FileExternalAccountCredentials(ExternalAccountCredentialsOptions options, + std::vector scopes, + grpc_error** error); + + private: + void RetrieveSubjectToken( + HTTPRequestContext* ctx, const ExternalAccountCredentialsOptions& options, + std::function cb) override; + + // Fields of credential source + std::string file_; + std::string format_type_; + std::string format_subject_token_field_name_; +}; + +} // namespace grpc_core + +#endif // GRPC_CORE_LIB_SECURITY_CREDENTIALS_EXTERNAL_FILE_EXTERNAL_ACCOUNT_CREDENTIALS_H diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 0fc2e5ffb30..0881c4b42ea 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -477,6 +477,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/file_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', diff --git a/test/core/security/credentials_test.cc b/test/core/security/credentials_test.cc index ef6e326fc17..bd3b964586f 100644 --- a/test/core/security/credentials_test.cc +++ b/test/core/security/credentials_test.cc @@ -28,6 +28,7 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" +#include "absl/strings/str_replace.h" #include #include @@ -44,6 +45,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/file_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" @@ -2122,7 +2124,7 @@ test_external_account_creds_success_with_service_account_impersonation(void) { grpc_httpcli_set_override(nullptr, nullptr); } -static void test_external_account_creds_faiure_invalid_token_url(void) { +static void test_external_account_creds_failure_invalid_token_url(void) { grpc_core::ExecCtx exec_ctx; grpc_auth_metadata_context auth_md_ctx = {test_service_url, test_method, nullptr, nullptr}; @@ -2155,7 +2157,7 @@ static void test_external_account_creds_faiure_invalid_token_url(void) { } static void -test_external_account_creds_faiure_invalid_service_account_impersonation_url( +test_external_account_creds_failure_invalid_service_account_impersonation_url( void) { grpc_core::ExecCtx exec_ctx; grpc_auth_metadata_context auth_md_ctx = {test_service_url, test_method, @@ -2190,7 +2192,7 @@ test_external_account_creds_faiure_invalid_service_account_impersonation_url( } static void -test_external_account_creds_faiure_token_exchange_response_missing_access_token( +test_external_account_creds_failure_token_exchange_response_missing_access_token( void) { grpc_core::ExecCtx exec_ctx; grpc_auth_metadata_context auth_md_ctx = {test_service_url, test_method, @@ -2301,7 +2303,7 @@ static void test_url_external_account_creds_success_format_json(void) { } static void -test_url_external_account_creds_faiure_invalid_credential_source_url(void) { +test_url_external_account_creds_failure_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); @@ -2331,6 +2333,188 @@ test_url_external_account_creds_faiure_invalid_credential_source_url(void) { GRPC_ERROR_UNREF(error); } +static void test_file_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; + char* subject_token_path = write_tmp_jwt_file("test_subject_token"); + grpc_core::Json credential_source = grpc_core::Json::Parse( + absl::StrFormat( + "{\"file\":\"%s\"}", + absl::StrReplaceAll(subject_token_path, {{"\\", "\\\\"}})), + &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::FileExternalAccountCredentials::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(httpcli_get_should_not_be_called, + 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); + GRPC_ERROR_UNREF(error); + gpr_free(subject_token_path); +} + +static void test_file_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; + char* subject_token_path = + write_tmp_jwt_file("{\"access_token\":\"test_subject_token\"}"); + grpc_core::Json credential_source = grpc_core::Json::Parse( + absl::StrFormat( + "{\n" + "\"file\":\"%s\",\n" + "\"format\":\n" + "{\n" + "\"type\":\"json\",\n" + "\"subject_token_field_name\":\"access_token\"\n" + "}\n" + "}", + absl::StrReplaceAll(subject_token_path, {{"\\", "\\\\"}})), + &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::FileExternalAccountCredentials::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(httpcli_get_should_not_be_called, + 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); + GRPC_ERROR_UNREF(error); + gpr_free(subject_token_path); +} + +static void test_file_external_account_creds_failure_file_not_found(void) { + 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("{\"file\":\"non_exisiting_file\"}", &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::FileExternalAccountCredentials::Create(options, {}, &error); + GPR_ASSERT(creds != nullptr); + GPR_ASSERT(error == GRPC_ERROR_NONE); + grpc_httpcli_set_override(httpcli_get_should_not_be_called, + httpcli_post_should_not_be_called); + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to load file"); + grpc_error* expected_error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Error occurred when fetching oauth2 token.", &error, 1); + request_metadata_state* state = + make_request_metadata_state(expected_error, nullptr, 0); + run_request_metadata_test(creds.get(), auth_md_ctx, state); + grpc_core::ExecCtx::Get()->Flush(); + grpc_httpcli_set_override(nullptr, nullptr); + GRPC_ERROR_UNREF(error); +} + +static void test_file_external_account_creds_failure_invalid_json_content( + void) { + 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; + char* subject_token_path = write_tmp_jwt_file("not_a_valid_json_file"); + grpc_core::Json credential_source = grpc_core::Json::Parse( + absl::StrFormat( + "{\n" + "\"file\":\"%s\",\n" + "\"format\":\n" + "{\n" + "\"type\":\"json\",\n" + "\"subject_token_field_name\":\"access_token\"\n" + "}\n" + "}", + absl::StrReplaceAll(subject_token_path, {{"\\", "\\\\"}})), + &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::FileExternalAccountCredentials::Create(options, {}, &error); + GPR_ASSERT(creds != nullptr); + GPR_ASSERT(error == GRPC_ERROR_NONE); + grpc_httpcli_set_override(httpcli_get_should_not_be_called, + httpcli_post_should_not_be_called); + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "The content of the file is not a valid json object."); + grpc_error* expected_error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Error occurred when fetching oauth2 token.", &error, 1); + request_metadata_state* state = + make_request_metadata_state(expected_error, nullptr, 0); + run_request_metadata_test(creds.get(), auth_md_ctx, state); + grpc_core::ExecCtx::Get()->Flush(); + grpc_httpcli_set_override(nullptr, nullptr); + GRPC_ERROR_UNREF(error); + gpr_free(subject_token_path); +} + int main(int argc, char** argv) { grpc::testing::TestEnvironment env(argc, argv); grpc_init(); @@ -2377,12 +2561,16 @@ int main(int argc, char** argv) { test_auth_metadata_context(); test_external_account_creds_success(); test_external_account_creds_success_with_service_account_impersonation(); - 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_external_account_creds_failure_invalid_token_url(); + test_external_account_creds_failure_invalid_service_account_impersonation_url(); + test_external_account_creds_failure_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(); + test_url_external_account_creds_failure_invalid_credential_source_url(); + test_file_external_account_creds_success_format_text(); + test_file_external_account_creds_success_format_json(); + test_file_external_account_creds_failure_file_not_found(); + test_file_external_account_creds_failure_invalid_json_content(); grpc_shutdown(); return 0; } diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index cf1ad97bf19..4bca2c7f459 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -1908,6 +1908,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/file_external_account_credentials.cc \ +src/core/lib/security/credentials/external/file_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 \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index d5a14e7e48f..b3459384268 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -1750,6 +1750,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/file_external_account_credentials.cc \ +src/core/lib/security/credentials/external/file_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 \